Android(安卓)View 滑动基础知识
1. 什么是View
View 是Android中所有控件的基类,ViewGroup继承了View,其内部包含一组View(或ViewGroup)
2. View的位置参数
先来了解一下 top, bottom, left, right,具体如下图所示。Android的坐标系是以屏幕的左上角为原点,x 轴向右为正,y 轴向下为正。而这四个参数是以其 父容器 (注意是父容器,不一定是整个屏幕哦)为参照物的相对坐标。
View 的位置参数与父容器的关系.png由此,可得出View的宽,高:
width = right - left;
height = bottom - top;
如果想获取这四个参数的值,可以通过view.getTop(), view.getBottom(), view.getLeft(), view.getRight()来获得。这四个参数分别对应View源码里的mTop, mBottom, mLeft, mRight.
从 Android 3.0 开始,View 又增加了几个参数:x, y, translationX, translationY. 其中x,y 是View相对于父容器左上角坐标,translationX 是View左上角相对于父容器在x轴的偏移量,translationY 是View左上角相对于父容器在y轴的偏移量。
这四个新增的参数也可通过相应的getXXX来获取, translationX, translationY的默认值是0.0(float类型);其中,
x = left + translationX;
y = top + translationY;
View 自身在做平移过程中,只有新增的这四个参数x, y, translationX, translationY会发生改变,而top, bottom, left, right这四个值则不会改变。
3. MotionEvent
手指点击屏幕滑动后抬起,看似简单,实际上包括一系列事件:ACTION_DOWN->ACTION_MOVE->...->ACTION_MOVE->ACTION_UP.
提到滑动事件,一般都会用到 MotionEvent 。通过这个对象我们可以获取事件发生时的 x,y 坐标,其中:
getX()/getY() 是相对于当前 View 左上角的 x, y 坐标;
getRawX()/getRawY()是相对于手机屏幕左上角的 x, y 坐标。
4. 直接滑动
- scrollTo/scrollBy
// 使用scrollBy()方法直接滑动View里的内容(滑动瞬间完成)private void scrollByTest() { Log.e(TAG, "before left = " + textView.getLeft() + ",right = " + textView.getRight() + ",top = " + textView.getTop() + ",bottom = " + textView.getBottom() + ",x=" + textView.getX() + ",y = " + textView.getY() + ",translationX=" + textView.getTranslationX() + ",translationY = " + textView.getTranslationY()); // mScrollX 表示(View左边缘-view内容左边缘) // 从左向右滑动时mScrollX是负的,反之是正的;从上向下滑动时,mScrollY是负的,反之是正的,此处是View的内容向左滑动100px textView.scrollBy(100, 0); Log.e(TAG, "after left = " + textView.getLeft() + ",right = " + textView.getRight() + ",top = " + textView.getTop() + ",bottom = " + textView.getBottom() + ",x=" + textView.getX() + ",y = " + textView.getY() + ",translationX=" + textView.getTranslationX() + ",translationY = " + textView.getTranslationY()); }
测试的结果如下图,可以发现,scrollBy(实际上还是调用的scrollTo)只是移动了View的内容,View本身位置并没有改变。
scrollBy(100, 0)结果.png
- 改变布局参数
比如,宽100多屏,高50dp的TextView位于左上角,父容器是LinearLayout,现在,想把TextView向右移动100px。
private void scrollByUpdateLayoutParams() { Log.e(TAG, "before left = " + textView.getLeft() + ",right = " + textView.getRight() + ",top = " + textView.getTop() + ",bottom = " + textView.getBottom() + ",x=" + textView.getX() + ",y = " + textView.getY() + ",translationX=" + textView.getTranslationX() + ",translationY = " + textView.getTranslationY()); ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) textView.getLayoutParams(); Log.e(TAG,"before leftMargin = " + params.leftMargin); // 向右移动100,就相当于左边距增加100 params.leftMargin += 100; textView.setLayoutParams(params); Log.e(TAG,"after leftMargin = " + params.leftMargin); Log.e(TAG, "after left = " + textView.getLeft() + ",right = " + textView.getRight() + ",top = " + textView.getTop() + ",bottom = " + textView.getBottom() + ",x=" + textView.getX() + ",y = " + textView.getY() + ",translationX=" + textView.getTranslationX() + ",translationY = " + textView.getTranslationY()); }
测试结果如下图, TextView向右移动了100px:
scrollByUpdateLayoutParams.png
此处测试需要注意的是,如果父容器是RelativeLayout,TextView不要设置layout_centerInParent = “true”,android:layout_centerHorizontal = "true等属性,不然的话,看不到滑动效果。
直接滑动很简单也很生硬,嘎嘣一下就滑过去了,用户体验着实不好。怎么办呢?办法还是有的,下面看一下怎样实现弹性滑动。
5. 弹性滑动
- 使用Scroller
Scroller类是一个实现弹性滑动的工具。那我们怎么使用它?它是怎么实现弹性滑动的呢?
使用Scroller主要有三步:
首先,new 一个Scroller对象;
其次,调用其startScroll方法,然后再调用invalidate();
最后,重写View的computeScroll()方法(注意这个方法是View的方法,不是Scroller的)
public class CustomView extends LinearLayout{ private Scroller mScroller; public CustomView(Context context) { this(context, null); } public CustomView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //首先,new 一个Scroller对象 mScroller = new Scroller(getContext()); } public void startSmoothScroll(int targetX, int targetY) { int diffX = targetX - getScrollX(); int diffY = targetY - getScrollY(); // 其次,调用其startScroll方法,然后再调用invalidate(); mScroller.startScroll(getScrollX(), getScrollY(), diffX, diffY, 3000); invalidate(); } @Override public void computeScroll() { // 重写computeScroll()方法,根据mScroller.computeScrollOffset()来判断是否继续滚动(true继续滚),然后调用scrollTo实现滚动,并再次调用invalidate(),以继续进行下一轮的滚动 if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); invalidate(); } }}
布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
点击TextView,将其向屏幕右下方平滑滚动,目标位置为(-400,-600)
textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { customView.startSmoothScroll(-400,-600); } });
为什么这样使用Scoller,就能实现弹性滚动呢?原理大致如下:
Scoller实现弹性滑动的原理.png
- 属性动画
属性动画实现弹性动画简直不要太简单,比如想实现两秒内TextView向右平滑滚动100px,一行代码就可以搞定:
ObjectAnimator.ofFloat(textView,"translationX",0,100).setDuration(2000).start();
这里只介绍思路,具体的属性动画的使用可以自己去学习哈。
- 使用延时策略
使用延迟策略也可以实现弹性滑动,其思想是通过循环发送一系列延迟消息,按百分比每一次滚动一点距离从而达到平滑滚动的效果。可以使用Handler或View的postDelayed方法,或者线程的sleep方法来实现。
下面以Handler为例,控制TextView通过10次滑动实现向右平滑滚动200px。
private static final int MSG_AUTO_SCROLL = 0; private static final int INTERVAL = 200; private static final int DISTANCE = -200; /** * 1O次滚动 */ private static final int MAX_COUNT = 10; private int mCount = 0; private Handler mHandler = new AutoScrollHandler(); private class AutoScrollHandler extends Handler { @Override public void handleMessage(Message msg) { if (msg.what == MSG_AUTO_SCROLL) { doScroll(); } } } private void sendAutoMessage() { mHandler.removeMessages(MSG_AUTO_SCROLL); mHandler.sendEmptyMessageDelayed(MSG_AUTO_SCROLL, INTERVAL); } private void doScroll() { Log.e(TAG, "doScroll run,mCount = " + mCount); mCount++; if (mCount <= MAX_COUNT) { float fraction = (float) (mCount * 1.0 / MAX_COUNT); // 注意scrollBy是View的内容滚动,所以想要实现textview本身的滚动,就要使用textview的父容器来调用scrollBy方法,这样父容器的内容就是textview,就能实现textview本身的滚动啦 customView.scrollBy((int) (DISTANCE * fraction), 0); sendAutoMessage(); } }
更多相关文章
- Android6.0 ViewGroup/View 事件分发机制详解
- android开发笔记之多媒体—播放音频(音乐)
- android连接远程数据库教程1
- Android监听输入法弹窗和关闭的实现方法
- Android(安卓)APK的反编译方法
- Android开发技术周报 Issue#56
- 【Android】由Looper引起的ThreadLocal相关分析
- Android进阶练习 - 高效显示Bitmap(管理Bitmap内存)
- Android(安卓)异步消息处理—让你深入理解 Looper、Handler、Mes