自定义View之头部可伸缩的ListView

最近看了动脑学院一些视频资料,讲的是通过自定义View实现一个头部可伸缩的ListView,觉得实现不难,而且思路挺有启发性的,于是就根据资料自己写了相关代码实现了一个。

因为添加gif失败,就添加图片吧。。

正常状态:

正常状态

手指下拉状态:

手指下拉状态

这里的思路首先是给ListView添加一个Header,一般是一个渲染一个包含一张图片的布局的View,然后通过检测手指过度下拉的尺寸取改变Header的属性使得图片缩放。

完成这一过程主要要解决这几个问题:

1.如何使得图片缩放:

这里巧妙运用了ImageVIew的ScaleType中的centerCrop属性(图片中心在ImageView的中心,图片铺满ImageView,即图片最小边至少和ImageView一样大),于是可以通过改变ImageView的高度来改变图片的大小。

2.如何检测过度下拉:
首先什么是过度下(上)拉?它就是当ListView的内容已经全部展开的时候,即ListView内容顶部到达ListView控件顶部或者内容底部没有超过ListView控件底部的时候,再去分别下拉和上拉就属于过度下上拉。

安卓系统View已经有方法去检测,当ListView过度上下拉的时候,系统会回调该方法,将上下拉的距离作为参数传进来,官方的注释是。

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.

这里ListView已经重写好了onOverScrolled实现滑动,所以这里要做的就是重写该方法,通过传进来的deltaY进行处理Header图片大小。

3.如何在不过度上下拉的情况下改变图片大小(比如过度下拉后假如此时ListView内容底部已经超过ListView控件底部时候,手指往上滑):
其实和2一样,只是重写的是onScrollChanged方法,该方法是当ListView内部内容滑动的时候回调。

4.如何在手指下拉之后离开屏幕让ListView自动滑动返回原来位置:
这里使用自定义动画,重写onTouchEvent方法,检测到手指离开屏幕则让ImageVeiw做动画恢复原来大小。

上面是基本思路,下面给出具体的代码:

public class ExtendHeaderListView extends ListView {
    private ImageView mImageView;
    private static int mImageViewHeight = 400;

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

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

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


    /**
     * 监听下拉过度,注意,只有在ListView顶部到屏幕顶部继续下拉或者底部到屏幕底部继续上推才会调用
     */
    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX
            , int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
        Log.d("overScrollBy","deltaY:" + deltaY + "  scrollY:" + scrollY + "   scrollRangeY:" + scrollRangeY +  "   maxOverScrollY:" + maxOverScrollY);
        if (mImageView != null){
            if (deltaY < 0){
                //过度下拉
                mImageView.getLayoutParams().height = mImageView.getHeight() - deltaY;
                mImageView.requestLayout();

            }else {
                //过度上拉。注意这里是当图片已经因下拉而放大的时候上滑,而因为ListView的底部还未超过屏幕底部时候,此时上拉属于过度上拉
                if (mImageView.getHeight() > mImageViewHeight){
                    mImageView.getLayoutParams().height = mImageView.getHeight() - deltaY;
                    mImageView.requestLayout();
                }
            }
        }

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


    /**
     * 当过度下拉之后再用手指滑回去
     *
     * 注意,当ListView内容全部展开的时候,即往上滑(此时属于过度上拉)不动了,就不会回调这个方法了
     * 该方法只在ListView内部内容滑动的时候回调
     */
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        View header = (View) mImageView.getParent();
        if (mImageView.getHeight() > mImageViewHeight){
            mImageView.getLayoutParams().height = mImageView.getHeight() + header.getTop();
            header.layout(header.getLeft(),0,header.getRight(),header.getHeight());
            mImageView.requestLayout();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            //手指离开屏幕,图片恢复原来的大小
            case MotionEvent.ACTION_UP:
                ShrinkAnimation animation = new ShrinkAnimation(mImageView,mImageView.getHeight(),mImageViewHeight);
                animation.setDuration(500);
                mImageView.startAnimation(animation);
                break;
        }
        return super.onTouchEvent(ev);
    }

    public void setImageView(ImageView imageView) {
        mImageView = imageView;
    }

    /**
     * 松手缩回动画
     */
    private class ShrinkAnimation extends Animation{
        private ImageView mImageView;
        private int originHeight;
        private int targetHeight;

        public ShrinkAnimation(ImageView imageView, int originHeight, int targetHeight) {
            mImageView = imageView;
            this.originHeight = originHeight;
            this.targetHeight = targetHeight;
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            mImageView.getLayoutParams().height = (int) (originHeight - (originHeight - targetHeight)*interpolatedTime);
            mImageView.requestLayout();
        }
    }
}

有了思路看懂代码不难,重点的代码也写了注释。

项目代码都在这里

代码下载地址

发布了69 篇原创文章 · 获赞 76 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/sinat_23092639/article/details/71945168