1.Android坐标系

在Android中,将屏幕左上角的顶点作为Android坐标系的原点,从这个点向右是X轴正方向,向下是Y轴正方向。
系统提供了getLocatinoOnScreen(int location[]);这样的方法来获取Android坐标系中点的位置。

2.视图坐标系

视图坐标系描述子视图在父视图中的位置关系,视图坐标系的原点是父视图的左上角。通过getX()getY()获得的是视图坐标系中的坐标。

3.触控事件MotionEvent

MotionEvent中封装着一些常用的事件常量:

//单点触摸按下动作public static final int ACTION_DOWN = 0;//单点触摸离开动作public static final int ACTION_UP = 1;//触摸点移动动作public static final int ACTION_MOVE = 2;//触摸动作取消public static final int ACTION_CANCEL = 3;//触摸动作超出边界public static final int ACTION_OUTSIDE = 4;//多点触摸按下动作public static final int ACTION_POINTER_DOWN = 5;//多点离开动作public static final int ACTION_POINTER_UP = 6;
View提供的获取坐标的方法

getTop():获取到的是View自身的顶边到其父布局顶边的距离
getLeft():获取到的是View自身的左边到其父布局左边的距离
getRight():获取到的是View自身的右边到期父布局左边的距离
getBottom():获取到的是View自身的底边到其父布局顶边的距离

MotionEvent提供的方法

getX():获取点击事件距离控件左边的距离,即视图坐标
getY():获取点击事件距离控件顶边的距离,即视图坐标
getRawX():获取点击事件距离整个屏幕左边的距离,即绝对坐标
getRawY():获取点击事件距离整个屏幕顶边的距离,即绝对坐标
可以参考下图:

4.实现滑动的7种方法

1.使用layout方法

用View中的layout(int left,int top,int right,int bottom)方法在每次触摸移动后加上相应的偏移量就能实现滑动:

  @Override    public boolean onTouchEvent(MotionEvent event) {        int x = (int) event.getX();        int y = (int) event.getY();        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                lastX = x;                lastY = y;                break;            case MotionEvent.ACTION_MOVE:                int offsetX = x - lastX;                int offsetY = y - lastY;                layout(getLeft() + offsetX, getTop() + offsetY,                        getRight() + offsetX, getBottom() + offsetY);                break;            case MotionEvent.ACTION_UP:                break;        }        return true;    }

或者使用getRawX()getRawY()方法来获取坐标,不过要记得在ACTION_MOVE逻辑的最后为lastXlastY重新赋值。

@Override    public boolean onTouchEvent(MotionEvent event) {        int rawX = (int) event.getRawX();        int rawY = (int) event.getRawY();        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                lastX = rawX;                lastY = rawY;                break;            case MotionEvent.ACTION_MOVE:                int offsetX = rawX - lastX;                int offsetY = rawY - lastY;                layout(getLeft() + offsetX, getTop() + offsetY,                        getRight() + offsetX, getBottom() + offsetY);                lastX = rawX;                lastY = rawY;                break;            case MotionEvent.ACTION_UP:                break;        }        return true;    }
2.offsetLeftAndRight()offsetTopAndBottom()

这个方法相当于系统提供的左右、上下移动的API的封装,计算出偏移量后,只需要使用如下的代码完成View的重新布局:

//对left和right进行偏移offsetLeftAndRight(offsetX);//对top和bottom进行偏移offsetTopAndBottom(offsetY);
3.使用LayoutParams

LayoutParams保存了一个View的布局参数,因此可以在程序中通过改变LayoutParams来动态地修改一个布局的位置参数。从而达到改变View位置的效果。使用getLayoutParams()来获取一个View的LayoutParams然后通过setLayoutParams()来改变View的LayoutParams,代码如下所示:

LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)getLayoutParams();layoutParams.leftMargin = getLeft() + offsetX;layoutParams.topMargin = getTop() + offsetY;setLayoutParams(layoutParams);
4.使用scrollToscrollBy

scrollTo(x,y)表示移动到一个具体的具体的坐标点(x,y),scrollBy(dx,dy)表示移动的偏移量为dx、dy;
使用scrollByscrollTo时需要通过父布局来进行移动,因为这两个方法移动的是View的内容或者ViewGroup的子view。因此通过父View调用该方法移动的才是子View,还有一点是此时移动的方向正好和offset的方向相反,需要将其取反。代码如下:

((View)getParent()).scrollBy(-offsetX,-offsetY);
5.Scroller

scrollBy()scrollTo()方法实现的移动都是瞬间完成的,而Scroller类可以实现平滑移动的效果。使用Scroller都要重写computeScroll()方法,系统在绘制View时会在draw()方法中调用它,这个方法实际上就是使用scrollTo()的方法,再结合Scroller对象帮助获取当前的滚动值,来实现滚动效果。下面是computeScroll()的代码:

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

computeScrollOffset()方法判断是否完成了整个滑动过程,过程结束后该方法会返回false,getCurrX()和getCurrY()来获得当前的滑动坐标。通过invalidate()方法重绘来循环调用computeScroll()方法。
使用ScrollerstartScroll()方法来开启滑动过程,Scroller类提供了两个重载的方法:

startScroll(int startX,int startY,int dx,int dy,int duration);startScroll(int startX,int startY,int dx,int dy);

下面的代码实现了View跟随手指滑动,当手指离开屏幕时,View自动回到原位置的效果:

int lastX;int lastY;View parent = ((View)getParent());@Overridepublic boolean onTouchEvent(MotionEvent event){    int x = (int)event.getX();    int y = (int)event.getY();    switch(event.getAction){        case MotionEvent.ACTION_DOWN:            lastX = x;            lastY = y;            break;        case MotionEvent.ACTION_MOVE:            int offsetX = x - lastX;            int offsetY = y - lastY;            .scrollBy(-offsetX,-offsetY);            break;        case MotionEvent.ACTION_UP:            mScroller.startScroll(                parent.getScrollX(),                parent.getScrollY(),                -parent.getScrollX(),                -parent.getScrollY());            invalidate();            break;    }}
6.属性动画

使用属性动画也能完成对View的移动

7.ViewDragHelper

通过ViewDragHelper可以实现各种不同的滑动、拖放需求。通过实现类似QQ侧滑栏的布局来了解一下ViewDragHelper
首先初始化ViewDragHelper:

mViewDragHelper = ViewDragHelper.create(this,mCallback);

第一个参数是要监听的View,通常是一个ViewGroup,即ParentView,第二个参数是一个Callback回调,这个回调是整个ViewDragHelper的逻辑核心。
然后还要重写事件拦截方法,将事件传递给ViewDragHelper来处理:

@Overridepublic boolean onInterceptTouchEvent(MotionEvent event){    return mViewDragHelper.shouldInterceptTouchEvent(event);}@Overridepublic boolean onTouchEvent(MotoinEvent event){    mViewDragHelper.processTouchEvent(event);    return true;}

处理computeScroll()

@Overridepublic void computeScroll(){    if(mViewDragHelper.continueSetling(true)){        ViewCompat.postInvalidateOnAnimation(this);    }}

处理callback回调

private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback(){    @Override    public boolean tryCaptureView(View child,int pointerId){        return mMainView == child;    }}

通过这个方法我们可以指定创建ViewDragHelper时,参数parentView中哪一个子view可以被移动,在这个实例中自定义了一个ViewGroup,里面有两个子view——mMenuView和mMainView,当指定了上述代码时,只有mMainView是可以被拖动的。

然后处理具体的滑动方法——clampViewPositionVertical()clampViewPositionHorizontal(),分别对应垂直和水平滑动。必须重写这两个方法实现滑动。它们的默认返回值是0,即不发生滑动,只重写其中一个,就会实现对应方向上的滑动。代码如下所示:

@Overridepublic int clampViewPositionVertical(View child,int top,int dy){    return top;}@Overridepublic int clampViewPositionHorizontal(View child,int left,int dx){    return left;}

参数top和left分别代表在对应方向上的child移动的距离,dy、dx则表示相对于前一次的增量。通常情况下只需要返回top和left即可,但当需要更加精确地计算padding等属性时,就需要针对left和top进行一些处理,并返回合适的值。
下面是实现水平滑动的代码:

private ViewDragHelpe.Callback mCallback = new ViewDragHelper.Callback(){    @Override    public boolean tryCaptureView(View child,int pointerId){        return mMainView == child;    }    @Override    public int clampViewPositionVertical(View child,int top,int dy){        return 0;    }    @Override    public int clampViewPositionHorizontal(View child,int left,int dx){        return left;    }}

下面实现手指离开屏幕后View返回原来位置的效果:
在ViewDragHelper.Callback 中重写onViewReleased方法

@Overridepublic void onViewReleased(View releasedChild,float xvel,float yvel){    super,onViewReleased(releasedChild,xvel,yvel);    if(mMainView.getLeft() < mWidth / 3){        mViewDragHelper.smoothSlideViewTo(mMainView,0,0);        ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);    }else{        mViewDragHelper.smoothSlideViewTo(mMainView,300,0);        ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);    }}

上述代码让MainView移动后左边距小于其三分之一时回到原位置,否则就滑动到300的位置。
下面是自定义DragViewGroup的完整代码:

package com.chaoyang805.chapter5scrolldemo.view;import android.content.Context;import android.support.v4.view.ViewCompat;import android.support.v4.widget.ViewDragHelper;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.widget.FrameLayout;/** * Created by chaoyang805 on 2015/12/16. */public class DragViewGroup extends FrameLayout {    private ViewDragHelper mHelper;    private View mMenuView;    private View mMainView;    private int mWidth;    public DragViewGroup(Context context) {        this(context, null);    }    public DragViewGroup(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public DragViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        mHelper = ViewDragHelper.create(this,mCallback);    }    @Override    protected void onFinishInflate() {        super.onFinishInflate();        mMenuView = getChildAt(0);        mMainView = getChildAt(1);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        return mHelper.shouldInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        mHelper.processTouchEvent(event);        return true;    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        mWidth = mMenuView.getMeasuredWidth();    }    @Override    public void computeScroll() {        if (mHelper.continueSettling(true)) {            ViewCompat.postInvalidateOnAnimation(this);        }    }    private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {        @Override        public boolean tryCaptureView(View child, int pointerId) {            return mMainView == child;        }        @Override        public int clampViewPositionHorizontal(View child, int left, int dx) {            if (left < 0 && dx < 0) {                return 0;            }            return left;        }        @Override        public int clampViewPositionVertical(View child, int top, int dy) {            return 0;        }        @Override        public void onViewReleased(View releasedChild, float xvel, float yvel) {            super.onViewReleased(releasedChild, xvel, yvel);            if (mMainView.getLeft() < mWidth / 3) {                mHelper.smoothSlideViewTo(mMainView, 0, 0);                ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);            } else {                mHelper.smoothSlideViewTo(mMainView,300,0);                ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);            }        }    };}

ViewDragHelper.Callback中其他事件介绍:

//这个事件在用户触摸到View后回调onViewCaptured(View capturedChild,int activePointerId);//这个事件在拖拽状态改变时的回调,比如idle、dragging等onViewDragStateChanged(int state);//这个事件在位置改变时回调,常用于滑动时更改scale进行缩放等效果onViewPositionChanged(View changedView,int left,int top,int dx,int dy);

更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Python list sort方法的具体使用
  3. python list.sort()根据多个关键字排序的方法实现
  4. Android中View的坐标常用方法
  5. AirPods怎么连接Android设备 AirPods与安卓设备连接方法
  6. Android下基于XML的Graphics shape使用方法
  7. Android(安卓)App开发基础篇—四大组件之Service
  8. Android事件总线EventBus的用法详解
  9. Android项目源码混淆问题解决方法

随机推荐

  1. android 禁止屏幕转向
  2. Android多线程下载远程图片
  3. android防被杀
  4. 如何删除已安装的Android软件
  5. MoSync:跨多平台android,j2me, Symbian ,
  6. android使用AlarmManager实现应用每天定
  7. Android(安卓): 如何得到Activities栈顶
  8. Android多进程app中Application回调onCre
  9. 【iOS-cocos2d-X 游戏开发之三】Mac下配
  10. Android(安卓)4.1果冻豆新特性详解