-
layout()方法
View进行绘制的时候会调用onLayout()方法来设置现实的位置,因此我们同样也可以通过修改View的left、top、right、bottom这4个属性来控制View的坐标。首选我们需要自定义一个View,在onTouchEvent()方法中获取触摸点的坐标,接下来我们在ACTION_MOVE事件中计算偏移量,再调用layout()方法重新放置控件的位置即可,代码如下所示:
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()方法来重新放置控件的位置
layout(getLeft() + offsetX, getTop() + offsetY,
getRight() + offsetX, getBottom() + offsetY);
break;
}
return true;
}
-
offsetLeftAndRight()与offsetTopAndBottom()
这种方法和上面的layout()方法差不多。我们将ACTION_MOVE中的代码替换成如下代码即可得到我们想要的效果。
case MotionEvent.ACTION_MOVE:
//计算移动的距离
int offsetX = x - lastX;
int offsetY = y - lastY;
offsetLeftAndRight(offsetX);
offsetTopAndBottom(offsetY);
break;
-
LayoutParams(改变布局参数)
LayoutParams主要保存了一个View的布局参数,因此我们可以通过LayoutParams来改变View的布局参数从而达到改变View位置的效果。同样,将ACTION_MOVE中的代码替换成如下代码:
case MotionEvent.ACTION_MOVE:
//计算移动的距离
int offsetX = x - lastX;
int offsetY = y - lastY;
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
break;
因为父控件是LinearLayout,所以我们使用了LinearLayout.LayoutParams。如果父控件是RelativeLayout,则要使用RelativeLayout.LayoutParams。除了使用布局的LayoutParams外,我们还可以使用ViewGroup.MarginLayoutParams来实现:
case MotionEvent.ACTION_MOVE:
//计算移动的距离
int offsetX = x - lastX;
int offsetY = y - lastY;
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
break;
-
动画
可以采用动画来移动,在res目录新建anim文件夹并创建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>
接下来在Java代码中调用:
mCustomView.setAnimation(AnimationUtils.loadAnimation(this, R.anim.anim_translate));
运行程序,控件会向右平移300个像素,并停留在当前位置。
需要注意的是View动画并不能改变View的位置参数。如果对控件设置一个点击事件,此时对控件执行如上操作,点击控件停留的位置并不会触发点击事件,但我们点击控件初始位置却触发了点击事件。对于系统来说这个控件并没有改变原有的位置,所以就出现上述问题。在Android3.0的时候推出的属性动画可以解决上述问题,它不仅执行了动画,还改变了控件的位置参数。代码如下:
ObjectAnimator.ofFloat(mCustomView, "translationX", 0, 300).setDuration(1000).start();
-
scrollTo与scrollBy
scrollTo(x,y)表示移动到一个具体的坐标点,而scrollBy(dx,dy)则表示移动的增量为dx,dy。其中,scrollBy最终也是要调用scrollTo的。scrollTo与scrollBy移动的是View的内容,如果在ViewGroup中使用,则是移动其所有的子View。我们将ACTION_MOVE中的代码替换成如下代码:
case MotionEvent.ACTION_MOVE:
//计算移动的距离
int offsetX = x - lastX;
int offsetY = y - lastY;
((View)getParent()).scrollBy(-offsetX, -offsetY);
break;
-
Scroller
我们在用scrollTo/scrollBy方法进行滑动时,这个过程是瞬间完成的,所以用户体验不太好,这里我们可以使用Scroller来实现过渡效果。Scroller本身是不能滑动的,它需要与View的computeScroll()方法配合才能实现弹性滑动效果。这里我们演示一下把控件向右移动400个像素,代码如下:
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private Scroller mScroller;
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
((View)getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
public void smoothScrollTo(int destX, int destY) {
int scrollX = getScrollX();
int delta = destX - scrollX;
mScroller.startScroll(scrollX, 0, delta, 0, 2000);
invalidate();
}
}
最后我们在activity中调用smoothScrollTo()方法:
mCustomView.smoothScrollTo(-400, 0);
网友评论