Android群英傳讀書(shū)筆記(第五章)

上一章

書(shū)中的示例代碼:github

1.Android的坐標(biāo)系是以左上角為頂點(diǎn),向右為x軸正方向,向下是y軸正方向。在觸控事件中通過(guò)getRawX()getRawY()獲取Android坐標(biāo)系中的坐標(biāo)。在View中通過(guò)getLocationOnScreen(intlocation[])獲取。

2.視圖坐標(biāo)系描述的是子視圖在父視圖中的位置關(guān)系,原點(diǎn)為父視圖的右上角,x、y軸方向與Android坐標(biāo)系一致。觸控事件中通過(guò)getX(),getY()獲取。還可以通過(guò)getTop(),getLeft(),getBottom(),getRight()來(lái)獲取到父視圖的距離。

3.MotionEvent常用事件常量:

MotionEvent.ACTION_DOWN//單點(diǎn)觸摸按下動(dòng)作
MotionEvent.ACTION_UP//單點(diǎn)觸摸離開(kāi)動(dòng)作
MotionEvent.ACTION_MOVE//觸摸點(diǎn)移動(dòng)動(dòng)作
MotionEvent.ACTION_CANCEL//觸摸動(dòng)作取消
MotionEvent.ACTION_OUTSIDE//觸摸動(dòng)作超出邊界
MotionEvent.ACTION_POINTER_DOWN//多點(diǎn)觸摸按下動(dòng)作
MotionEvent.ACTION_POINTER_UP//多點(diǎn)離開(kāi)動(dòng)作

4.實(shí)現(xiàn)滑動(dòng)的七種方法:

  • layout()方法:
private int lastX;
    private int lastY;
    private int offsetX;
    private int offsetY;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            lastX = x;
            lastY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            offsetX = x - lastX;
            offsetY = y - lastY;

            layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);

            lastX = x;
            lastY = y;
            break;
        }

        return true;
    }
  • offsetLeftAndRight()offsetTopAndBottom()方法:
//替換上面的layout方法即可
offsetLeftAndRight(offsetX);
offsetTopAndBottom(offsetY);
  • LayoutParams方法:
//替換上面的layout方法即可
ViewGroup.MarginLayoutParams layoutParams=(MarginLayoutParams) getLayoutParams();
            layoutParams.leftMargin=getLeft()+offsetX;
            layoutParams.topMargin=getTop()+offsetY;
            setLayoutParams(layoutParams);
  • scrollToscrollBy
    //scrollTo和scrollBy移動(dòng)的是view的內(nèi)容而不是view本身
    //如果在viewgroup中使用就是移動(dòng)所有子view。
    View view=(View) getParent();
    //scrollTo和scrollBy參考的坐標(biāo)系正好與視圖坐標(biāo)系相反,所以offset需為負(fù)
    view.scrollBy(-offsetX, -offsetY);
  • Scroller:
    使用Scroller主要有三個(gè)步驟:
    1.初始化Scroller對(duì)象,一般在view初始化的時(shí)候同時(shí)初始化scroller;
    2.重寫(xiě)viewcomputeScroll方法,computeScroll方法是不會(huì)自動(dòng)調(diào)用的,只能通過(guò)invalidate來(lái)間接調(diào)用,實(shí)現(xiàn)循環(huán)獲取scrollXscrollY的目的,當(dāng)移動(dòng)過(guò)程結(jié)束之后,Scroller.computeScrollOffset方法會(huì)返回false,從而中斷循環(huán);
    3.調(diào)用Scroller.startScroll方法,將起始位置、偏移量以及移動(dòng)時(shí)間(可選)作為參數(shù)傳遞給startScroll方法。
    這個(gè)例子中,要實(shí)現(xiàn)的是view跟著手指滑動(dòng) 松手后平滑移動(dòng)到原位置。
    先初始化Scroller
mScroller=new Scroller(getContext());

然后重寫(xiě)ViewcomputeScroll()方法

@Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            ((View) getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        }
    }

最后在onTouchEventMotionEvent.ACTION_UP時(shí)開(kāi)啟移動(dòng)

@Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) event.getX();
                lastY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                ((View) getParent()).scrollBy(-offsetX, -offsetY);
                break;
            case MotionEvent.ACTION_UP:
                // 手指離開(kāi)時(shí),執(zhí)行滑動(dòng)過(guò)程
                View viewGroup = ((View) getParent());
                mScroller.startScroll( viewGroup.getScrollX(), viewGroup.getScrollY(),
                        -viewGroup.getScrollX(), -viewGroup.getScrollY(),1000);
                invalidate();
                break;
        }
        return true;
    }

Scroller的實(shí)現(xiàn)原理就是不斷調(diào)用scrollTo或者scrollBy

  • 屬性動(dòng)畫(huà)(以后章節(jié)會(huì)詳細(xì)介紹)
  • ViewDragHelper
    ViewDragHelper基本可以實(shí)現(xiàn)各種不同滑動(dòng)需求,但使用稍微復(fù)雜。
public class DragViewGroup extends FrameLayout {

    private ViewDragHelper mViewDragHelper;
    private View mMenuView, mMainView;
    private int mWidth;

    public DragViewGroup(Context context) {
        super(context);
        initView();
    }

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

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

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mMenuView = getChildAt(0);
        mMainView = getChildAt(1);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = mMenuView.getMeasuredWidth();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //將觸摸事件傳遞給ViewDragHelper,此操作必不可少
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

    private void initView() {
        mViewDragHelper = ViewDragHelper.create(this, callback);
    }

    private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {

                // 何時(shí)開(kāi)始檢測(cè)觸摸事件
                @Override
                public boolean tryCaptureView(View child, int pointerId) {
                    //如果當(dāng)前觸摸的child是mMainView時(shí)開(kāi)始檢測(cè)
                    return mMainView == child;
                }

                // 觸摸到View后回調(diào)
                @Override
                public void onViewCaptured(View capturedChild, int activePointerId) {
                    super.onViewCaptured(capturedChild, activePointerId);
                }

                // 當(dāng)拖拽狀態(tài)改變,比如idle,dragging
                @Override
                public void onViewDragStateChanged(int state) {
                    super.onViewDragStateChanged(state);
                }

                // 當(dāng)位置改變的時(shí)候調(diào)用,常用與滑動(dòng)時(shí)更改scale等
                @Override
                public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
                    super.onViewPositionChanged(changedView, left, top, dx, dy);
                }

                // 處理垂直滑動(dòng)
                @Override
                public int clampViewPositionVertical(View child, int top, int dy) {
                    return 0;
                }

                // 處理水平滑動(dòng)
                @Override
                public int clampViewPositionHorizontal(View child, int left, int dx) {
                    return left;
                }

                // 拖動(dòng)結(jié)束后調(diào)用
                @Override
                public void onViewReleased(View releasedChild, float xvel, float yvel) {
                    super.onViewReleased(releasedChild, xvel, yvel);
                    //手指抬起后緩慢移動(dòng)到指定位置
                    if (mMainView.getLeft() < 500) {
                        //關(guān)閉菜單,相當(dāng)于Scroller的startScroll方法
                        mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
                        ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
                    } else {
                        //打開(kāi)菜單
                        mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);
                        ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
                    }
                }
            };

    @Override
    public void computeScroll() {
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }
}

下一章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容