轮子怎么造---可以下拉缩放HeaderView的ListView:PullToZoomInListView

来源:互联网 发布:网络发展阶段 编辑:程序博客网 时间:2024/05/22 05:31

github地址如下:https://github.com/matrixxun/PullToZoomInListView



原理:

PullToZoomListView的实现原理主要是自定义ListView,在ListView中对监听手势,当MotionEvent为MotionEvent.ACTION_MOVE的时候,去判断向下滑动的偏移量,根据这个来改变headerView内容区域的高度,并且在手指放开的那一刻,停止缩放,启用一个动画来使HeaderView平滑的恢复到放大之前的状态。


先来理清Android坐标和偏移量

http://blog.csdn.net/lvxiangan/article/details/19971509

http://www.jb51.net/article/77546.htm这两篇文章都写的不错的。


//获取屏幕区域的宽高等尺寸获取DisplayMetrics metrics = new DisplayMetrics();getWindowManager().getDefaultDisplay().getMetrics(metrics);int widthPixels = metrics.widthPixels;int heightPixels = metrics.heightPixels;//应用程序App区域宽高等尺寸获取Rect rect = new Rect();getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);//获取状态栏高度Rect rect= new Rect();getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);int statusBarHeight = rectangle.top;//View布局区域宽高等尺寸获取Rect rect = new Rect();getWindow().findViewById(Window.ID_ANDROID_CONTENT).getDrawingRect(rect);



自定义listView的代码:

public class MyListView extends ListView implements AbsListView.OnScrollListener  {    private static final Interpolator sInterpolator = new Interpolator() {        public float getInterpolation(float paramAnonymousFloat) {            float f = paramAnonymousFloat - 1.0F;            return 1.0F + f * (f * (f * (f * f)));        }    };    int mActivePointerId = -1;    private FrameLayout mHeaderContainer;    private int mHeaderHeight;    private ImageView mHeaderImage;    float mLastMotionY = -1.0F;    float mLastScale = -1.0F;    float mMaxScale = -1.0F;    private AbsListView.OnScrollListener mOnScrollListener;    private ScalingRunnalable mScalingRunnalable;    private int mScreenHeight;    public MyListView(Context context) {        super(context);        init(context);    }    public MyListView(Context context, AttributeSet attrs) {        super(context, attrs);        init(context);    }    public MyListView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init(context);    }    @Override    public void onScroll(AbsListView paramAbsListView, int paramInt1,                         int paramInt2, int paramInt3) {      //  Log.d("mmm", "onScroll");        float f = mHeaderHeight - mHeaderContainer.getBottom();//        Log.d("mmm", "f|" + f);        if ((f > 0.0F) && (f < mHeaderHeight)) {//            Log.d("mmm", "1");            int i = (int) (0.65D * f);            mHeaderImage.scrollTo(0, -i);        } else if (mHeaderImage.getScrollY() != 0) {//            Log.d("mmm", "2");            mHeaderImage.scrollTo(0, 0);        }        if (mOnScrollListener != null) {            mOnScrollListener.onScroll(paramAbsListView, paramInt1,                    paramInt2, paramInt3);        }    }    public void onScrollStateChanged(AbsListView paramAbsListView, int paramInt) {        if (mOnScrollListener != null)            mOnScrollListener.onScrollStateChanged(paramAbsListView,                    paramInt);    }    private void init(Context context){        DisplayMetrics localDisplayMetrics = new DisplayMetrics();        ((Activity) context).getWindowManager().getDefaultDisplay()                .getMetrics(localDisplayMetrics);        mScreenHeight = localDisplayMetrics.heightPixels;        mHeaderContainer = new FrameLayout(context);        mHeaderImage = new ImageView(context);        int i = localDisplayMetrics.widthPixels;        setHeaderViewSize(i, (int) (5.0F * (i / 10.0F)));        mHeaderContainer.addView(mHeaderImage);        addHeaderView(mHeaderContainer);        mScalingRunnalable = new ScalingRunnalable();    }    public ImageView getHeaderView() {        return mHeaderImage;    }    /**     * 设置header的高度     * @param paramInt1     * @param paramInt2     */    public void setHeaderViewSize(int paramInt1, int paramInt2) {        Object localObject = mHeaderContainer.getLayoutParams();        if (localObject == null)            localObject = new AbsListView.LayoutParams(paramInt1, paramInt2);        ((ViewGroup.LayoutParams) localObject).width = paramInt1;        ((ViewGroup.LayoutParams) localObject).height = paramInt2;        mHeaderContainer                .setLayoutParams((ViewGroup.LayoutParams) localObject);        mHeaderHeight = paramInt2;    }    /**     * 停止滑动     */    private void endScraling() {        if (mHeaderContainer.getBottom() >= mHeaderHeight)            mScalingRunnalable.startAnimation(200L);    }    private void reset() {        mActivePointerId = -1;        mLastMotionY = -1.0F;        mMaxScale = -1.0F;        mLastScale = -1.0F;    }   
 public boolean onTouchEvent(MotionEvent ev) {        switch (ev.getAction() & MotionEvent.ACTION_MASK) {//每个手指第一次按下的时候,都被分配了一个独立的ID,一个ID在最终手指离开触摸屏之前或者手势失效之前,会一直有效。//PointerIndex和PointerId是什么。PointerId会被放入一个数组,PointerIndex就是Pointer在数组内的下标,//PointerIndex值从0到getPointerCount()-1,getPointerCount会得到The number of pointers of data contained in this event            case MotionEvent.ACTION_DOWN://用户开始触摸.                if (!mScalingRunnalable.mIsFinished) {                    mScalingRunnalable.abortAnimation();                }                mLastMotionY = ev.getY();//触摸点相对当前控件的y轴                mActivePointerId = ev.getPointerId(0);                mMaxScale = (mScreenHeight / mHeaderHeight);                mLastScale = (mHeaderContainer.getBottom() / mHeaderHeight);                break;            case MotionEvent.ACTION_MOVE://用户在移动(手指或者其他)                Log.d("mmm", "mActivePointerId" + mActivePointerId);                int j = ev.findPointerIndex(mActivePointerId);                if (j == -1) {                    Log.e("PullToZoomListView", "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");                } else {                    if (mLastMotionY == -1.0F) mLastMotionY = ev.getY(j);                    if (mHeaderContainer.getBottom() >= mHeaderHeight) {                        ViewGroup.LayoutParams localLayoutParams = mHeaderContainer.getLayoutParams();                        float f = ((ev.getY(j) - mLastMotionY + mHeaderContainer.getBottom()) / mHeaderHeight - mLastScale) / 2.0F + mLastScale;                        if ((mLastScale <= 1.0D) && (f < mLastScale)) {                            localLayoutParams.height = mHeaderHeight;                            mHeaderContainer.setLayoutParams(localLayoutParams);                            return super.onTouchEvent(ev);                        }                        mLastScale = Math.min(Math.max(f, 1.0F), mMaxScale);                        localLayoutParams.height = ((int) (mHeaderHeight * mLastScale));                        if (localLayoutParams.height < mScreenHeight)                            mHeaderContainer.setLayoutParams(localLayoutParams);                        mLastMotionY = ev.getY(j);                        return true;                    }                    mLastMotionY = ev.getY(j);                }                break;            case MotionEvent.ACTION_UP://用户抬起了手指                                reset();                endScraling();                break;        }        return super.onTouchEvent(ev);    }    class ScalingRunnalable implements Runnable {        long mDuration;        boolean mIsFinished = true;        float mScale;        long mStartTime;        ScalingRunnalable() {        }        public void abortAnimation() {            mIsFinished = true;        }        public boolean isFinished() {            return mIsFinished;        }        public void run() {            float f2;            ViewGroup.LayoutParams localLayoutParams;            if ((!mIsFinished) && (mScale > 1.0D)) {                float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) mStartTime) / (float) mDuration;                f2 = mScale - (mScale - 1.0F) * sInterpolator.getInterpolation(f1);                localLayoutParams = mHeaderContainer.getLayoutParams();                if (f2 > 1.0F) {                    Log.d("mmm", "f2>1.0");                    localLayoutParams.height = mHeaderHeight;                    localLayoutParams.height = ((int) (f2 * mHeaderHeight));                    mHeaderContainer.setLayoutParams(localLayoutParams);                    post(this);                    return;                }                mIsFinished = true;            }        }        /**         * 手指下拉停止释放后的动画         *         * @param paramLong         */        public void startAnimation(long paramLong) {            mStartTime = SystemClock.currentThreadTimeMillis();            mDuration = paramLong;            //缩小比例                        mScale = ((float) (mHeaderContainer.getBottom()) / mHeaderHeight);            mIsFinished = false;            post(this);        }    }}

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0627/1625.html这篇文章对
PullToZoomInListView有很好的分析


我觉得,难点在于:改变headerView的高度的时候,下面的listview的item也会跟着移动,我尝试用缩放的动画,属性动画也没有解决,缩放headerview的时候,它是独立的,不能跟listview一起走,源码作者是ScalingRunnalable在startAnimation中调用了PullToZoomListView.this.post(this);post调用ScalingRunnalable的run方法,而ScalingRunnalable run方法中再次调用了post,就这样不断的更新UI,直到达到一定的条件退出这个循环。

不断更新UI是通过重新设置headerView的宽高来实现

localLayoutParams.height = mHeaderHeight;localLayoutParams.height = ((int) (f2 * mHeaderHeight));mHeaderContainer.setLayoutParams(localLayoutParams);



0 0
原创粉丝点击