1. 背景:快端午节了,运营想搞个活动,根据用户交互次数的多少,让龙舟来划动。
需求图如下:
在此界面,每隔15s就要发一起请求,从而来更新龙舟的位置。
涉及到的技术点就是:view坐标的移动。
2.实现:
先说走过的弯路:
- getLeft拿到的坐标为0的问题:
这个很常见,因为获取时机不对,view 还没布置好,这个时候去拿坐标是拿不到的。
具体怎么获取,可以参考:此处
我是在onWindowFocusChanged中拿到了5条龙舟各自的onLeft值,而不再是0了。 - 控件的移动问题:
最初开始用的是view.layout(left,top,right,bottom)。
假定的龙舟只能横向向左移动,只需要计算left值减去要移动的距离,就为龙舟最终要移动的位置。
//此处是测量view的left值
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Timber.d("onWindowFocusChanged");
measureBoat();
isMeasured = true;
}
private void measureBoat() {
beginLeftX1 = ivDragon1.getLeft();
beginLeftX2 = ivDragon2.getLeft();
beginLeftX3 = ivDragon3.getLeft();
beginLeftX4 = ivDragon4.getLeft();
beginLeftX5 = ivDragon5.getLeft();
Timber.d("beginLeftX %d, %d,%d,%d,%d", beginLeftX1,beginLeftX2,beginLeftX3, beginLeftX4,beginLeftX5);
}
//此处是计算view要移动的值:boat_1是移动的比率,以此类推各个参数的意义
private void moveBoat(double boat_1, double boat_2, double boat_3, double boat_4,
double boat_5) {
if (!isMeasured) {
Timber.d("还没有测量");
return;
}
Timber.d("left %d,%d,%d,%d,%d",beginLeftX1 -(int) (CONSTANT_DISATANCE * boat_1),
beginLeftX2-(int) (CONSTANT_DISATANCE * boat_2),
beginLeftX3-(int) (CONSTANT_DISATANCE * boat_3),
beginLeftX4-(int) (CONSTANT_DISATANCE * boat_4),
beginLeftX5-(int) (CONSTANT_DISATANCE * boat_5));
ivDragon1.layout(beginLeftX1-(int) (CONSTANT_DISATANCE * boat_1), ivDragon1.getTop(), ivDragon1.getRight(), ivDragon1.getBottom());
ivDragon2.layout(beginLeftX2 -(int) (CONSTANT_DISATANCE * boat_2), ivDragon2.getTop(), ivDragon2.getRight(), ivDragon2.getBottom());
ivDragon3.layout( beginLeftX3 -(int) (CONSTANT_DISATANCE * boat_3), ivDragon3.getTop(), ivDragon3.getRight(), ivDragon3.getBottom());
ivDragon4.layout(beginLeftX4 -(int) (CONSTANT_DISATANCE * boat_4), ivDragon4.getTop(), ivDragon4.getRight(), ivDragon4.getBottom());
ivDragon5.layout(beginLeftX5 -(int) (CONSTANT_DISATANCE * boat_5), ivDragon5.getTop(), ivDragon5.getRight(), ivDragon5.getBottom());
}
然后奇怪的事件来了,计算移动的数据明明是对的,也是变动的,但是龙舟居然一动不动。
单独做了一个测试:写一个按钮,来调用同样的方法,看看是否能移动。
测试代码如下:
@OnClick(R.id.btn_move)
public void onViewClicked() {
ivDragon1.layout(1005, ivDragon1.getTop(), ivDragon1.getRight(), ivDragon1.getBottom());
Timber.d("beginLeftX5 %d", beginLeftX5);
ivDragon2.layout(1274, ivDragon2.getTop(), ivDragon2.getRight(), ivDragon2.getBottom());
ivDragon3.layout(1331, ivDragon3.getTop(), ivDragon3.getRight(), ivDragon3.getBottom());
ivDragon4.layout(1293, ivDragon4.getTop(), ivDragon4.getRight(), ivDragon4.getBottom());
ivDragon5.layout(1235, ivDragon5.getTop(), ivDragon5.getRight(), ivDragon5.getBottom());
}
当我点击屏幕上这个move button的时候,龙舟是移动的,但是定时调用moveBoat的时候,船只又回到原处去了,而且还是一动不动。怀疑是layout这里出了问题。
网上搜索了下,参考此篇文章
照葫芦画瓢来改进下:
ivDragon1.postDelayed(new Runnable() {
@Override
public void run() {
ivDragon1.layout(beginLeftX1 -(int) (CONSTANT_DISATANCE * boat_1), ivDragon1.getTop(), ivDragon1.getRight(), ivDragon1.getBottom());
ivDragon2.layout(beginLeftX2 -(int) (CONSTANT_DISATANCE * boat_2), ivDragon2.getTop(), ivDragon2.getRight(), ivDragon2.getBottom());
ivDragon3.layout( beginLeftX3-(int) (CONSTANT_DISATANCE * boat_3), ivDragon3.getTop(), ivDragon3.getRight(), ivDragon3.getBottom());
ivDragon4.layout(beginLeftX4 -(int) (CONSTANT_DISATANCE * boat_4), ivDragon4.getTop(), ivDragon4.getRight(), ivDragon4.getBottom());
ivDragon5.layout(beginLeftX5-(int) (CONSTANT_DISATANCE * boat_5), ivDragon5.getTop(), ivDragon5.getRight(), ivDragon5.getBottom());
}
},100);
结果是:龙舟可以移动了,但是效果特别不好,因为页面要定时刷新数据,而每次刷新一次数据,都可以看到控件从原点移动到目标位置,就算控件已经在最新位置了,用户还是可以感知view从原点再移动到最新位置。这个结果肯定是不敢给产品看的,一定通不过,只能再换个方案移动view了。
换一种方案:
采用layputparams来移动控件。
做完后发现,此方案才是简单有效的。
修改如下:
//确定好layoutparams:
private void initLayoutParams() {
params1 = new RelativeLayout.LayoutParams(530, 164);
params1.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,RelativeLayout.TRUE);
params1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
params1.bottomMargin = 460;
params2 = new RelativeLayout.LayoutParams(530, 164);
params2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,RelativeLayout.TRUE);
params2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
params2.bottomMargin = 348;
params3 = new RelativeLayout.LayoutParams(530, 164);
params3.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,RelativeLayout.TRUE);
params3.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
params3.bottomMargin = 236;
params4 = new RelativeLayout.LayoutParams(530, 164);
params4.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,RelativeLayout.TRUE);
params4.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
params4.bottomMargin = 112;
params5 = new RelativeLayout.LayoutParams(530, 164);
params5.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM,RelativeLayout.TRUE);
params5.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
params5.bottomMargin = 12;
}
//更新layotparams:主要是rigthMargin的改变,船的位置就改变了
private void moveBoat(double boat_1, double boat_2, double boat_3, double boat_4,
double boat_5) {
params1.rightMargin =(int) (CONSTANT_DISATANCE * boat_1);
ivDragon1.setLayoutParams(params1);
params2.rightMargin =(int) (CONSTANT_DISATANCE * boat_2);
ivDragon2.setLayoutParams(params2);
params3.rightMargin =(int) (CONSTANT_DISATANCE * boat_3);
ivDragon3.setLayoutParams(params3);
params4.rightMargin =(int) (CONSTANT_DISATANCE * boat_4);
ivDragon4.setLayoutParams(params4);
params5.rightMargin =(int) (CONSTANT_DISATANCE * boat_5);
ivDragon5.setLayoutParams(params5);
}
就算是在做定时刷新,不会看到控件总是从原点再次运行到目标位置了。