Android UI设计系列之自定义ListView仿QQ空间阻尼下拉刷新和渐变菜单栏效果(8)

好久没有写有关UI的博客了,刚刚翻了一下之前的博客,最近一篇有关UI的博客:Android UI设计系列之自定义Dialog实现各种风格的对话框效果(7) ,实现各种风格效果的对话框,在那篇博客写完后由于公司封闭开发封网以及其它原因致使博客中断至今,中断这么久很是惭愧,后续我会尽量把该写的都补充出来。近来项目有个需求,要做个和QQ空间类似的菜单栏透明度渐变和下拉刷新带有阻尼回弹的效果。于是花点时间动手试了试,基本上达到了QQ空间的效果,截图如下:

通过观察QQ空间的运行效果,发现当往上滚动时菜单栏会随着滚动距离的增大其透明度组件增大直到完全不透明,反之逐渐透明。当滚动到顶部后继续下拉会出现拉升效果当松手之后出现阻尼回弹效果。于是就通过重写ListView模仿了QQ空间的运行效果。
实现QQ空间运行效果前需要考虑两个问题:
1)、如何实现菜单栏透明度渐变
        通过观察QQ空间的运行效果可知其菜单栏的透明度是根据滚动距离而动态变化的,要想实现透明度的变化就需要知道的ListView的滚动距离,所以有关透明度的问题也就转化成了滚动距离的问题。
2)、如何实现阻尼拉升和回弹效果
        要想利用ListView实现阻尼效果就要求ListView首先滚动到了顶部,当ListView滚动到了顶部之后若继续手动下滑就要求其第一个Child变化来模拟下拉效果,当手指松开后该Child要回弹到初始状态。
        我们先看第一个问题:要想实现透明度渐变就要先获取到ListView的滚动距离,通过滚动距离来计算相应的透明度。由于ListView的复用机制就决定了不能通过第一个可见Item的getTop()方法来得到滚动值,所以我们可以通过HeaderView来获取滚动距离,因为Header在ListView中是不参与复用的。
        下面先了解一下ListView添加HeaderView后的滚动流程:

上图大致画了ListView含有HeaderView时的三个滚动状态,状态一可称为初始状态或者是恰好滚动到最顶部状态,此时HeaderView的getTop()值为0;状态二为ListView的滚动中状态,此时HeaderView没有完全滚动出ListView边界,getTop()的返回值为负数且其绝对值范围在0和HeaderView的高度之间;状态三表示的是HeaderView完全滚动出了ListView边界,若调用getTop()得到的返回值为负数且绝对值等于HeaderView的高度(此后可理解成HeaderView一直固定在ListView的顶部)。
        明白了ListView的滚动原理,我们先尝试实现渐变菜单栏的功能。首先定义自己的ListView,取名为FlexibleListView,单词flexible是灵活的、多样的的意思,因为我们的ListView不仅要实现菜单栏的透明度渐变还要实现阻尼效果,所以取名为FlexibleListView比较恰当。FlexibleListView继承ListView后需要实现其构造方法,代码如下:

public class FlexibleListView extends ListView { 

 public FlexibleListView(Context context) {
 super(context);
 } 

 public FlexibleListView(Context context, AttributeSet attrs) {
 super(context, attrs);
 } 

 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 } 

 @TargetApi(21)
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
 super(context, attrs, defStyleAttr, defStyleRes);
 }
} 

FlexibleListView仅仅是继承了ListView,这本质上和ListView没有区别。既然我们是通过给ListView添加HeaderView的方式来判断滚动距离,那就要获取到HeaderView对象。怎么获取到HeaderView对象呢?这里有个技巧,由于给ListView添加HeaderView最终是调用ListView的addHeaderView(View v, Object data, boolean isSelected)方法,所以我们可以重写该方法,取到添加进来的第一个HeaderView,那怎么判断是第一个添加进来的HeaderView呢?因为HeaderView的添加是有序的即先添加的先绘制。所以可以定义一个代表第一个HeaderView的属性mHeaderView,当调用到addHeaderView()方法时通过判断mHeaderView的值是否为空,如果为空就赋值否则不赋值,代码如下:

public class FlexibleListView extends ListView { 

 private View mHeaderView;
 private int mMaxScrollHeight; 

 public FlexibleListView(Context context) {
 super(context);
 } 

 public FlexibleListView(Context context, AttributeSet attrs) {
 super(context, attrs);
 } 

 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 } 

 @TargetApi(21)
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
 super(context, attrs, defStyleAttr, defStyleRes);
 } 

 @Override
 public void addHeaderView(View v, Object data, boolean isSelectable) {
 super.addHeaderView(v, data, isSelectable);
 if(null == mHeaderView) {
 mHeaderView = v;
 mMaxScrollHeight = mHeaderView.getLayoutParams().height;
 }
 }
} 

FlexibleListView中定义了mHeaderView和mMaxScrollHeight属性,在addHeaderView()方法中对mHeaderView做非空判断来获取到第一个HeaderView并赋值给mHeadereView,mMaxScrollHeight表示HeaderView的最大滚动距离,当HeaderView的滚动距离超过此值我们就要设置菜单栏不透明否则就更改透明度。在这里我直接使用了HeaderView的高度来表示其允许滚动的最大距离。
        现在可以获取到ListView的第一个HeaderView,接下来就是判断ListView的滚动了,这时候有的童靴可能会想到采用给ListView添加ScrollListener的方式,这种方式是可行的,但我们这次不采用添加Listener的方式,如果你对ListView的源码比较熟悉的话就清楚触发OnItemScrollListener的回调时机是在AbsListView的invokeOnItemScrollListener()方法中,该方法源码如下:

/**
 * Notify our scroll listener (if there is one) of a change in scroll state
 */
void invokeOnItemScrollListener() {
 if (mFastScroll != null) {
 mFastScroll.onScroll(mFirstPosition, getChildCount(), mItemCount);
 }
 if (mOnScrollListener != null) {
 mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount);
 }
 onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
}

invokeOnItemScrollListener()方法就是触发滚动回调的,无论我们给不给ListView设置OnItemScrollListener那该方法都会调用,细心的同学可能发现在该方法最后调用了View的onScrollChanged()方法,这时候你恍然大悟,我们可以重写该方法呀,当ListView发生滚动了也就调用了onScrollChange()方法,多省事呀。呵呵,恭喜你,答对了,我们今天就是采用重写onScrollChanged()方法并在该方法中通过判断ListView的HeaderView的滚动距离来设置菜单栏的透明度的。
        现在我们清楚了ListView的滚动时机,也有了HeaderView和最大滚动距离,接下来就是分析实现渐变的条件了:要实现渐变我们就要清楚是谁要渐变,在我们的APP中可能是ActionBar,也可能是ToolBar,还有可能是我们自定义的一个ViewGroup来模拟的ActionBar,所以FlexibleListView得有个代表ActionBar的mActionBar属性并对外提供一个方法bindActionBar(),该方法就表示把需要实现渐变的ActionBar传递进来,代码如下:

public class FlexibleListView extends ListView { 

 private View mActionBar;
 private View mHeaderView;
 private int mMaxScrollHeight;
 private Drawable mActionBarBackground; 

 public FlexibleListView(Context context) {
 super(context);
 } 

 public FlexibleListView(Context context, AttributeSet attrs) {
 super(context, attrs);
 } 

 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 } 

 @TargetApi(21)
 public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
 super(context, attrs, defStyleAttr, defStyleRes);
 } 

 @Override
 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
 super.onScrollChanged(l, t, oldl, oldt);
 if(null != mActionBarBackground) {
 mActionBarBackground.setAlpha(evaluateAlpha(Math.abs(mHeaderView.getTop())));
 }
 } 

 @Override
 public void addHeaderView(View v, Object data, boolean isSelectable) {
 super.addHeaderView(v, data, isSelectable);
 if(null == mHeaderView) {
 mHeaderView = v;
 mMaxScrollHeight = mHeaderView.getLayoutParams().height;
 }
 } 

 private int evaluateAlpha(int t) {
 if (t >= mMaxScrollHeight) {
 return 255;
 }
 return (int) (255 * t /(float) mMaxScrollHeight);
 } 

 public void bindActionBar(View actionBar) {
 if(null != actionBar) {
 mActionBar = actionBar;
 mActionBarBackground = actionBar.getBackground();
 if(null == mActionBarBackground) {
 mActionBarBackground = new ColorDrawable(Color.TRANSPARENT);
 }
 mActionBarBackground.setAlpha(0);
 if(Build.VERSION.SDK_INT >= 16) {
 mActionBar.setBackground(mActionBarBackground);
 } else {
 mActionBar.setBackgroundDrawable(mActionBarBackground);
 }
 }
 } 

 public void bindActionBar(ActionBar actionBar) {
 if(null != actionBar) {
 // TODO impl with ActionBar
 // actionBar.setBackgroundDrawable();
 }
 }
} 

FlexibleListView新增了mActionBar和mActionBarBackground属性,mActionBar代表需要渐变的菜单栏,mActionBarBackground为菜单栏的背景。其次对外提供了重载方法bindActionBar(),参数为ActionBar的方法是空实现,里边添加了TODO提示符并给了setBackgroundDrawable()提示(注意ActionBar实现渐变需要设置WindowFeature),希望童靴们自己可以实现出来。
        FlexibleListView中重写了onScrollChanged()方法,在该方法中通过获取mHeaderView的getTop()值然后调用evaluateAlpha()方法计算出alpha值,evaluateAlpha()的计算很简单,当滚动值超过了最大滚动距离mMaxScrollHeight就返回255(255表示不透明,0表示透明),否则计算出当前滚动值所对应的alpha值,最后通过调用mActionBarBackground的setAlpha()来达到mActionBar的透明度变化。
        现在实现菜单栏的透明度的逻辑准备就绪了,我们先测试一下看看,定义菜单栏布局,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="@dimen/action_bar_height"
 android:background="#aabbcc"
 android:clickable="true"
 android:orientation="vertical"
 android:paddingLeft="10dp"> 

 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="center_vertical"
 android:drawableLeft="@mipmap/back"
 android:text="动态"
 android:textColor="#b8e7fe"
 android:textSize="17sp" /> 

 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="center"
 android:text="好友动态"
 android:textColor="#b8e7fe"
 android:textSize="17sp" /> 

</FrameLayout>

菜单栏包含一个返回按钮和一个标题,并且给菜单栏设置了固定高度和背景色,然后布局我们的activity_main.xml文件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"> 

 <com.llew.wb.git.qqzone.FlexibleListView
 android:id="@+id/flexible_list_view"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:scrollbars="none"></com.llew.wb.git.qqzone.FlexibleListView> 

 <include
 android:id="@+id/custom_action_bar"
 layout="@layout/action_bar_layout"/>
</FrameLayout>

activity_main.xml的布局文件很简单,采用FrameLayout根布局让菜单栏悬浮在FlexibleListView上边,然后编写我们的MainActivity代码,如下所示:

public class MainActivity extends AppCompatActivity { 

 private FlexibleListView mListView; 

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main); 

 initGlobalParams();
 } 

 private void initGlobalParams() {
 mListView = (FlexibleListView) findViewById(R.id.flexible_list_view); 

 View mFlexibleHeaderView = new View(getApplicationContext());
 mFlexibleHeaderView.setBackgroundColor(Color.parseColor("#bbaacc"));
 int height = getResources().getDimensionPixelSize(R.dimen.header_height);
 LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, height);
 mFlexibleHeaderView.setLayoutParams(params); 

 final View actionBar = findViewById(R.id.custom_action_bar); 

 mListView.bindActionBar(actionBar);
 mListView.addHeaderView(mFlexibleHeaderView); 

 mListView.setAdapter(new Adapter());
 } 

 static class Adapter extends BaseAdapter {
 @Override
 public int getCount() {
 return 80;
 } 

 @Override
 public Object getItem(int position) {
 return null;
 } 

 @Override
 public long getItemId(int position) {
 return 0;
 } 

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
 TextView textView = new TextView(parent.getContext());
 textView.setPadding(50, 50, 50, 50);
 textView.setText(position + 10 + "");
 return textView;
 }
 }
}

在MainActivity中给FlexibleListView添加了一个固定高度背景色为"#bbaacc"的Header,并把悬浮菜单栏actionBar赋值给了FlexibleListView的mActionBar,最后设置Adapter,为了测试代码写的很简单,我们运行一下程序,看看效果:

看到运行效果好开心呀,(*^__^*) ……透明度渐变功能达到了我们的预期,接下来开始实现阻尼效果,阻尼效果就是当ListView滚动到了顶部此时若继续下滑,ListView能够继续往下滚动一段距离当手指离开屏幕后ListView要恢复原位置。为了实现这个功能有的童靴可能会想到重写有关事件传递的onXXXEvent()等方法,之后在MotionEvent为DOWN,MOVE,UP或者CANCEL条件下分别做逻辑判断来实现阻尼效果,此方式可行,但是和今天我们的实现相比起来复杂了许多......
        这里所实现阻尼效果所采用的方法是利用View的overScrollBy()方法,有的童靴可能会问overScrollBy()方法是2.3版本之后才增加的,2.3版本之前的兼容性怎么办?我实现这个功能之前也考虑过这个问题,一方面我们公司的APP只支持3.0以上版本,另一方面2.3及以前的版本市场占有率几乎微乎其微了,所以可以考虑不再兼容2.3以前的老版本。
        有的同学或许对overScrollBy()方法比较陌生,先大致说一下该方法,其源码如下:

/**
 * Scroll the view with standard behavior for scrolling beyond the normal
 * content boundaries. Views that call this method should override
 * {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the
 * results of an over-scroll operation.
 *
 * Views can use this method to handle any touch or fling-based scrolling.
 *
 * @param deltaX Change in X in pixels
 * @param deltaY Change in Y in pixels
 * @param scrollX Current X scroll value in pixels before applying deltaX
 * @param scrollY Current Y scroll value in pixels before applying deltaY
 * @param scrollRangeX Maximum content scroll range along the X axis
 * @param scrollRangeY Maximum content scroll range along the Y axis
 * @param maxOverScrollX Number of pixels to overscroll by in either direction
 * along the X axis.
 * @param maxOverScrollY Number of pixels to overscroll by in either direction
 * along the Y axis.
 * @param isTouchEvent true if this scroll operation is the result of a touch event.
 * @return true if scrolling was clamped to an over-scroll boundary along either
 * axis, false otherwise.
 */
@SuppressWarnings({"UnusedParameters"})
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY,
 int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
 // ......
}

阅读源码看注释很重要,我们先看一下注释,大致意思如下:
        当View组件滚动到边界时还会继续进行之前的滚动操作(注意:没有滚动到边界时是不会触发该方法的),如果View组件调用了该方法那么View组件就应该重写onOverScrolled()方法来响应over-scroll操作。View控件可以调用该方法处理任何的触摸滚动或者是快速滑动等。感觉翻译的好别扭,说的直白点就是当ListView,ScrollView等滚动到头了若继续下滑就会调用该方法。
        overScrollBy()方法有9个参数,每个参数注释都说的很详细,我们只看需要用到的俩参数deltaY和isTouchEvent;deltaY表示的是在Y轴上滚动的相对值,比如ListView滚动到了顶部此时如果继续下拉,deltaY值为负数,当其滚动到了最底部当我们继续上拉,deltaY值为正数,所以我们可以根据deltaY判断ListView是上拉操作还是下拉操作,isTouchEvent为true表示手指在触摸屏幕否则离开屏幕。
        了解overScrollBy()方法后开始实现阻尼效果,核心就是重写overScrollBy()方法,在该方法中动态改变HeaderView的高度,若手指松开我们就复原HeaderView。我们知道QQ空间顶部是一张图片,当下拉的时候该图片有弹性拉升效果,当手指松开后图片又伸缩回去了,所以我们就直接用ImageView模拟此效果。模拟图片阻尼可以让ImageView的宽高为MATCH_PARENT(HeaderView的高度改变之后ImageView的高度也可以随之更改),这个时候还要设置ImageView的scaleType为CENTER_CROP(不清楚ImageView的scaleType属性可参照我之前写的一篇博文:Android 源码系列之<一>从源码的角度深入理解ImageView的ScaleType属性)。
        现在开始在FlexibleListView中重写overScrollBy()方法,代码如下:

@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
 if(null != mHeaderView) {
 if(isTouchEvent && deltaY < 0) {
 mHeaderView.getLayoutParams().height += Math.abs(deltaY / 3.0);
 mHeaderView.requestLayout();
 }
 }
 return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}

overScrollBy()方法中我们根据deltaY值动态的更改了mHeaderView的高度并重新布局达到更改ImageView高度的目的,注意:计算高度的时候用了deltaY除以3,此时的3表示增长因子,目的是让HeaderView缓慢的增长,这里可以对外提供一个方法来设置此值。
        现在仅实现了HeaderView的拉升功能,但是还没有实现缩放功能,因为overScrollBay()中实现的是手指触摸的下拉,当手指离开屏幕后要进行HeaderView的复原操作,所以我们可以在考虑在onTouchEvent()方法中判断MotionEvent的类型,当为UP或者CANCEL时就复原HeaderView,复原HeaderView不能一下子复原而是要用动画的方式,这样看上去才比较自然,所以onTouchEvent()代码如下:

@Override
public boolean onTouchEvent(MotionEvent ev) {
 if(null != mHeaderView) {
 int action = ev.getAction();
 if(MotionEvent.ACTION_UP == action || MotionEvent.ACTION_CANCEL == action) {
 resetHeaderViewHeight();
 }
 }
 return super.onTouchEvent(ev);
} 

private void resetHeaderViewHeight() {
 ValueAnimator valueAnimator = ValueAnimator.ofInt(1);
 valueAnimator.setDuration(700);
 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
 @Override
 public void onAnimationUpdate(ValueAnimator animation) {
 final float f = animation.getAnimatedFraction();
 mHeaderView.getLayoutParams().height -= f * (mHeaderView.getLayoutParams().height - mMaxScrollHeight);
 mHeaderView.requestLayout(); 

 }
 });
 valueAnimator.setInterpolator(new OvershootInterpolator());
 valueAnimator.start();
}

HeaderView的复原动画我们采用了ValueAnimator,当动画执行过程中我们动态的更改HeaderView的值来达到渐变效果。接下来布局HeaderView来模拟QQ空间,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="@dimen/header_height"> 

 <ImageView
 android:id="@+id/iv"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:scaleType="centerCrop"
 android:src="@mipmap/ttt" /> 

 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="30dp"
 android:layout_gravity="bottom"
 android:background="#33333333"
 android:gravity="center_vertical"
 android:orientation="horizontal"> 

 <TextView
 android:layout_width="0dp"
 android:layout_height="match_parent"
 android:layout_weight="1"
 android:text="相册"
 android:gravity="center"
 android:textColor="@android:color/white" /> 

 <View
 android:layout_width="1dp"
 android:layout_height="20dp"
 android:background="#ffffff" /> 

 <TextView
 android:layout_width="0dp"
 android:layout_height="match_parent"
 android:layout_weight="1"
 android:text="说说"
 android:gravity="center"
 android:textColor="@android:color/white" /> 

 <View
 android:layout_width="1dp"
 android:layout_height="20dp"
 android:background="#ffffff" /> 

 <TextView
 android:layout_width="0dp"
 android:layout_height="match_parent"
 android:layout_weight="1"
 android:text="个性化"
 android:gravity="center"
 android:textColor="@android:color/white" /> 

 <View
 android:layout_width="1dp"
 android:layout_height="20dp"
 android:background="#ffffff" /> 

 <TextView
 android:layout_width="0dp"
 android:layout_height="match_parent"
 android:layout_weight="1"
 android:text="\@ 与我相关"
 android:gravity="center"
 android:textColor="@android:color/white" /> 

 </LinearLayout>
</FrameLayout>

HeaderView的布局中让ImageView的宽高都设置成了match_parent并且把scaleType设置为centerCrop。修改MainActivity的initGlobalParams()方法,代码如下:

void initGlobalParams() {
 mListView = (FlexibleListView) findViewById(R.id.flexible_list_view);
 View mFlexibleHeaderView = LayoutInflater.from(this).inflate(R.layout.flexible_header_layout, mListView, false);
 AbsListView.LayoutParams params = (AbsListView.LayoutParams)mFlexibleHeaderView.getLayoutParams();
 if(null == params) {
 params = new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.WRAP_CONTENT);
 }
 params.height = getResources().getDimensionPixelSize(R.dimen.header_height);
 mFlexibleHeaderView.setLayoutParams(params); 

 final View actionBar = findViewById(R.id.custom_action_bar); 

 mListView.bindActionBar(actionBar);
 mListView.addHeaderView(mFlexibleHeaderView); 

 mListView.setAdapter(new Adapter());
}

OK,一切都准备就绪,赶紧运行一下程序,看看效果吧(*^__^*) ……

恩,看上去效果还不错......
 好了,有关实现QQ空间的阻尼下拉刷新和渐变菜单栏就结束了,主要是利用了2.3版本之后的overScrollBy()方法(如果要兼容2.3之前版本需要童靴们自己去实现相关逻辑);其次充分的利用了ImageView的ScaleType属性来模拟了QQ空间图片阻尼回弹的效果。再次感谢收看(*^__^*) ……

原文链接:http://blog.csdn.net/llew2011/article/details/51559694

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义view实现阻尼效果的加载动画

    效果: 需要知识: 1. 二次贝塞尔曲线 2. 动画知识 3. 基础自定义view知识 先来解释下什么叫阻尼运动 阻尼振动是指,由于振动系统受到摩擦和介质阻力或其他能耗而使振幅随时间逐渐衰减的振动,又称减幅振动.衰减振动.[1] 不论是弹簧振子还是单摆由于外界的摩擦和介质阻力总是存在,在振动过程中要不断克服外界阻力做功,消耗能量,振幅就会逐渐减小,经过一段时间,振动就会完全停下来.这种振幅随时间减小的振动称为阻尼振动.因为振幅与振动的能量有关,阻尼振动也就是能量不断减少的振动.阻尼振动是非简谐运

  • Android通过overScrollBy实现下拉视差特效

    overScrollBy实现下拉视差特效,效果图如下 先来分析overScrollBy方法的使用,它是View的方法,参数有点多: /** * 当滑动的超出上,下,左,右最大范围时回调 * * @param deltaX x方向的瞬时偏移量,左边到头,向右拉为负,右边到头,向左拉为正 * @param deltaY y方向的瞬时偏移量,顶部到头,向下拉为负,底部到头,向上拉为正 * @param scrollX 水平方向的永久偏移量 * @param scrollY 竖直方向的永久偏移量 * @

  • Android UI设计系列之自定义ListView仿QQ空间阻尼下拉刷新和渐变菜单栏效果(8)

    好久没有写有关UI的博客了,刚刚翻了一下之前的博客,最近一篇有关UI的博客:Android UI设计系列之自定义Dialog实现各种风格的对话框效果(7) ,实现各种风格效果的对话框,在那篇博客写完后由于公司封闭开发封网以及其它原因致使博客中断至今,中断这么久很是惭愧,后续我会尽量把该写的都补充出来.近来项目有个需求,要做个和QQ空间类似的菜单栏透明度渐变和下拉刷新带有阻尼回弹的效果.于是花点时间动手试了试,基本上达到了QQ空间的效果,截图如下: 通过观察QQ空间的运行效果,发现当往上滚动时菜单

  • Android UI设计系列之自定义ViewGroup打造通用的关闭键盘小控件ImeObserverLayout(9)

    转载请注明出处:http://blog.csdn.net/llew2011/article/details/51598682 我们平时开发中总会遇见一些奇葩的需求,为了实现这些需求我们往往绞尽脑汁有时候还茶不思饭不香的,有点夸张了(*^__^*)--我印象最深的一个需求是在一段文字中对部分词语进行加粗显示.当时费了不少劲,不过还好,这个问题最终解决了,有兴趣的童靴可以看一下:Android UI设计之<六>使用HTML标签,实现在TextView中对部分文字进行加粗显示. 之前产品那边提了这样

  • Android UI设计系列之自定义Dialog实现各种风格的对话框效果(7)

    虽然Android给我们提供了众多组件,但是使用起来都不是很方便,我们开发的APK都有自己的风格,如果使用了系统自带的组件,总是觉得和应用的主题不着边际并且看起来也不顺心,那我们就需要自定义了,为了方便大家对自定义组件的学习,我接下来准备了几遍有关自定义的Dialog的文章,希望对大家有帮助. 在开发APK中最常见的估计就数弹出对话框了,这种对话框按照按钮数量来分大致是三种:一个按钮,两个按钮,三个按钮.现在要讲的就是按照按钮数量分为以上三类吧(当然了可以有更多的按钮,只要你愿意). 自定义Di

  • Android UI设计系列之自定义DrawView组件实现数字签名效果(5)

    最近项目中有个新的需求,用户在完交易需要进行输入支付密码付款的时候,要让用户签下自己的签名,提起到数字签名这个东西,感觉有点高大上,后来想想数字签名的原理也不是太复杂,主要实现原理就是利用了View的绘图原理,把用户在屏幕上的手指移动轨迹显示在屏幕上,接着把在屏幕上显示的轨迹View转换成一张图片,最后把图片保存到本地或者上传到服务器... 还是老规矩,首先看一下工程目录吧: public class DrawView extends View { /** * 签名画笔 */ private P

  • Android UI设计系列之自定义TextView属性实现带下划线的文本框(4)

    在Android开发过程中,如果Android系统自带的属性不能满足我们日常开发的需求,那么就需要我们给系统控件添加额外的属性了.假如有个需求是实现带下划线的文本显示(下划线),如果不使用自定义属性的话实现起来也不太难(起码我认为的实现方式是有许多种的),今天就讲解一下如何使用自定义属性来实现上述带下划线的文本框吧.还好Android中自定义属性不是很复杂,也可以归纳为三步走吧. 老规矩,还是先贴出工程目录吧: 一.添加属性文件 在values文件夹中新建attrs.xml文件,在文件中新建属性

  • Android UI设计系列之自定义SwitchButton开关实现类似IOS中UISwitch的动画效果(2)

    做IOS开发的都知道,IOS提供了一个具有动态开关效果的UISwitch组件,这个组件很好用效果相对来说也很绚丽,当我们去点击开关的时候有动画效果,但遗憾的是Android上并没有给我们提供类似的组件(听说在Android4.0的版本上提供了具有动态效果的开关组件,不过我还没有去看文档),如果我们想实现类似的效果那该怎么办了呢?看来又得去自定义了. 公司的产品最近一直在做升级,主要做的就是把界面做的更绚丽更美观给用户更好的体验(唉,顾客是上帝......),其中的设置功能中就有开关按钮,原来的开

  • Android UI设计系列之自定义EditText实现带清除功能的输入框(3)

    最近公司的产品在陆续做升级,上级领导给的任务是优化代码结构以及项目架构,力争把项目写的精巧简练,于是我们满工程找冗余... 我们都知道每一个项目基本上都是有登陆页的,在登陆页中肯定是少不了输入框了,当我们在输入框中输入数据后如果输入的内容不正确或者是错误的或者是想重新输入,如果嗯键盘上的删除键就得一个一个的去删除,这时候我们或许就想要是能有一个标记当点击了这个标记能把我们刚刚输入的内容清空就好了.这样可以极大的提升用户体验,就拿QQ的登陆来说吧,效果如下: 当点击密码框右侧的小×图标时输入的内容

  • Android自定义View仿腾讯TIM下拉刷新View

    一 概述 自定义 View 是 Android 开发里面的一个大学问.偶然间看到 TIM 邮箱界面的刷新 View 还挺好玩的,于是就自己动手实现了一个,先看看 TIM 里边的效果图: 二 需求分析 看到上面的动图,大概也知道我们需要实现的功能: 根据拖动的进度来移动小球的位置 小球移动过程的动画 三 功能实现 新建一个 RefreshView 类继承自 View ,然后我们再在 RefreshView 里面新建一个内部实体类: Circle 来看一下 Circle类的代码 #Cirlce.ja

  • Android仿QQ空间顶部条背景变化效果

    本文给大家分享仿QQ空间页面顶部条随界面滑动背景透明度变化的效果,这个效果在其他应用程序中也很常见,技能+1. 一.上代码,具体实现 笔者之前的文章第二部分总是二话不说,直接上代码,很干脆,其实更好的方式是引导读者思考:这个效果如何实现.前期做好效果的功能分析,才能读者更好的理解. QQ空间的这个页面其实并不复杂,我们看看QQ空间的演示界面: 可以看见,整个页面其实只有两个根元素,一个是ListView,一个是标题栏,前者可以上下滑动,给用户呈现内容:后者固定位置不动,类似于一个导航栏,左边一个

  • Android 自定义ListView实现QQ空间界面(说说内包含图片、视频、点赞、评论、转发功能)

    前端时间刚好需要做一个类似于QQ空间的社区分享功能,说说内容包含文字(话题.内容).视频.图片,还需包含点赞,评论,位置信息等功能. 就采用LIstview做了一个,先来看下效果,GIF太大,CSDN传不了,请移步Gitee连接:GIF效果 1. 先来分析一下ListView中每一个条目包含的控件,请看下图 序号1:头像,ImageView,自定义为圆形即可: 序号2:用户名,TextView; 序号3:发布时间,TextView; 序号4:说说文字部分,TextView; 序号5:说说中视频或

随机推荐