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();        }    }

更多相关文章

  1. Android6.0 ViewGroup/View 事件分发机制详解
  2. android开发笔记之多媒体—播放音频(音乐)
  3. android连接远程数据库教程1
  4. Android监听输入法弹窗和关闭的实现方法
  5. Android(安卓)APK的反编译方法
  6. Android开发技术周报 Issue#56
  7. 【Android】由Looper引起的ThreadLocal相关分析
  8. Android进阶练习 - 高效显示Bitmap(管理Bitmap内存)
  9. Android(安卓)异步消息处理—让你深入理解 Looper、Handler、Mes

随机推荐

  1. Android中的绘图
  2. android 横屏显示
  3. Android Parcelable的使用
  4. android 学习 之 prefernce
  5. 将TabHost选项卡置于下方
  6. Android Studio ------Dynamic layout pr
  7. android小项目
  8. Android 圆角矩形
  9. android中颜色对应的值
  10. Button去掉自带的阴影效果