下拉刷新控件

/**
 * 兼容 4.2 一下所有
 * 修复    4.1.2 BUG(箭头动画不动)
 * @author {author lsw}
 *
 */
public class PullToRefresh extends FrameLayout implements
        GestureDetector.OnGestureListener {
    public static final int STATE_CLOSE = 1;
    public static final int STATE_OPEN = 2;
    public static final int STATE_OPEN_MAX = 4;
    public static final int STATE_OPEN_MAX_RELEASE = 5;
    public static final int STATE_OPEN_RELEASE = 3;
    public static final int STATE_UPDATE = 6;
    public static final int STATE_UPDATE_SCROLL = 7;
    private  int MAXHEIGHT = 80;
    private final String TAG = "PullToRefresh";

    private ImageView mArrow;
    private String mDate;
    private GestureDetector mDetector;
    private Flinger mFlinger;
    private boolean mIsAutoScroller;
    private int mPading;
    private ProgressBar mProgressBar;
    private int mState;
    private TextView mTitle;

    private UpdateHandle mUpdateHandle;

    private RotateAnimation mFlipAnimation;
    private RotateAnimation mReverseFlipAnimation;

    private boolean mIsOpen = true;

    public PullToRefresh(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        DisplayMetrics dm =context.getResources().getDisplayMetrics();
        MAXHEIGHT =(int) (context.getResources().getDimension(R.dimen.updatebar_height) * (dm.densityDpi/160));
        addUpdateBar();
        init();
    }

    public PullToRefresh(Context context, AttributeSet attrs) {
        super(context, attrs);
        DisplayMetrics dm =context.getResources().getDisplayMetrics();
        MAXHEIGHT =(int) (context.getResources().getDimension(R.dimen.updatebar_height) * (dm.densityDpi/160));
        //Log.i("lgsh", "MAXHEIGHT"+MAXHEIGHT);
        addUpdateBar();
        init();
    }

    public PullToRefresh(Context context) {
        super(context);
        DisplayMetrics dm =context.getResources().getDisplayMetrics();
        MAXHEIGHT =(int) (context.getResources().getDimension(R.dimen.updatebar_height) * (dm.densityDpi/160));
        addUpdateBar();
        init();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d(TAG, "[dispatchTouchEvent]");

        boolean bool1 = this.mIsAutoScroller;
        GestureDetector localGestureDetector = this.mDetector;
        float lastY = 0;
        float newY = 0;
        
        
        localGestureDetector.onTouchEvent(ev);
        switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            lastY = ev.getY();
        
        case MotionEvent.ACTION_MOVE:
            int i1 = getChildAt(1).getTop();
            //Log.i("lgs", "getChildAt(1).getTop" + i1);
            if (i1 != 0) {
                updateView();
            }

            break;
        case MotionEvent.ACTION_UP:
            newY = ev.getY();
            if (this.mState == STATE_OPEN) {
                this.mState = STATE_OPEN_RELEASE;
            }
            if (this.mState == STATE_OPEN_MAX) {
                this.mState = STATE_OPEN_MAX_RELEASE;
            }
            Log.d(TAG, "[onScroll] ActionUP mState=" + mState);

            release();
            break;
        }
        if (mState != STATE_UPDATE) {
            bool1 = super.dispatchTouchEvent(ev);
            //Log.i("lgs", "dispatchTouchEvent"+bool1+mState);
        }

        int i1 = getChildAt(1).getTop();
        Log.d("lgs", "[dispatchTouchEvent] getTop()=" + i1);
        if (i1 != 0) {
            ev.setAction(3);
            super.dispatchTouchEvent(ev);
            updateView();
        }
        if(Math.abs(lastY -newY  ) < 10){
            return true;
        }
        return bool1;
    }

    private void init() {
        GestureDetector localGestureDetector = new GestureDetector(this);
        this.mDetector = localGestureDetector;
        Flinger localFlinger = new Flinger();
        this.mFlinger = localFlinger;
        this.mState = 1;
        setDrawingCacheEnabled(true);
        setClipChildren(true);
        this.mDetector.setIsLongpressEnabled(false);
    }

    private void updateView() {
        Log.d("lgs", String.format("updateView %d", this.mState));
        View localView1 = getChildAt(0);
        View localView2 = getChildAt(1);
        if (this.mDate == null)
            this.mDate = "";

        switch (this.mState) {
        case STATE_CLOSE:
            if (localView1.getVisibility() != View.INVISIBLE)
                localView1.setVisibility(View.INVISIBLE);
        case STATE_OPEN:
        case STATE_OPEN_RELEASE:
            Log.d(TAG, "updateView STATE_OPEN");
            // STATE_OPEN
            int m = localView2.getTop();
            int n = -this.mPading - m;
            //Log.i("lgs1", "localView2__m-n="+m+","+n+"mPading+"+mPading);
            localView2.offsetTopAndBottom(n);
            if (localView1.getVisibility() != 0)
                localView1.setVisibility(View.VISIBLE);
            int i1 = localView1.getTop();// 相对于父窗口的顶部大小
            int i2 = -MAXHEIGHT;
            int i3 = this.mPading;
            int i4 = i2 - i3 - i1;
            //Log.i("lgs1", "localView1__i1-4="+i1+","+i2+","+i3+","+i4);
            localView1.offsetTopAndBottom(i4);
            TextView localTextView1 = this.mTitle;
            String str1 = "下拉可以刷新";
            StringBuilder localStringBuilder1 = new StringBuilder(str1)
                    .append("\n");
            localStringBuilder1.append(this.mDate);
            localTextView1.setText(localStringBuilder1.toString());
            this.mProgressBar.setVisibility(View.INVISIBLE);
            this.mArrow.setVisibility(View.VISIBLE);
            if (!mIsOpen) {
                mIsOpen = true;
                this.mArrow.startAnimation(mReverseFlipAnimation);
//                mReverseFlipAnimation.start();

            }
            break;
        case STATE_OPEN_MAX_RELEASE:
        case STATE_OPEN_MAX:
            int i5 = localView2.getTop();
            int i6 = -this.mPading - i5;
            //Log.i("lgs1", "localView2__i5-6="+i5+","+i6);
            localView2.offsetTopAndBottom(i6);
            if (localView1.getVisibility() != View.VISIBLE)
                localView1.setVisibility(View.VISIBLE);
            int i7 = localView1.getTop();
            int i8 = -MAXHEIGHT;
            int i9 = this.mPading;
            int i10 = i8 - i9 - i7;
            //Log.i("lgs1", "localView1__i7-10="+i7+","+i8+","+i9+","+i10);
            localView1.offsetTopAndBottom(i10);
            TextView localTextView2 = this.mTitle;
            String str4 = "松开可以刷新";// release_update:松开可以刷新
            StringBuilder localStringBuilder2 = new StringBuilder(str4)
                    .append("\n");

            localStringBuilder2.append(this.mDate);
            localTextView2.setText(localStringBuilder2.toString());
            this.mProgressBar.setVisibility(View.INVISIBLE);
            this.mArrow.setVisibility(View.VISIBLE);
            if (mIsOpen) {
                mIsOpen = false;
                this.mArrow.startAnimation(mFlipAnimation);
//                mFlipAnimation.start();

            }
            break;
        case STATE_UPDATE:
            // STATE_UPDATE
            int i11 = localView2.getTop();
            int i12 = -this.mPading - i11;
            localView2.offsetTopAndBottom(i12);
            int i13 = localView1.getTop();
            if (this.mProgressBar.getVisibility() != View.VISIBLE)
                this.mProgressBar.setVisibility(View.VISIBLE);
            if (this.mArrow.getVisibility() != View.INVISIBLE)
                this.mArrow.setVisibility(View.INVISIBLE);
            TextView localTextView3 = this.mTitle;
            String str7 = "加载中...";// doing_update:加载中...
            StringBuilder localStringBuilder3 = new StringBuilder(str7)
                    .append("\n");

            localStringBuilder3.append(this.mDate);
            localTextView3.setText(localStringBuilder3.toString());
            int i14 = -MAXHEIGHT;
            int i15 = this.mPading;
            int i16 = i14 - i15 - i13;
            localView1.offsetTopAndBottom(i16);
            if (localView1.getVisibility() != 0)
                localView1.setVisibility(0);
            this.mProgressBar.setVisibility(View.VISIBLE);
            this.mArrow.setVisibility(View.GONE);
            mArrow.clearAnimation();
            break;
        }
        invalidate();
    }

    private void addUpdateBar() {
        Context localContext1 = getContext();
        mFlipAnimation = new RotateAnimation(0, -180,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        mFlipAnimation.setInterpolator(new LinearInterpolator());
        mFlipAnimation.setDuration(200);
        mFlipAnimation.setFillAfter(true);
        mReverseFlipAnimation = new RotateAnimation(-180, 0,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
        mReverseFlipAnimation.setDuration(200);
        mReverseFlipAnimation.setFillAfter(true);

        View localView = LayoutInflater.from(localContext1).inflate(
                R.layout.vw_update_bar, null);
        localView.setVisibility(4);
        addView(localView);

        this.mArrow = (ImageView) localView.findViewById(R.id.arrow_image);
        this.mProgressBar = (ProgressBar) localView.findViewById(R.id.refresh_progress);
        this.mProgressBar.setVisibility(View.INVISIBLE);
        this.mTitle = (TextView) findViewById(R.id.tv_title);
    }

    protected void onLayout(boolean paramBoolean, int paramInt1, int paramInt2,
            int paramInt3, int paramInt4) {
        View localView1 = getChildAt(0);
        int i = -MAXHEIGHT;
        int j = this.mPading;
        int k = i - j;
        int l = getMeasuredWidth();
        int i1 = -this.mPading;
        localView1.layout(0, k, l, i1);
        //Log.i("lgs9", "onLayout="+"   i=" +i+"   j="+j+"   k=" +k+"   l="+l+"   i1="+i1);

        View localView2 = getChildAt(1);
        int i2 = -this.mPading;
        int i3 = getMeasuredWidth();
        int i4 = getMeasuredHeight();
        int i5 = this.mPading;
        int i6 = i4 - i5;
        localView2.layout(0, i2, i3, i6);
        //Log.i("lgs9", "onLayout="+"   i2=" +i2+"   i3="+i3+"   i4=" +i4+"   i5="+i5+"   i6="+i6);
    }

    public void endUpdate(String paramString) {
        this.mDate = paramString;
        Log.d(TAG, "[endUpdate]  mPading=" + this.mPading);
        if (this.mPading != 0) {
            this.mState = STATE_CLOSE;
            scrollToClose();
        }
    }

    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
            float distanceY) {
        Log.d("lgs2", "[onScroll] paramFloat2=" + distanceY + " mPadding="
                + this.mPading);
        View childView;
        if(getChildCount()>1){
            childView = this.getChildAt(1);
            if(childView instanceof ListView){
                AdapterView localAdapterView = (AdapterView) getChildAt(1);
                int k = localAdapterView.getCount();
                if (k == 0) {
                    return false;
                }
                k = localAdapterView.getFirstVisiblePosition();// 获取第一个显示项目的position
                int t = localAdapterView.getChildAt(0).getTop();
                if (k != 0 || t != 0) {
                    return false;
                }
            }else if(childView instanceof ScrollView){
                ScrollView localAdapterView = (ScrollView) getChildAt(1);
                int k = localAdapterView.getChildCount();
                if (k == 0) {
                    return false;
                }
                k = localAdapterView.getScrollY();
                int t = localAdapterView.getChildAt(0).getTop();
                if (k != 0 || t != 0) {
                    return false;
                }
            }
            Log.d("lgs2", "[onScroll] ACTION_MOVE mState=" + mState);
            this.mPading = (int) (this.mPading + distanceY / 1.2);

            if (this.mPading > 0)
                this.mPading = 0;

            if (Math.abs(this.mPading) <= MAXHEIGHT) {
                this.mState = STATE_OPEN;

            } else {
                this.mState = STATE_OPEN_MAX;

            }
            updateView();
            
            
        }
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
            float velocityY) {
        return false;
    }

    private int getScrollRange() {
        int scrollRange = 0;
        if (getChildCount() > 0) {
            View child = getChildAt(0);
            scrollRange = Math
                    .max(0,
                            child.getHeight()
                                    - (getHeight()
                                            - this.getBottomPaddingOffset() - this
                                                .getTopPaddingOffset()));
        }
        return scrollRange;
    }

    private boolean release() {
        int tempStatus = STATE_OPEN_MAX_RELEASE;
        int i = this.mPading;
        if (i >= 0) {
            return true;
        }
        int j = this.mState;
        switch (j) {
        case STATE_OPEN_RELEASE:
            int k = Math.abs(this.mPading);
            int i1 = MAXHEIGHT;
            if (k < i1) {
                tempStatus = STATE_OPEN_MAX_RELEASE;
                this.mState = tempStatus;
            }
            scrollToClose();
            break;
        case STATE_OPEN_MAX_RELEASE:

            this.mState = tempStatus;
            scrollToUpdate();
            break;
        }
        return false;
    }

    private void scrollToClose() {
        Log.d(TAG, "[scrollToClose]");
        Flinger localFlinger = this.mFlinger;
        int i = -this.mPading;
        localFlinger.startUsingDistance(i, 500);
    }

    private void scrollToUpdate() {
        Log.d(TAG, "[scrollToUpdate]");
        Flinger localFlinger = this.mFlinger;

        int k = -this.mPading - MAXHEIGHT;
        localFlinger.startUsingDistance(k, 500);
    }

    class Flinger implements Runnable {
        private int mLastFlingX;
        private Scroller mScroller;

        public Flinger() {
            Context localContext = PullToRefresh.this.getContext();
            Scroller localScroller = new Scroller(localContext);
            this.mScroller = localScroller;
        }

        private void startCommon() {
            PullToRefresh.this.removeCallbacks(this);
        }

        public void run() {
            Log.d(TAG, "[Flinger.run] mPading=" + mPading);
            boolean bool1 = Math.abs(mPading) != MAXHEIGHT;
            Scroller localScroller = this.mScroller;
            boolean bool2 = localScroller.computeScrollOffset();
            int i = localScroller.getCurrX();
            int j = this.mLastFlingX - i;
            PullToRefresh localPullDownView = PullToRefresh.this;

            localPullDownView.move(j, bool1);
            PullToRefresh.this.updateView();
            if (bool2) {
                this.mLastFlingX = i;
                PullToRefresh.this.post(this);
            } else {
                PullToRefresh.this.mIsAutoScroller = bool1;
                PullToRefresh.this.removeCallbacks(this);
            }
        }

        public void startUsingDistance(int paramInt1, int paramInt2) {
            Log.d(TAG, String.format(
                    "[Flinger.startUsingDistance]paramInt1=%d,paramInt2=%d",
                    paramInt1, paramInt2));
            int i = 0;
            if (paramInt1 == 0)
                --paramInt1;
            startCommon();
            this.mLastFlingX = i;
            Scroller localScroller = this.mScroller;

            localScroller.startScroll(i, 0, -paramInt1, 0, paramInt2);
            PullToRefresh.this.mIsAutoScroller = true;
            PullToRefresh.this.post(this);
        }
    }

    /**
     * 释放的时候使用*
     * @param f
     * @param bool1
     */
    public void move(float f, boolean bool1) {
        Log.d(TAG, "[move]mIsAutoScroller=" + mIsAutoScroller);
        if (this.mState != STATE_CLOSE) {
            if (!bool1) {
                // 刷新
                Log.d(TAG, "[move]refresh");
                if (mState == STATE_OPEN_MAX_RELEASE) {
                    this.mState = STATE_UPDATE;
                    if (mUpdateHandle != null) {
                        mUpdateHandle.onUpdate();
                    }
                }
            }
            if (this.mState == STATE_OPEN_MAX_RELEASE
                    || this.mState == STATE_OPEN_RELEASE) {
                this.mPading += f;

            }
        } else {
            Log.d(TAG, "[move]up top");
            if (mIsAutoScroller) {
                this.mPading += f;
            }
        }

    }

    public abstract interface UpdateHandle {
        public abstract void onUpdate();
    }

    public void setUpdateDate(String paramString) {
        this.mDate = paramString;
    }

    public void setUpdateHandle(UpdateHandle paramUpdateHandle) {
        this.mUpdateHandle = paramUpdateHandle;
    }

    public void updateWithoutOffset() {
        this.mState = STATE_UPDATE_SCROLL;
        invalidate();
    }

}

下拉刷新控件

上一篇:【实战虚拟化】安全设计之一基本架构


下一篇:导航引擎结构分析之二