在熟悉了View的基础知识之后,下面看一下 View 的滑动。
在 Android 设备中,滑动是必不可少的部分;因为手机屏幕是有限的,如果想呈现更多的内容,需要通过滑动来实现显示和隐藏一些内容,因此掌握基础的滑动是很有必要的。(部分内容摘自Android开发艺术探索)
我们实现滑动效果有三种常用的方式:第一种是通过 View 自身的 scrollTo/scrollBy 方法实现滑动效果;第二种是通过动画 ( 如: 补间动画) 来实现动画的效果;第三种是通过改变 View 的 LayoutParams 使 View 重新布局来实现滑动。
1. 使用 scrollTo/scrollBy 方法
为了实现 View 的滑动,View 提供了两个专门的方法,要想了解这两个方法,必不可少的要看它的源码:
12407 /**
12408 * Set the scrolled position of your view. This will cause a call to
12409 * {@link #onScrollChanged(int, int, int, int)} and the view will be
12410 * invalidated.
12411 * @param x the x position to scroll to
12412 * @param y the y position to scroll to
12413 */
12414 public void scrollTo(int x, int y) {
12415 if (mScrollX != x || mScrollY != y) {
12416 int oldX = mScrollX;
12417 int oldY = mScrollY;
12418 mScrollX = x;
12419 mScrollY = y;
12420 invalidateParentCaches();
12421 onScrollChanged(mScrollX, mScrollY, oldX, oldY);
12422 if (!awakenScrollBars()) {
12423 postInvalidateOnAnimation();
12424 }
12425 }
12426 }
12427
12428 /**
12429 * Move the scrolled position of your view. This will cause a call to
12430 * {@link #onScrollChanged(int, int, int, int)} and the view will be
12431 * invalidated.
12432 * @param x the amount of pixels to scroll by horizontally
12433 * @param y the amount of pixels to scroll by vertically
12434 */
12435 public void scrollBy(int x, int y) {
12436 scrollTo(mScrollX + x, mScrollY + y);
12437 }
不难看出 scrollBy 方法内执行了 scrollTo 方法,来看一下这两个方法。
scrollTo/scrollBy 方法只能改变 View 内容的位置而不能改变 View 在布局中的位置。
先看两个方法内需要传的两个参数:
第一个参数 x :当前位置横向移动的距离,为正值向左运动,为负值向右运动。
第二个参数 y :当前位置纵向移动的距离,为正值向上运动,为负值向下运动。
两个方法中的 mScrollX 和 mScrollY 又是什么呢?
mScrollX :水平滚动视图内容的偏移量,以像素为单位,为正值向左运动,为负值向右运动。
mScrollY :竖直滚动视图内容的偏移量,以像素为单位,为正值向上运动,为负值向下运动。
那么在滑动过程中,mScrollX 和 mScrollY 这两个值是如何变化的呢?
mScrollX :总是等于 View 左边缘和 View 内容左边缘在水平方向的距离。
mScrollY :总是等于 View 上边缘和 View 内容上边缘在竖直方向的距离。
拿 View 初始坐标为 (0,0) 来说,从而更好的理解 mScrollX 和 mScrollY 值的变化;
如果 View 由左向右移动 100 个像素:View 左边缘初始值为 0,移动 100 像素后,View 左边缘值变为 100;mScrollX 为 -100;
如果 View 由上向下移动 100 个像素:View 上边缘初始值为 0,移动 100 像素后,View 上边缘值变为 100;mScrollY 为 -100;
具体看下图:
理解了这其中的参数后,那么 scrollTo 和 scrollBy 方法,有什么区别呢?
scrollTo :让 View 相对于初始的位置滚动某段距离;
scrollBy :让 View 相对于当前的位置滚动某段距离;
这么说不容易明白,下面是写了一个 Demo 的效果图,可以更好的理解这两个方法,代码就不给出了:
mScrollX 和 mScrollY滑动就介绍到这里。
2. 使用动画
下面介绍另一种能够让 View 进行移动的方法:使用动画。
相信动画都不陌生:逐帧动画、补间动画和属性动画。
如果还不了解可以先看下:补间动画的使用、逐帧动画与属性动画的使用。
下面通过补间动画来实现一下 View 的移动,先看下效果图:
上面动画有些卡顿,实际是没有卡顿的。来看下是如何实现的,首先定义了动画:
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="300"
android:toYDelta="300" />
<!-- 位移 -->
<!--
fromXDelta:动画起始位置的X坐标
toXDelta:动画起结束位置的X坐标
fromYDelta:动画起始位置的Y坐标
toYDelta:动画结束位置的Y坐标
-->
然后使用动画就可以了:
public class RJPActivity extends Activity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.item_rjp);
textView = findViewById(R.id.textView);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Animation animation = AnimationUtils.loadAnimation(RJPActivity.this,
R.anim.translate);
//保证动画运动之后停留在此处
animation.setFillAfter(true);
view.startAnimation(animation);
}
});
}
}
实现之后,要注意几个问题,我们为了保证动画在运动之后 View 停留在此处,需要将动画的 FillAfter 属性设置为 true ;
补间动画只是对 View 影像操作,View 的实际位置并没有改变,这样就会导致这个 View 的点击事件在移动后失效;所以 Android 为我们提供了另外一个动画:属性动画;属性动画可以解决上面的问题。
属性动画的位移就不再给出示例了,想了解的可以自己试一试。
3. 改变布局参数
这个就很好理解了,比如我想将一个 Button 向右平移 100 px,那么只要将这个 Button 的 LayoutParams 的 marginLeft 参数值增加 100 px 即可。
还有另外一种方法,在 Button 左侧放一个空的 View ,将此 View 的宽度设置为 0 ,当我们向右移动 Button 时,只需要重新设置 View 的宽度即可。先看一下效果图:
实现也很简单,只需要在相应的地方加入下面代码即可:
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) button.getLayoutParams();
params.leftMargin += 100;
button.requestLayout();
//或 button.setLayoutParams(params);
4. View 跟手滑动
View 跟随手滑动,先看效果图:
下面来看一下是如何达到效果的,下面是 Activity 的核心代码:
public class RJPActivity extends Activity implements View.OnTouchListener {
private Button button;
private int lastX, lastY;//停下来的坐标
private int moveX, moveY;//手指移动的像素
private int getX, getY;//最终控件距离父布局左边距离和右边距离
private LinearLayout.LayoutParams layoutParams;//获取到父布局
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.item_rjp);
button = findViewById(R.id.button);
button.setOnTouchListener(this);
layoutParams = (LinearLayout.LayoutParams) button.getLayoutParams();
}
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//手指按下坐标
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
//手指移动的像素
moveX = (int) event.getRawX() - lastX;
moveY = (int) event.getRawY() - lastY;
//最终控件距离父布局左边距离和右边距离
getX = view.getLeft() + moveX;
getY = view.getTop() + moveY;
//设置据左边/上边的距离
layoutParams.leftMargin = getX;
layoutParams.topMargin = getY;
view.setLayoutParams(layoutParams);
//停下来的坐标
lastX = (int) event.getRawX();
lastY = (int) event.getRawY();
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
//更新视图
view.invalidate();
return true;
}
}
布局文件很简单,里面只有一个 Button 就不再给出了。上面注释写的很清楚,不再分析步骤了。
下面来简单的分析一下上面的代码中的一些方法 :
getRawX/getRawY :获取手指当前的坐标;
getLeft/getTop :视图相对于父布局左/上坐标;
这里不要与 getX/getY 方法搞混了,getX/getY 方法是获取的视图的坐标。
到这里对 View 的滑动讲解就结束了。