Android(安卓)View体系(三)--实现 View 的滑动七种方式
文章目录
- 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滑动的六种方法
更多相关文章
- 调用Android短信软件进行短信群发
- Android(安卓)Cursor源码笔记(2)
- Android加载长图滑动显示
- Android(安卓)横竖屏操作
- Android(安卓)属性动画 实现view翻转 旋转 平移 拉伸 透明度 背
- 两个APP之间怎么调用---IT蓝豹
- Android中一个动画应用于两个View中不同步的问题
- Android关于疑难bug处理方法之一(搜索不到相同bug解决方案)
- Android(安卓)Local Service