您的位置:首页 > 其它

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

2017-08-02 17:58 477 查看
效果图:



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

[java] view
plain copy

/** 

    * 当滑动的超出上,下,左,右最大范围时回调 

    * 

    * @param deltaX         x方向的瞬时偏移量,左边到头,向右拉为负,右边到头,向左拉为正 

    * @param deltaY         y方向的瞬时偏移量,顶部到头,向下拉为负,底部到头,向上拉为正 

    * @param scrollX        水平方向的永久偏移量 

    * @param scrollY        竖直方向的永久偏移量 

    * @param scrollRangeX   水平方向滑动的范围 

    * @param scrollRangeY   竖直方向滑动的范围 

    * @param maxOverScrollX 水平方向最大滑动范围 

    * @param maxOverScrollY 竖直方向最大滑动范围 

    * @param isTouchEvent   是否是手指触摸滑动, true为手指, false为惯性 

    * @return 

    */  

   @Override  

   protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,  

                                  int scrollRangeX, int scrollRangeY, int maxOverScrollX,  

                                  int maxOverScrollY, boolean isTouchEvent) {  

       return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,  

               scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY,  

               isTouchEvent);  

   }  

大致步骤如下:

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

<?xml version="1.0" encoding="utf-8"?>  

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  

    android:layout_width="match_parent"  

    android:layout_height="match_parent"  

    android:orientation="vertical" >  

    <ImageView  

        android:id="@+id/imageView"  

        android:layout_width="match_parent"  

        android:layout_height="160dp"  

        <span style="color:#ff0000;">android:scaleType="centerCrop"</span>  

        android:src="@drawable/header" />  

</LinearLayout>  

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



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

[java] view
plain copy

/** 

 * Created by mChenys on 2015/12/23. 

 */  

public class MyListView extends ListView {  

    private ImageView mHeaderIv; //HeaderView 的ImageView  

    private int mOriginalHeight; //最初ImageView的高度  

    private int mDrawableHeight;//ImageView中图片的高度  

  

    public MyListView(Context context) {  

        this(context, null);  

    }  

  

    public MyListView(Context context, AttributeSet attrs) {  

        this(context, attrs, 0);  

    }  

  

    public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {  

        super(context, attrs, defStyleAttr);  

        init();  

    }  

  

    /** 

     * 设置头部和获取高度信息 

     */  

    private void init() {  

        //初始化头部文件  

        View headerView = View.inflate(getContext(), R.layout.view_header, null);  

        mHeaderIv = (ImageView) headerView.findViewById(R.id.imageView);  

        //将其添加到ListView的头部  

        addHeaderView(headerView);  

        //通过设置监听来获取控件的高度  

        mHeaderIv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {  

            @TargetApi(Build.VERSION_CODES.JELLY_BEAN)  

            @Override  

            public void onGlobalLayout() {  

                //只需监听一次,否则之后的onLayout方法回调的时候还是会回调这里  

                mHeaderIv.getViewTreeObserver().removeOnGlobalLayoutListener(this);  

                mOriginalHeight = mHeaderIv.getMeasuredHeight();//获取ImageView的初始高度  

                mDrawableHeight = mHeaderIv.getDrawable().getIntrinsicHeight();//获取ImageView中图片的高度  

            }  

        });  

        //去掉下拉到头部后的蓝色线  

        setOverScrollMode(OVER_SCROLL_NEVER);  

    }  

  

    /** 

     * 当滑动的超出上,下,左,右最大范围时回调 

     * 

     * @param deltaX         x方向的瞬时偏移量,左边到头,向右拉为负,右边到头,向左拉为正 

     * @param deltaY         y方向的瞬时偏移量,顶部到头,向下拉为负,底部到头,向上拉为正 

     * @param scrollX        水平方向的永久偏移量 

     * @param scrollY        竖直方向的永久偏移量 

     * @param scrollRangeX   水平方向滑动的范围 

     * @param scrollRangeY   竖直方向滑动的范围 

     * @param maxOverScrollX 水平方向最大滑动范围 

     * @param maxOverScrollY 竖直方向最大滑动范围 

     * @param isTouchEvent   是否是手指触摸滑动, true为手指, false为惯性 

     * @return 

     */  

    @Override  

    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,  

                                   int scrollRangeX, int scrollRangeY, int maxOverScrollX,  

                                   int maxOverScrollY, boolean isTouchEvent) {  

        // 手指拉动并且是下拉  

        if (isTouchEvent && deltaY < 0) {  

            // 把拉动的瞬时变化量的绝对值交给Header, 就可以实现放大效果  

            if (mHeaderIv.getHeight() <= mDrawableHeight) {  

                // 高度不超出图片最大高度时,才让其生效  

                int newHeight = (int) (mHeaderIv.getHeight() + Math.abs(deltaY / 3.0f));//这里除以3是为了达到视差的效果  

                mHeaderIv.getLayoutParams().height = newHeight;  

                //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位  

                mHeaderIv.requestLayout();  

            }  

        }  

        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);  

    }  

  

    @Override  

    public boolean onTouchEvent(MotionEvent ev) {  

        switch (ev.getAction()) {  

            case MotionEvent.ACTION_UP:  

                // 执行回弹动画, 方式一: 属性动画\值动画  

                //获取ImageView在松手时的高度  

                int currHeight = mHeaderIv.getHeight();  

                // 从当前高度mHeaderIv.getHeight(), 执行动画到原始高度mOriginalHeight  

                ValueAnimator animator = ValueAnimator.ofInt(currHeight, mOriginalHeight);  

                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  

                    @Override  

                    public void onAnimationUpdate(ValueAnimator animation) {  

                        int value = (int) animation.getAnimatedValue();  

                        mHeaderIv.getLayoutParams().height = value;  

                        //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位  

                        mHeaderIv.requestLayout();  

                    }  

                });  

                animator.setDuration(500);  

                animator.setInterpolator(new OvershootInterpolator());  

                animator.start();  

  

                //方式二,通过自定义动画  

                /*ResetAnimation animation = new ResetAnimation(mHeaderIv, mHeaderIv.getHeight(), mOriginalHeight); 

                startAnimation(animation);*/  

                break;  

        }  

        return super.onTouchEvent(ev);  

    }  

}  

看看自定义动画:

[java] view
plain copy

/** 

 * 自定义动画 

 * Created by mChenys on 2015/12/24. 

 */  

public class ResetAnimation extends Animation {  

    private final ImageView headerIv; //要执行动画的目标ImageView  

    private final int startHeight;//执行动画的开始时的高度  

    private final int endHeight;//执行动画结束时的高度  

    private IntEvaluator mEvaluator; //整型估值器  

  

    /** 

     * 构造方法初始化 

     * 

     * @param headerIv    应用动画的目标控件 

     * @param startHeight 开始的高度 

     * @param endHeight   结束的高度 

     */  

    public ResetAnimation(ImageView headerIv, int startHeight, int endHeight) {  

        this.headerIv = headerIv;  

        this.startHeight = startHeight;  

        this.endHeight = endHeight;  

        //定义一个int类型的类型估值器,用于获取实时变化的高度值  

        mEvaluator = new IntEvaluator();  

        //设置动画持续时间  

        setDuration(500);  

        //设置插值器  

        setInterpolator(new OvershootInterpolator());  

    }  

  

    /** 

     * 在指定的时间内一直执行该方法,直到动画结束 

     * interpolatedTime:0-1  标识动画执行的进度或者百分比 

     * 

     * @param interpolatedTime 

     * @param t 

     */  

    @Override  

    protected void applyTransformation(float interpolatedTime, Transformation t) {  

        int currHeight = mEvaluator.evaluate(interpolatedTime, startHeight, endHeight);  

        //通过LayoutParams不断的改变其高度  

        headerIv.getLayoutParams().height = currHeight;  

        //此方法必须调用,调用后会重新调用onMeasure和onLayout方法进行测量和定位  

        headerIv.requestLayout();  

    }  

}  

MainActivity测试类:

[java] view
plain copy

public class MainActivity extends AppCompatActivity {  

  

    @Override  

    protected void onCreate(Bundle savedInstanceState) {  

        super.onCreate(savedInstanceState);  

        MyListView listView = new MyListView(this);  

        listView.setDividerHeight(1);  

        listView.setSelector(new ColorDrawable());  

        listView.setCacheColorHint(Color.TRANSPARENT);  

        listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Cheeses.NAMES));  

        setContentView(listView);  

    }  

}  

文章来自:http://blog.csdn.net/mchenys/article/details/50409938
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: