Android群英传读书笔记(第五章)

上一章

书中的示例代码:github

1.Android的坐标系是以左上角为顶点,向右为x轴正方向,向下是y轴正方向。在触控事件中通过getRawX()getRawY()获取Android坐标系中的坐标。在View中通过getLocationOnScreen(intlocation[])获取。

2.视图坐标系描述的是子视图在父视图中的位置关系,原点为父视图的右上角,x、y轴方向与Android坐标系一致。触控事件中通过getX(),getY()获取。还可以通过getTop(),getLeft(),getBottom(),getRight()来获取到父视图的距离。

3.MotionEvent常用事件常量:

MotionEvent.ACTION_DOWN//单点触摸按下动作
MotionEvent.ACTION_UP//单点触摸离开动作
MotionEvent.ACTION_MOVE//触摸点移动动作
MotionEvent.ACTION_CANCEL//触摸动作取消
MotionEvent.ACTION_OUTSIDE//触摸动作超出边界
MotionEvent.ACTION_POINTER_DOWN//多点触摸按下动作
MotionEvent.ACTION_POINTER_UP//多点离开动作

4.实现滑动的七种方法:

  • 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移动的是view的内容而不是view本身
    //如果在viewgroup中使用就是移动所有子view。
    View view=(View) getParent();
    //scrollTo和scrollBy参考的坐标系正好与视图坐标系相反,所以offset需为负
    view.scrollBy(-offsetX, -offsetY);
  • Scroller:
    使用Scroller主要有三个步骤:
    1.初始化Scroller对象,一般在view初始化的时候同时初始化scroller
    2.重写viewcomputeScroll方法,computeScroll方法是不会自动调用的,只能通过invalidate来间接调用,实现循环获取scrollXscrollY的目的,当移动过程结束之后,Scroller.computeScrollOffset方法会返回false,从而中断循环;
    3.调用Scroller.startScroll方法,将起始位置、偏移量以及移动时间(可选)作为参数传递给startScroll方法。
    这个例子中,要实现的是view跟着手指滑动 松手后平滑移动到原位置。
    先初始化Scroller
mScroller=new Scroller(getContext());

然后重写ViewcomputeScroll()方法

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

最后在onTouchEventMotionEvent.ACTION_UP时开启移动

@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:
                // 手指离开时,执行滑动过程
                View viewGroup = ((View) getParent());
                mScroller.startScroll( viewGroup.getScrollX(), viewGroup.getScrollY(),
                        -viewGroup.getScrollX(), -viewGroup.getScrollY(),1000);
                invalidate();
                break;
        }
        return true;
    }

Scroller的实现原理就是不断调用scrollTo或者scrollBy

  • 属性动画(以后章节会详细介绍)
  • ViewDragHelper
    ViewDragHelper基本可以实现各种不同滑动需求,但使用稍微复杂。
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() {

                // 何时开始检测触摸事件
                @Override
                public boolean tryCaptureView(View child, int pointerId) {
                    //如果当前触摸的child是mMainView时开始检测
                    return mMainView == child;
                }

                // 触摸到View后回调
                @Override
                public void onViewCaptured(View capturedChild, int activePointerId) {
                    super.onViewCaptured(capturedChild, activePointerId);
                }

                // 当拖拽状态改变,比如idle,dragging
                @Override
                public void onViewDragStateChanged(int state) {
                    super.onViewDragStateChanged(state);
                }

                // 当位置改变的时候调用,常用与滑动时更改scale等
                @Override
                public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
                    super.onViewPositionChanged(changedView, left, top, dx, dy);
                }

                // 处理垂直滑动
                @Override
                public int clampViewPositionVertical(View child, int top, int dy) {
                    return 0;
                }

                // 处理水平滑动
                @Override
                public int clampViewPositionHorizontal(View child, int left, int dx) {
                    return left;
                }

                // 拖动结束后调用
                @Override
                public void onViewReleased(View releasedChild, float xvel, float yvel) {
                    super.onViewReleased(releasedChild, xvel, yvel);
                    //手指抬起后缓慢移动到指定位置
                    if (mMainView.getLeft() < 500) {
                        //关闭菜单,相当于Scroller的startScroll方法
                        mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
                        ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
                    } else {
                        //打开菜单
                        mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);
                        ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
                    }
                }
            };

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

下一章

你可能感兴趣的:(Android群英传读书笔记(第五章))