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