1 使用MarginLayoutParams来实现View跟随手指滑动的问题
如果搜索View滑动
百度搜出来的都是下面这段:
LinearLayout.LayoutParams layoutParams= (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
但是这样写运行后会发现
每次手指滑动一点。View却滑动很大的距离
运行下面代码后
//手指按下的纵坐标
private float startY;
//手指滑动的距离
private float dy;
ViewGroup.MarginLayoutParams layoutParams;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(layoutParams==null){
layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = ev.getRawY();
super.dispatchTouchEvent(ev);
return true;
case MotionEvent.ACTION_MOVE:
dy = ev.getRawY() - startY;
layoutParams.topMargin = (int) dy+getTop();
Log.i("TAG","dy="+dy);
Log.i("TAG","getTop="+getTop());
Log.i("TAG","topMargin="+layoutParams.topMargin);
setLayoutParams(layoutParams);
return true;
case MotionEvent.ACTION_UP:
requestLayout();
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
滑动前
11-13 01:35:36.254 4750-4750/? I/TAG: dy=16.748535
11-13 01:35:36.254 4750-4750/? I/TAG: getTop=0
11-13 01:35:36.254 4750-4750/? I/TAG: topMargin=16
滑动后
11-13 01:35:36.663 4750-4750/? I/TAG: dy=37.980194
11-13 01:35:36.663 4750-4750/? I/TAG: getTop=513
11-13 01:35:36.663 4750-4750/? I/TAG: topMargin=550
可以看到我只滑动了37像素但是topMargin却变成了550.
这是因为在滑动过程中,每次滑动都会多次调用 ACTION_MOVE里面的内容。
getTop的值时因为topMargin变而变
所以这一行代码
layoutParams.topMargin = (int) dy+getTop();
就相当于
layoutParams.topMargin = layoutParams.topMargin+(int) dy;
而每次滑动的时候,就相当于迭代累加layoutParams.topMargin的值。
比如你只滑动了40个像素。那么可能调用了10次ACTION_MOVE里面的内容。
那么layoutParams.topMargin实际上就会叠加10次。并不是只加40。
2 marginTop参数并不一定是View到ViewGroup的距离
/**
* The top margin in pixels of the child. Margin values should be positive.
* Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
* to this field.
*/
@ViewDebug.ExportedProperty(category = "layout")
public int topMargin;
源码中对marginTop的解释是:
* The top margin in pixels of the child. Margin values should be positive.
child顶部(到父布局)的像素距离。Margin值应该是正直
Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
当重新制定(Margin)值时,要调用setLayoutParams()方法(来保存);
源码的解释和官方API写的一样。并没有其他的解释。
layoutParams.topMargins的真实含义
例如
不设置margin
<com.example.test05_layoutparmas.MyTextView
android:id="@+id/textView"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#f00"/>
//手指滑动的距离
private float dy;
ViewGroup.MarginLayoutParams layoutParams;
//记录上一次滑动的距离
int lastDy;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(layoutParams==null){
layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = ev.getRawY();
super.dispatchTouchEvent(ev);
return true;
case MotionEvent.ACTION_MOVE:
Log.i("TAG","before topMargin="+layoutParams.topMargin);
}
return super.dispatchTouchEvent(ev);
}
打印结果
11-13 02:03:10.103 5432-5432/com.example.test05_layoutparmas I/TAG: before topMargin=0
设置margin
<com.example.test05_layoutparmas.MyTextView
android:id="@+id/textView"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginTop="20dp"
android:background="#f00"/>```
//手指按下的纵坐标
private float startY;
//手指滑动的距离
private float dy;
ViewGroup.MarginLayoutParams layoutParams;
//记录上一次滑动的距离
int lastDy;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(layoutParams==null){
layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = ev.getRawY();
super.dispatchTouchEvent(ev);
return true;
case MotionEvent.ACTION_MOVE:
Log.i("TAG","before topMargin="+layoutParams.topMargin);
return true;
}
return super.dispatchTouchEvent(ev);
}
打印结果
11-13 02:08:43.155 6108-6108/com.example.test05_layoutparmas I/TAG: before topMargin=53
可以看出layoutParams.topMargins并不是margin的值。
遗留问题: layoutParams.topMargins的真实距离 和影响此参数的因素
3 最终解决
//手指按下的纵坐标
private float startY;
//手指滑动的距离
private float dy;
ViewGroup.MarginLayoutParams layoutParams;
//记录上一次滑动的距离
int lastDy;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(layoutParams==null){
layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
}
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = ev.getRawY();
super.dispatchTouchEvent(ev);
return true;
case MotionEvent.ACTION_MOVE:
dy = (ev.getRawY() - startY)*2;
layoutParams.topMargin = (int) dy+lastDy;
setLayoutParams(layoutParams);
return true;
case MotionEvent.ACTION_UP:
lastDy = (int)dy+lastDy;
requestLayout();
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
最终通过上面的代码解决了我的滑动问题。但是还留下一个问题:
问题:dy并不是实际滑动的距离。每次还要X2。这样手指滑动的距离才是View滑动的距离。