通过overScrollBy实现下拉视差特效(阻尼效果)

效果图:


先来分析overScrollBy方法的使用,它是View的方法,参数有点多:

[java]  view plain  copy
  1. /** 
  2.     * 当滑动的超出上,下,左,右最大范围时回调 
  3.     * 
  4.     * @param deltaX         x方向的瞬时偏移量,左边到头,向右拉为负,右边到头,向左拉为正 
  5.     * @param deltaY         y方向的瞬时偏移量,顶部到头,向下拉为负,底部到头,向上拉为正 
  6.     * @param scrollX        水平方向的永久偏移量 
  7.     * @param scrollY        竖直方向的永久偏移量 
  8.     * @param scrollRangeX   水平方向滑动的范围 
  9.     * @param scrollRangeY   竖直方向滑动的范围 
  10.     * @param maxOverScrollX 水平方向最大滑动范围 
  11.     * @param maxOverScrollY 竖直方向最大滑动范围 
  12.     * @param isTouchEvent   是否是手指触摸滑动, true为手指, false为惯性 
  13.     * @return 
  14.     */  
  15.    @Override  
  16.    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,  
  17.                                   int scrollRangeX, int scrollRangeY, int maxOverScrollX,  
  18.                                   int maxOverScrollY, boolean isTouchEvent) {  
  19.        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,  
  20.                scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY,  
  21.                isTouchEvent);  
  22.    }  


大致步骤如下:

1.这整体是一个ListView,所以需要自定义一个ListView.

2.处理头部布局文件,将其以HeaderView的方式添加到自定义的ListView中

3.需要获取HeaderView的ImageView的初始高度和ImageView中图片的高度.因为这2个高度将决定下来的时候图片拉出的范围,以及松手后图片回弹的动画效果.对应控件宽高的获取,有兴趣的可以看这篇文章浅谈自定义View的宽高获取

4.在overScrollBy方法内通过修改ImageView的LayoutParams的height值来显示更多的图片内容.

5.在onTouchEvent方法内处理ACTION_UP事件,使ImageView有回弹的动画效果,这里介绍2种方式,分别是属性动画和自定义动画.


好了,先来看HeaderView的布局文件:

[html]  view plain  copy
  1. xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.     <ImageView  
  7.         android:id="@+id/imageView"  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="160dp"  
  10.         <span style="color:#ff0000;">android:scaleType="centerCrop"span>  
  11.         android:src="@drawable/header" />  
  12. LinearLayout>  


没什么特别的,就是一个ImageView,通过src设置了一张图片,这里唯一要将的就是scaleType属性,我这边设置了centerCrop,以图片的最小的边开始截取,因为这里选择的图片是高度大于宽度的,所以裁剪的时候会保留完整的宽度,中心裁剪,如下图所示:

通过overScrollBy实现下拉视差特效(阻尼效果)_第1张图片

自定义ListView代码,整体代码还是比较简短的.

[java]  view plain  copy
  1. /** 
  2.  * Created by mChenys on 2015/12/23. 
  3.  */  
  4. public class MyListView extends ListView {  
  5.     private ImageView mHeaderIv; //HeaderView 的ImageView  
  6.     private int mOriginalHeight; //最初ImageView的高度  
  7.     private int mDrawableHeight;//ImageView中图片的高度  
  8.   
  9.     public MyListView(Context context) {  
  10.         this(context, null);  
  11.     }  
  12.   
  13.     public MyListView(Context context, AttributeSet attrs) {  
  14.         this(context, attrs, 0);  
  15.     }  
  16.   
  17.     public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {  
  18.         super(context, attrs, defStyleAttr);  
  19.         init();  
  20.     }  
  21.   
  22.     /** 
  23.      * 设置头部和获取高度信息 
  24.      */  
  25.     private void init() {  
  26.         //初始化头部文件  
  27.         View headerView = View.inflate(getContext(), R.layout.view_header, null);  
  28.         mHeaderIv = (ImageView) headerView.findViewById(R.id.imageView);  
  29.         //将其添加到ListView的头部  
  30.         addHeaderView(headerView);  
  31.         //通过设置监听来获取控件的高度  
  32.         mHeaderIv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {  
  33.             @TargetApi(Build.VERSION_CODES.JELLY_BEAN)  
  34.             @Override  
  35.             public void onGlobalLayout() {  
  36.                 //只需监听一次,否则之后的onLayout方法回调的时候还是会回调这里  
  37.                 mHeaderIv.getViewTreeObserver().removeOnGlobalLayoutListener(this);  
  38.                 mOriginalHeight = mHeaderIv.getMeasuredHeight();//获取ImageView的初始高度  
  39.                 mDrawableHeight = mHeaderIv.getDrawable().getIntrinsicHeight();//获取ImageView中图片的高度  
  40.             }  
  41.         });  
  42.         //去掉下拉到头部后的蓝色线  
  43.         setOverScrollMode(OVER_SCROLL_NEVER);  
  44.     }  
  45.   
  46.     /** 
  47.      * 当滑动的超出上,下,左,右最大范围时回调 
  48.      * 
  49.      * @param deltaX         x方向的瞬时偏移量,左边到头,向右拉为负,右边到头,向左拉为正 
  50.      * @param deltaY         y方向的瞬时偏移量,顶部到头,向下拉为负,底部到头,向上拉为正 
  51.      * @param scrollX        水平方向的永久偏移量 
  52.      * @param scrollY        竖直方向的永久偏移量 
  53.      * @param scrollRangeX   水平方向滑动的范围 
  54.      * @param scrollRangeY   竖直方向滑动的范围 
  55.      * @param maxOverScrollX 水平方向最大滑动范围 
  56.      * @param maxOverScrollY 竖直方向最大滑动范围 
  57.      * @param isTouchEvent   是否是手指触摸滑动, true为手指, false为惯性 
  58.      * @return 
  59.      */  
  60.     @Override  
  61.     protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,  
  62.                                    int scrollRangeX, int scrollRangeY, int maxOverScrollX,  
  63.                                    int maxOverScrollY, boolean isTouchEvent) {  
  64.         // 手指拉动并且是下拉  
  65.         if (isTouchEvent && deltaY < 0) {  
  66.             // 把拉动的瞬时变化量的绝对值交给Header, 就可以实现放大效果  
  67.             if (mHeaderIv.getHeight() <= mDrawableHeight) {  
  68.                 // 高度不超出图片最大高度时,才让其生效  
  69.                 int newHeight = (int) (mHeaderIv.getHeight() + Math.abs(deltaY / 3.0f));//这里除以3是为了达到视差的效果  
  70.                 mHeaderIv.getLayoutParams().height = newHeight;  
  71.                 //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位  
  72.                 mHeaderIv.requestLayout();  
  73.             }  
  74.         }  
  75.         return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);  
  76.     }  
  77.   
  78.     @Override  
  79.     public boolean onTouchEvent(MotionEvent ev) {  
  80.         switch (ev.getAction()) {  
  81.             case MotionEvent.ACTION_UP:  
  82.                 // 执行回弹动画, 方式一: 属性动画\值动画  
  83.                 //获取ImageView在松手时的高度  
  84.                 int currHeight = mHeaderIv.getHeight();  
  85.                 // 从当前高度mHeaderIv.getHeight(), 执行动画到原始高度mOriginalHeight  
  86.                 ValueAnimator animator = ValueAnimator.ofInt(currHeight, mOriginalHeight);  
  87.                 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  88.                     @Override  
  89.                     public void onAnimationUpdate(ValueAnimator animation) {  
  90.                         int value = (int) animation.getAnimatedValue();  
  91.                         mHeaderIv.getLayoutParams().height = value;  
  92.                         //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位  
  93.                         mHeaderIv.requestLayout();  
  94.                     }  
  95.                 });  
  96.                 animator.setDuration(500);  
  97.                 animator.setInterpolator(new OvershootInterpolator());  
  98.                 animator.start();  
  99.   
  100.                 //方式二,通过自定义动画  
  101.                 /*ResetAnimation animation = new ResetAnimation(mHeaderIv, mHeaderIv.getHeight(), mOriginalHeight); 
  102.                 startAnimation(animation);*/  
  103.                 break;  
  104.         }  
  105.         return super.onTouchEvent(ev);  
  106.     }  
  107. }  


看看自定义动画:

[java]  view plain  copy
  1. /** 
  2.  * 自定义动画 
  3.  * Created by mChenys on 2015/12/24. 
  4.  */  
  5. public class ResetAnimation extends Animation {  
  6.     private final ImageView headerIv; //要执行动画的目标ImageView  
  7.     private final int startHeight;//执行动画的开始时的高度  
  8.     private final int endHeight;//执行动画结束时的高度  
  9.     private IntEvaluator mEvaluator; //整型估值器  
  10.   
  11.     /** 
  12.      * 构造方法初始化 
  13.      * 
  14.      * @param headerIv    应用动画的目标控件 
  15.      * @param startHeight 开始的高度 
  16.      * @param endHeight   结束的高度 
  17.      */  
  18.     public ResetAnimation(ImageView headerIv, int startHeight, int endHeight) {  
  19.         this.headerIv = headerIv;  
  20.         this.startHeight = startHeight;  
  21.         this.endHeight = endHeight;  
  22.         //定义一个int类型的类型估值器,用于获取实时变化的高度值  
  23.         mEvaluator = new IntEvaluator();  
  24.         //设置动画持续时间  
  25.         setDuration(500);  
  26.         //设置插值器  
  27.         setInterpolator(new OvershootInterpolator());  
  28.     }  
  29.   
  30.     /** 
  31.      * 在指定的时间内一直执行该方法,直到动画结束 
  32.      * interpolatedTime:0-1  标识动画执行的进度或者百分比 
  33.      * 
  34.      * @param interpolatedTime 
  35.      * @param t 
  36.      */  
  37.     @Override  
  38.     protected void applyTransformation(float interpolatedTime, Transformation t) {  
  39.         int currHeight = mEvaluator.evaluate(interpolatedTime, startHeight, endHeight);  
  40.         //通过LayoutParams不断的改变其高度  
  41.         headerIv.getLayoutParams().height = currHeight;  
  42.         //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位  
  43.         headerIv.requestLayout();  
  44.     }  
  45. }  

MainActivity测试类:

[java]  view plain  copy
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         MyListView listView = new MyListView(this);  
  7.         listView.setDividerHeight(1);  
  8.         listView.setSelector(new ColorDrawable());  
  9.         listView.setCacheColorHint(Color.TRANSPARENT);  
  10.         listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Cheeses.NAMES));  
  11.         setContentView(listView);  
  12.     }  
  13. }  


文章来自:http://blog.csdn.net/mchenys/article/details/50409938

你可能感兴趣的:(安卓)