参考文章转自:https://blog.csdn.net/shakespeare001/article/details/51657795
文章说了5种方式的滚动方法,说的都没啥毛病。但是OnTouchEvent方法没有说清楚,我想了一阵子之后明白其中的道理
@Override
public boolean onTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_MOVE:
int offsetX = x - mLastX;
int offsetY = y - mLastY;
//调整layout的四个坐标
layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
int left=getLeft();
Log.v("xhw","left:"+left+" x:"+x +" mLastX:"+mLastX+ " offsetX:"+offsetX);
break;
}
return true;
}
我们view的样式如图,当我们点击这个view的时候,会触发OnTouchEvent方法。而这个坐标
int x = (int) ev.getX();
int y = (int) ev.getY();
是指我们蓝色view中的坐标,(0,0)是蓝色view的左上角的位置。当点击view的时候出发如下ACTION_DOWN
case MotionEvent.ACTION_DOWN:
mLastX = x;
mLastY = y;
这个x和y是蓝色view上面的坐标,而不是屏幕上的坐标。
当出发滑动事件的时候:
方法1、layout
int offsetX = x - mLastX;
int offsetY = y - mLastY;
//调整layout的四个坐标
layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
这里是关键了啊,当我们手指滑动得时候,会在蓝色得view上滑动,那么就会产生一个滑动得距离。我们通过计算滑动距离之后,通过layout方法设置我们得位置,并且给这个位置添加移动的偏移量。这样我们的蓝色view就跟着手指移动了,手指点击的位置又回到了我们mLastX以及mLastY的位置。形象点来说 我们的视图view跟上来了!
需要注意的是我们layout方法改变的是这个view的getLeft、getRight等属性。
方法2、offsetLeftAndRight以及 offsetTopAndBottom
int offsetX = x - mLastX;
int offsetY = y - mLastY;
//调整layout的四个坐标
//layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
//使用简写形式
offsetLeftAndRight(offsetX);
offsetTopAndBottom(offsetY);
都是改变的view的left、right、top、bottom的属性的。我们第二种移动方法原理跟这个是一样的。
方法3、margin值
case MotionEvent.ACTION_MOVE:
int offsetX = x - mLastX;
int offsetY = y - mLastY;
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams ) getLayoutParams();
lp.leftMargin = getLeft() + offsetX;
lp.topMargin = getTop() + offsetY;
setLayoutParams(lp);
break;
设置view的距离也可以通过margin去影响
方法4、scrollBy
case MotionEvent.ACTION_MOVE:
//int offsetX = x - mLastX;
//int offsetY = y - mLastY;
//此时,计算坐标是相反的
int offsetX = mLastX - x;
int offsetY = mLastY - y;
//让View所在的ViewGroup进行移动
((View)getParent()).scrollBy(offsetX,offsetY);
break;
这个是通过父类容器的scrollBy方法来移动我们的试图,这个不会改变view本身的属性,而是通过滑动视窗的方式
方法5、Scroller
这种方式好像没啥用,Scroller是为了计算用的 滑动最终还是通过scrollBy或者scrollTo方法
文章把这几种方式都介绍完了,但是偏偏忘记了另一种方式setTranslateX以及setTranslateY的方法,我们属性动画都是通过改变translateX这种方式。
我们view的坐标X=left+translateX。我们上面的动画已经使用了left的方式,那么还可以通过translateX方式
方法6:setTranslateX
public class DragView5 extends View {
private int mLastX;
private int mLastY;
private int cumulativeX;
private int cumulativeY;
public DragView5(Context context) {
super(context);
init();
}
public DragView5(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DragView5(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
setBackgroundColor(Color.BLUE);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_MOVE:
int offsetX = x - mLastX;
int offsetY = y - mLastY;
cumulativeX+=offsetX;
cumulativeY+=offsetY;
setTranslationX(cumulativeX);
setTranslationY(cumulativeY);
int left=getLeft();
float translateX=getTranslationX();
Log.v("xhw","left:"+left+" translateX:"+translateX);
break;
}
return true;
}
}
我们参数值每次设置都会把之前的距离覆盖掉,所以我通过cumulativeX以及cumulativeY去累计我们移动的距离