文章目录

  • 1 前言
  • 2 方式
    • 2.1 layout()
    • 2.2 动画
      • 2.2.1 View 动画
      • 2.2.2 属性动画
    • 2.3 scollTo与scollBy
    • 2.4 offsetTopAndBottom/offsetLeftAndRight
    • 2.5 LayoutParams 改变布局参数
    • 2.6 Scroller
      • 2.6.1 Scroller 原理
    • 2.7 ViewDragHelper
  • 3 参考文章

1 前言

View 的滑动在 Android 中非常广泛,例如我们在自定义控件,实现一些效果时候,常常需要使用到 View 的滑动,View 滑动方式包含如下七种。

  • layout()
  • 动画(View 动画和属性动画)
  • scollTo/scollBy
  • offsetTopAndBottom/offsetLeftAndRight
  • LayoutParams 改变布局参数
  • Scroller
  • ViewDragHelper

2 方式

2.1 layout()

View 在绘制时候,会调用 onLayout() 方法设置 View 的显示位置,因此,我们完全可以使用 layout() 函数重新对 View 位置进行布局,来实现滑动的效果。接下来,我们来自定义一个跟随手指移动的 View。

//1.自定义 Viewpublic class CustomView extends View {    private int mLastX;    private int mLastY;    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);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        //获取 X坐标 Y坐标        int x = (int) event.getX();        int y = (int) event.getY();        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                mLastX = x;                mLastY = y;                break;            case MotionEvent.ACTION_MOVE:                //计算移动的距离                int offsetX = x - mLastX;                int offsetY = y - mLastY;                //调用layout方法来重新布局                layout(getLeft() + offsetX, getTop() + offsetY,                        getRight() + offsetX, getBottom() + offsetY);                break;            default:                break;        }        return true;    }}//2.在布局文件中使用<com.yoyiyi.blob.CustomView     android:id="@+id/cv"     android:layout_width="40dp"     android:layout_height="40dp"     android:background="#000000" />

上面代码非常简单,在 onTouchEvent 中我们获取到手指点击的坐标,然后计算出其移动距离,最后是由 layout() 重新进行布局。

2.2 动画

采用动画来进行移动,动画分为 View 动画和属性动画,最大区别是 View 动画只是对 View 的影像的变化,View 还是在原来位置,响应事件也在原来位置。

2.2.1 View 动画

//1.创建一个在 X 轴上 平移到坐标 200 动画TranslateAnimation translateAnimation = new TranslateAnimation(0, 200, 0, 0);translateAnimation.setDuration(1000);translateAnimation.setFillAfter(true);cv.setAnimation(translateAnimation);translateAnimation.start();

2.2.2 属性动画

属性动画就更加简单了,代码如下。

ObjectAnimator translation = ObjectAnimator.ofFloat(cv, "translationX", 0, 200).setDuration(1000).start();

2.3 scollTo与scollBy

scollTo(x, y) 表示移动到具体的坐标点,scollBy(dx, dy) 表示移动增量 dx、dy,scollBy 最终也是调用 scollTo。需要注意的是这两个方法都是移动 View 的内容,如果是 ViewGroup 则移动子 View。

linearLayout.scollTo(300,0)//需要注意,负数表示向正方向移动,这只是参考系不同而已 ((View) getParent()).scrollBy(-offsetX, -offsetY);

2.4 offsetTopAndBottom/offsetLeftAndRight

这两个方法和 layout() 效果差不多,所以 2.1 的例子完全可以替换成如下方式。

offsetTopAndBottom(offsetY);offsetLeftAndRight(offsetX);

2.5 LayoutParams 改变布局参数

LayoutParams 中保存了 View 的布局参数,我们可以通过获取到参数,再改变参数来实现滑动效果。

//1.使用父控件的 LayoutParams LinearLayout.LayoutParams params= (LinearLayout.LayoutParams)getLayoutParams();params.leftMargin = getLeft() + offsetX;params.topMargin = getTop() + offsetY;setLayoutParams(params)//2.使用 ViewGroup.MarginLayoutParamsViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();layoutParams.leftMargin = getLeft() + offsetX;layoutParams.topMargin = getTop() + offsetY;setLayoutParams(layoutParams);

2.6 Scroller

使用 scollTo 和 scollBy 方法,滑动是一瞬间完成,体验不好,这里就需要使用到 Scroller 来实现弹性滑动。我们自定义一个能实现滑动 View,如下所示。

//1.自定义 Viewpublic class CustomView extends View {    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);        mScroller = new Scroller(context);    }    public void smoothScrollTo(int dstX,int dstY){        int scrollX = getScrollX();        int deltaX = dstX - scrollX;        //6000 秒内滑向 destX        mScroller.startScroll(scrollX, 0, deltaX, 0, 6000);        invalidate();    }    @Override    public void computeScroll() {        super.computeScroll();        //computeScrollOffset() 根据时间的流逝,计算当前的 scrollX 和 scrollY 的值        if(mScroller.computeScrollOffset()){            ((View) getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());            //通过不断的重绘不断的调用computeScroll方法            invalidate();        }    }}//2.使用,向右滑动 300 cv.smoothScrollTo(-300,0);

2.6.1 Scroller 原理

public void startScroll(int startX, int startY, int dx, int dy, int duration) {        mMode = SCROLL_MODE;        mFinished = false;        mDuration = duration; //滑动的时间        mStartTime = AnimationUtils.currentAnimationTimeMillis();        mStartX = startX; //滑动的起点        mStartY = startY;         mFinalX = startX + dx; //要滑动的距离        mFinalY = startY + dy;        mDeltaX = dx;        mDeltaY = dy;        mDurationReciprocal = 1.0f / (float) mDuration;    }

从上面代码看出,当我们创建一个 Scroller,并使用 startScroll(),只是保存了我们传递过来的参数,Scroller 就没有做其他的事情,那么它是如何工作的,在 startScroll() 方法后面我们调用 invalidate(),众所周知,这个方法会导致 View 重新绘制,也就是调用 View 的 draw(),而在 draw() 方法又会调用 computeScroll(),这是一个空实现,需要我们重写,上面的代码,我们调用 computeScrollOffset() ,这个方法作用根据时间的流逝,计算当前的 scrollX 和 scrollY 的值,接着获取 Scroller 当前的 scrollX 和 scrollY,然后通过,scrollTo 方法进行移动,接着又调用 invalidate() 进行第二次绘制,总结如下。

  • 创建 Scroller,调用 startScroll() 保存参数
  • 调用 invalidate() 重绘,会调用 View 的 draw()
  • View 的 draw() 会调用 computeScroll()
  • computeScroll() 调用 computeScrollOffset() 作用根据时间的流逝,计算当前的 scrollX 和 scrollY 的值
  • 获取到当前的 scrollX 和 scrollY,使用 scrollTo() 进行移动
  • invalidate() 进行第二次重绘,又调用 View 的 draw() ,如此不断重复

2.7 ViewDragHelper

ViewDragHelper 这个类负责手势操作,它是官方写的一个专门为自定义ViewGroup处理拖拽的手势类,所以 2.1 随手势移动自定义控件可以用 ViewDragHelper 实现,这里我们实现在自定义控件里面子 View 可以自由拖拽。

//1.自定义子 Viewpublic class CustomView extends LinearLayout {    private ViewDragHelper mHelper;    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);        mHelper =  ViewDragHelper.create(this, new ViewDragHelper.Callback() {            @Override            public boolean tryCaptureView(@NonNull View child, int pointerId) {                //返回 true                return true;            }            @Override            public int clampViewPositionHorizontal(View child, int left, int dx) {                return left;            }            @Override            public int clampViewPositionVertical(View child, int top, int dy) {                return top;            }        });    }    @Override    public boolean onInterceptTouchEvent(MotionEvent event) {        //接管事件        return mHelper.shouldInterceptTouchEvent(event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        mHelper.processTouchEvent(event);        //返回 true 消费事件        return true;    }}//2.使用自定义 View<com.yoyiyi.blob.CustomView     android:id="@+id/cv"     android:layout_width="match_parent"     android:layout_height="match_parent">     <ImageView         android:layout_width="40dp"         android:layout_height="40dp"         android:background="#000000"/></com.yoyiyi.blob.CustomView>

3 参考文章

Android View滑动的七种方式总结
Android View体系(二)实现View滑动的六种方法

更多相关文章

  1. 调用Android短信软件进行短信群发
  2. Android(安卓)Cursor源码笔记(2)
  3. Android加载长图滑动显示
  4. Android(安卓)横竖屏操作
  5. Android(安卓)属性动画 实现view翻转 旋转 平移 拉伸 透明度 背
  6. 两个APP之间怎么调用---IT蓝豹
  7. Android中一个动画应用于两个View中不同步的问题
  8. Android关于疑难bug处理方法之一(搜索不到相同bug解决方案)
  9. Android(安卓)Local Service

随机推荐

  1. Android(安卓)学习 笔记_11. Service初步
  2. android 权限动态申请
  3. android显示段落文本及多行文本显示处理
  4. android类作用整理
  5. Android中关于Volley的使用(三)认识 CacheD
  6. Android模拟器网络连接问题解决[转]
  7. Android(安卓)studio 命令gradlew assemb
  8. 修改Android镜像文件 ramdisk.img、syste
  9. Android学习笔记(Android(安卓)Studio)属性
  10. android NDK JNI so文件的制作和使用