美文网首页自定义控件
自定义View之PullToZoomListView

自定义View之PullToZoomListView

作者: rockman_ | 来源:发表于2016-05-21 15:17 被阅读0次

看到这个控件很炫,一直想研究,拖了好几天,今天终于手打出来了。好了现在展示下成果。github地址:https://github.com/matrixxun/PullToZoomInListView

效果

pulltoview.gif

实现代码

public class PullToZoomListView extends ListView  implements
            AbsListView.OnScrollListener{

    private static final int INVALID_VALUE = -1;
    private static final String TAG = "PullToZoomListView";

    int mActivePointerId = -1;
    private FrameLayout mHeaderContainer;
    private int mHeadHeight;//头部视图的高
    private ImageView mHeaderImage;

    //偏移量
    float mLastMotionY = -1.0F;
    float mLastScale = -1.0F;
    float mMaxScale = -1.0F;

    //抽象的listView
    private AbsListView.OnScrollListener mOnScrollListener;
    private ScalingRunnalable mScalingRunnalable;
    private int mScreenHeight;  //屏幕的高
    private ImageView mShadow;

    //定义动画变化的速率
    /**
     * Interpolator定义了动画变化的速率,在Animations框架当中定义了一下几种Interpolator
     Ø         AccelerateDecelerateInterpolator:在动画开始与结束的地方速率改变比较慢,在中间的时候速率快。
     Ø         AccelerateInterpolator:在动画开始的地方速率改变比较慢,然后开始加速
     Ø         CycleInterpolator:动画循环播放特定的次数,速率改变沿着正弦曲线
     Ø         DecelerateInterpolator:在动画开始的地方速率改变比较慢,然后开始减速
     Ø         LinearInterpolator:动画以均匀的速率改变
     */
    private static final Interpolator sInterpolatar = new Interpolator(){
        @Override
        public float getInterpolation(float input) {
            float f = input - 1.0F;
            return 1.0F + f * (f*(f*(f*f)));
        }
    };
    public PullToZoomListView(Context context) {
        super(context);
        init(context);
    }

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

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

    private void endScraling(){
        //下拉开始
    if (this.mHeaderContainer.getBottom() >= this.mHeadHeight)
        Log.d("mmm","endScraling");
        this.mScalingRunnalable.startAnimation(200L);
    }

    private void init(Context paramContext){
        //屏幕的像素
        DisplayMetrics localDisplayMetrics = new DisplayMetrics();

        ///将当前窗口的一些信息放在DisplayMetrics类中,
        ((Activity)paramContext).getWindowManager().getDefaultDisplay()
                .getMetrics(localDisplayMetrics);

        //屏幕高度
        this.mScreenHeight = localDisplayMetrics.heightPixels;
        this.mHeaderContainer  = new FrameLayout(paramContext);
        this.mHeaderImage = new ImageView(paramContext);
        //屏幕宽度
        int i = localDisplayMetrics.widthPixels;
        setHeaderViewSize(i, (int) (9.0F * (i / 16.0F)));

        this.mShadow = new ImageView(paramContext);

        //指定了该布局的宽和高(-1为宽,-2为高)
        FrameLayout.LayoutParams localLayoutParams = new FrameLayout.LayoutParams(-1,-2);
        localLayoutParams.gravity = 80;
        this.mShadow.setLayoutParams(localLayoutParams);
        this.mHeaderContainer.addView(this.mHeaderImage);
        this.mHeaderContainer.addView(this.mShadow);
        addHeaderView(this.mHeaderContainer);

        //ScalingRunnalable
        this.mScalingRunnalable = new ScalingRunnalable();
        super.setOnScrollListener(this);
    }

    //第二次下拉设为0
    private void onSecondaryPointerUp(MotionEvent paramMotionEvent){
        int i = (paramMotionEvent.getAction()) >> 8;
        if (paramMotionEvent.getPointerId(i) == this.mActivePointerId)
            if (i != 0){
                int j = 1;
                this.mLastMotionY = paramMotionEvent.getY(0);
                this.mActivePointerId = paramMotionEvent.getPointerId(0);
                return;
            }
    }

    private void reset(){
        this.mActivePointerId = -1;
        this.mLastMotionY = -1.0F;
        this.mMaxScale = -1.0F;
        this.mLastScale = -1.0F;
    }

    public ImageView getHeaderView(){
        return this.mHeaderImage;
    }

    //返回true表示拦截。
    public boolean onInterceptTouchEvent(MotionEvent paramMotionEvent) {
        return super.onInterceptTouchEvent(paramMotionEvent);
    }

    // 确定View的位置
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (this.mHeadHeight == 0){
            this.mHeadHeight = this.mHeaderContainer.getHeight();
        }
    }

    @Override
    public void onScroll(AbsListView paramAbsListview, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        float f = this.mHeadHeight - this.mHeaderContainer.getBottom();

        //偏移量
        Log.d("fullview", "f+" + f);
        if ((f > 0.0f) && (f < this.mHeadHeight)){
            int i = (int) (0.65D * f);
            this.mHeaderImage.scrollTo(0 , -i);
            //没有向下滑
        } else if(this.mHeaderContainer.getScrollY() != 0){
            this.mHeaderContainer.scrollTo(0,0);
        }
        if (this.mOnScrollListener != null){
            this.mOnScrollListener.onScroll(paramAbsListview, firstVisibleItem,
                    visibleItemCount, totalItemCount);
        }

    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (this.mOnScrollListener != null){
            this.mOnScrollListener.onScrollStateChanged(view,
                    scrollState);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        switch (MotionEvent.ACTION_MASK & ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                if (!this.mScalingRunnalable.mIsFinished){
                    //结束动画
                    this.mScalingRunnalable.abortAnimation();
                }
                this.mLastMotionY = ev.getY();
                this.mActivePointerId = ev.getPointerId(0);
                this.mMaxScale = (this.mScreenHeight / this.mHeadHeight);
                this.mLastScale = (this.mHeaderContainer.getBottom() / this.mHeadHeight);
                break;
            case MotionEvent.ACTION_MOVE:
                //获得指定ID的pointer在当前事件中的index。
                int j = ev.findPointerIndex(this.mActivePointerId);
                if (j == -1){
                    Log.e("pullToZoomListView", "Invalid pointerId"
                    + this.mActivePointerId + "in onTouchEvent");
                } else {
                    if (this.mLastMotionY == -1.0F)
                        this.mLastMotionY = ev.getY(j);
                    //如果底部大于图片的高
                    if (this.mHeaderContainer.getBottom() >= this.mHeadHeight){
                        //下滑
                        ViewGroup.LayoutParams localLayoutParams = this.mHeaderContainer.getLayoutParams();
                        float f = ((ev.getY(j) - this.mLastMotionY + this.mHeaderContainer.
                                getBottom()) / this.mHeadHeight - this.mLastScale)
                                /2.0F + this.mLastScale;
                        if ((this.mLastScale <= 1.0D) && (f < this.mLastScale)){
                            //初始化param的高,没有改变还是原来样子
                            localLayoutParams.height = this.mHeadHeight;
                            this.mHeaderContainer.setLayoutParams(localLayoutParams);
                            return super.onTouchEvent(ev);
                        }
                        this.mLastScale = Math.min(Math.max(f, 1.0F),this.mMaxScale);

                        //高度乘偏移量 为 拉伸的图片高度
                        localLayoutParams.height = (int)((this.mHeadHeight * this.mLastScale));
                        if (localLayoutParams.height < this.mScreenHeight)
                            this.mHeaderContainer.setLayoutParams(localLayoutParams);
                        this.mLastMotionY = ev.getY(j);
                        return true;
                    }
                    this.mLastMotionY = ev.getY(j);
                }
                break;
            case MotionEvent.ACTION_UP:
                reset();
                //初始化 开始恢复动画
                endScraling();
                break;
            //当屏幕上已经有一个点被按住,此时再按下其他点时触发。
            case MotionEvent.ACTION_POINTER_DOWN:
                onSecondaryPointerUp(ev);
                this.mLastMotionY = ev.getY(ev.findPointerIndex(this.mActivePointerId));
                break;
            case MotionEvent.ACTION_POINTER_UP:
        }
        return super.onTouchEvent(ev);
    }

    //设置头部的大小
    public void setHeaderViewSize(int paramInt1, int paramInt2){
        Object localObject = this.mHeaderContainer.getLayoutParams();
        if (localObject == null){
            //如果没有的话,设置listview的宽高
            localObject = new AbsListView.LayoutParams(paramInt1, paramInt2);
            ((ViewGroup.LayoutParams)localObject).width = paramInt1;
            ((ViewGroup.LayoutParams)localObject).height = paramInt2;
            //这里代码有点乱
            this.mHeaderContainer.setLayoutParams((ViewGroup.LayoutParams)localObject);
            this.mHeadHeight = paramInt2;
        }
    }

    public void setOnScrollListener(
            AbsListView.OnScrollListener paramOnScrollListener
    ){
        this.mOnScrollListener = paramOnScrollListener;
    }

    public void setShadow(int paramInt){
        this.mShadow.setBackgroundResource(paramInt);
    }

    //启用一个动画来使HeaderView平滑的恢复到放大之前的状态。
    class ScalingRunnalable implements Runnable{
        long mDuration;  //持续时间
        boolean mIsFinished = true;  //是否结束
        float mScale;  //测量规模
        long mStartTime; //开始时间

        ScalingRunnalable(){}

        public void abortAnimation(){
            this.mIsFinished = true;
        }

        public boolean isFinished(){
            return this.mIsFinished;
        }
        @Override
        public void run() {
            float f2;
            ViewGroup.LayoutParams locallayoutParams;
            if ((!this.mIsFinished) && (this.mScale > 1.0D)){
                float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) this.mStartTime)
                        /this.mDuration;

                //每次执行run的时候mHeadContainer的高度改变了多少
                //偏移量-超出的值(速度快慢)
                f2 = this.mScale - (this.mScale - 1.0F)
                        * PullToZoomListView.sInterpolatar.getInterpolation(f1);
                //设计控件的布局
                locallayoutParams = PullToZoomListView.this.mHeaderContainer.getLayoutParams();
                //当超过高度的时候,开始变形
                if (f2 > 1.0F){

                    //设置图片的布局的 高度
                    locallayoutParams.height = PullToZoomListView.this.mHeadHeight;
                    locallayoutParams.height = (int)(f2* PullToZoomListView.this.mHeadHeight);

                    //设置变化
                    PullToZoomListView.this.mHeaderContainer.setLayoutParams(locallayoutParams);
                    PullToZoomListView.this.post(this);
                    return;
                }
                    this.mIsFinished = true;
            }
        }
        public void startAnimation(long paramLong){
            this.mStartTime = SystemClock.currentThreadTimeMillis();
            this.mDuration = paramLong;
            //mLastScale是根据垂直偏移计算出来的
            this.mScale = ((float) (PullToZoomListView.this.mHeaderContainer.getBottom())/PullToZoomListView.this.mHeadHeight);
            this.mIsFinished = false;
            //post调用ScalingRunnalable的run方法,而ScalingRunnalable run方法中再次调用了post,就这样不断的更新UI,直到达到一定的条件退出这个循环
            PullToZoomListView.this.post(this);
        }
    }

}

相关文章

网友评论

    本文标题:自定义View之PullToZoomListView

    本文链接:https://www.haomeiwen.com/subject/dyqorttx.html