View的滑动就是获取View的偏移量,修改View的坐标,实现View的滑动有很多种方法。
1.layout()方法
View绘制的时候会通过调用onLayout()方法设置控件的位置,我们可以通过修改设置位置的属性来控制View的坐标。
private int lastX;
private int lastY;
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;
}
2.offsetLeftAndRight()与offsetTopAndBottom()
也是根据偏移距离设置上下的坐标位置。
@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;
offsetLeftAndRight(offsetX);
offsetTopAndBottom(offsetY);
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
3.setLayoutParams()动态修改布局参数
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams();
lp.topMargin = getTop() + offsetY;
lp.leftMargin = getLeft() + offsetX;
setLayoutParams(lp);
4.动画位移
可以用View动画效果移动,在res目录创建anim文件夹,创建translate.xml。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000" android:fillAfter="true">
<translate android:fromXDelta="0" android:toXDelta="300" />
</set>
customView.setAnimation(AnimationUtils.loadAnimation(this,R.anim.translate));
这里的View动画并不能改变View的位置参数,View的点击事件触摸都会在原来的位置才能触发。在android3.0出现了属性动画,就可以改变View的位置参数。
ObjectAnimator.ofFloat(customView,"translationX",0,300).setDuration(1000).start();
5.scrollTo()和scrollBy()
scrollTo(x,y)表示移动到一个具体的坐标点,而scrollBy(dx,dy)则表示移动的增量为dx,dy。其中scrollBy()最终也是要调用scrollTo()的。
scrollBy和scrollTo的源码:
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
scrollTo、scrollBy移动的是View的内容,如果在ViewGroup中使用,则是移动其所有的子View。我们将ACTION_MOVE中的代码替换成如下代码:
((View)getParent()).scrollBy(-offsetX,-offsetY);
这里若要实现CustomView随手指移动的效果,就需要将偏移量设置为负值。
这里移动的相当于画布,屏幕相当于覆盖在一个画布上,当我们想移动View时,随着我们手指向右移动,但是要是参照画布的话,画布应该是往左移动,所以参考坐标不一样,需要设置为负值。
6.Scroller
我们在用scrollTo/scrollBy方法进行滑动时,这个过程是瞬间完成的,所以用户体验不大好。这里我们可以使用Scroller来实现有过度效果的滑动,这个过程不是瞬间完成的,而是在一定的时间间隔内完成的。Scroller本身是不能实现View的滑动,它需要与View的computeScroll()方法配合才能实现弹性滑动的效果。在这里我们实现CustomView平滑的移动。
scroller = new Scroller(context);
接下来重写computeScroll()方法,系统会在绘制View的时候在draw()方法中调用该方法。在这个方法中,我们调用父类的scrollTo()方法并通过Scroller来不断获取当前的滚动值,每滑动一小段距离我们就调用invalidate()方法不断的进行重绘,重绘就会调用computeScroll()方法,这样我们通过不断的移动一个小的距离并连贯起来就实现了平滑移动的效果。
public void smoothScrollTo(int destX, int destY){
int startX = getScrollX();
int dx = destX - startX;
scroller.startScroll(startX,0,dx,0, 2000);
invalidate();
}
customView.smoothScrollTo(-400,0);