[关闭]
@TryLoveCatch 2022-05-17T02:30:37.000000Z 字数 5650 阅读 1656

Android知识体系之滑动的几种方式

Android知识体系


坐标系相关

layout()

  1. mBtn.setOnTouchListener(new View.OnTouchListener() {
  2. float tLastX;
  3. float tLastY;
  4. @Override
  5. public boolean onTouch(View v, MotionEvent event) {
  6. switch (event.getAction()){
  7. case MotionEvent.ACTION_DOWN:
  8. Log.e(TAG, "按下事件");
  9. tLastX = event.getX();
  10. tLastY = event.getY();
  11. break;
  12. case MotionEvent.ACTION_MOVE:
  13. Log.e(TAG, "移动事件");
  14. int tOffsetX = Math.round(event.getX() - tLastX);
  15. int tOffsetY = Math.round(event.getY() - tLastY);
  16. v.layout(v.getLeft() + tOffsetX, v.getTop() + tOffsetY,
  17. v.getRight() + tOffsetX, v.getBottom() + tOffsetY);
  18. break;
  19. case MotionEvent.ACTION_UP:
  20. Log.e(TAG, "松开事件");
  21. break;
  22. }
  23. return false;
  24. }
  25. });

ViewGroup 的位置被确定后,会调用 onLayout() ,在 onLayout() 中会遍历所有的子元素并调用子元素的 layout()layout()是确定 View 本身在屏幕上显示的具体位置,即在代码中设置其成员变量 mLeftmTopmRightmBottom 的值,这几个值是在屏幕上构成矩形区域的四个坐标点,就是该 View 显示的位置,不过这里的具体位置都是相对与父视图的位置而言

offsetLeftAndRight()和offsetTopAndBottom()

  1. mBtn.setOnTouchListener(new View.OnTouchListener() {
  2. float tLastX;
  3. float tLastY;
  4. @Override
  5. public boolean onTouch(View v, MotionEvent event) {
  6. switch (event.getAction()){
  7. case MotionEvent.ACTION_DOWN:
  8. Log.e(TAG, "按下事件");
  9. tLastX = event.getX();
  10. tLastY = event.getY();
  11. break;
  12. case MotionEvent.ACTION_MOVE:
  13. Log.e(TAG, "移动事件");
  14. int tOffsetX = Math.round(event.getX() - tLastX);
  15. int tOffsetY = Math.round(event.getY() - tLastY);
  16. v.offsetLeftAndRight(tOffsetX);
  17. v.offsetTopAndBottom(tOffsetY);
  18. break;
  19. case MotionEvent.ACTION_UP:
  20. Log.e(TAG, "松开事件");
  21. break;
  22. }
  23. return false;
  24. }
  25. });

修改原来的layout()offsetLeftAndRight()offsetTopAndBottom()即可了。这是系统提供的对View上下、左右同时进行移动的API,效果与上相同。

LayoutParams

  1. mBtn.setOnTouchListener(new View.OnTouchListener() {
  2. float tLastX;
  3. float tLastY;
  4. @Override
  5. public boolean onTouch(View v, MotionEvent event) {
  6. switch (event.getAction()){
  7. case MotionEvent.ACTION_DOWN:
  8. Log.e(TAG, "按下事件");
  9. tLastX = event.getX();
  10. tLastY = event.getY();
  11. break;
  12. case MotionEvent.ACTION_MOVE:
  13. Log.e(TAG, "移动事件");
  14. int tOffsetX = Math.round(event.getX() - tLastX);
  15. int tOffsetY = Math.round(event.getY() - tLastY);
  16. ViewGroup.MarginLayoutParams tParams = (ViewGroup.MarginLayoutParams)v.getLayoutParams();
  17. tParams.leftMargin = v.getLeft() + tOffsetX;
  18. tParams.topMargin = v.getTop() + tOffsetY;
  19. v.setLayoutParams(tParams);
  20. break;
  21. case MotionEvent.ACTION_UP:
  22. Log.e(TAG, "松开事件");
  23. break;
  24. }
  25. return false;
  26. }
  27. });

通过LayoutParams来移动View,主要通过修改Margin属性来实现的,所以View的getLeft()getRight()就会改变。而这个Margin属性,只有ViewGroup.MarginLayoutParams及其子类才有这个属性,我们熟知的LinearLayout.LayoutParams或者RelativeLayout.LayoutParams都是继承了这个,所以可以使用,或者直接使用ViewGroup.MarginLayoutParams

setTranslationY和setTranslationX()

  1. mBtn.setOnTouchListener(new View.OnTouchListener() {
  2. float tLastX;
  3. float tLastY;
  4. @Override
  5. public boolean onTouch(View v, MotionEvent event) {
  6. switch (event.getAction()){
  7. case MotionEvent.ACTION_DOWN:
  8. tLastX = event.getX();
  9. tLastY = event.getY();
  10. Log.e(TAG, "按下事件: " + tLastX + ", " + tLastY);
  11. break;
  12. case MotionEvent.ACTION_MOVE:
  13. int tOffsetX = Math.round(event.getX() - tLastX);
  14. int tOffsetY = Math.round(event.getY() - tLastY);
  15. Log.e(TAG, "移动事件: " + tOffsetX + ", " + tOffsetY + ", " + (v.getTranslationX() + tOffsetX)
  16. + ", " + (v.getTranslationY() + tOffsetY));
  17. v.setTranslationX(v.getTranslationX() + tOffsetX);
  18. v.setTranslationY(v.getTranslationY() + tOffsetY);
  19. break;
  20. case MotionEvent.ACTION_UP:
  21. Log.e(TAG, "松开事件");
  22. Log.e(TAG, "left: " + mBtn.getLeft() + ", right: " + mBtn.getRight() + ", top: " + mBtn.getTop()
  23. + ", bottom: " + mBtn.getBottom());
  24. break;
  25. }
  26. return false;
  27. }
  28. });

View的setTranslationY(),会改变translationX和translationY的值,但是不会更改margin的值,所以getLeft()getRight()不会改变。所以,我们从这里开得出一个结论:View的位置是会受translationXtranslationY影响的,另外,setTranslationX(0)会恢复TranslationX的值。为了使View的移动显得更为平滑,因此可以使用View的属性动画来指定translationX和translationY。

scrollTo与scrollBy

  1. mBtn.setOnTouchListener(new View.OnTouchListener() {
  2. float tLastX;
  3. float tLastY;
  4. @Override
  5. public boolean onTouch(View v, MotionEvent event) {
  6. switch (event.getAction()){
  7. case MotionEvent.ACTION_DOWN:
  8. Log.e(TAG, "按下事件");
  9. tLastX = event.getX();
  10. tLastY = event.getY();
  11. break;
  12. case MotionEvent.ACTION_MOVE:
  13. Log.e(TAG, "移动事件");
  14. int tOffsetX = Math.round(event.getX() - tLastX);
  15. int tOffsetY = Math.round(event.getY() - tLastY);
  16. ((View)(v.getParent())).scrollBy(-tOffsetX, -tOffsetY);//移动子view
  17. // v.scrollBy(-tOffsetX, -tOffsetY);//移动按钮文字
  18. break;
  19. case MotionEvent.ACTION_UP:
  20. Log.e(TAG, "松开事件");
  21. break;
  22. }
  23. return false;
  24. }
  25. });

View 类中,有两个变量 mScrollXmScrollY,它们记录的是 View 的内容的偏移值。mScrollXmScrollY 的默认值都是 0,即默认不偏移。假设我们令 mScrollX = 10,那么该 View 的内容会相对于原来向左偏移 10px
我们一般不直接设置mScrollXmScrollY,可以通过一下两个方法来设置:

scrollTo():移动到具体的坐标点位置
scrollBy():在原有的位置基础上再移动一个偏移量


1、这两个方法会修改mScrollXmScrollY的值
2、这两个方法会触发onScrollChanged回调

前面几个方法都是移动View自己本身,而这两个方法移动的都是View里面的内容,比如 textView 中移动的是文字,imagView 中移动的是图片;如果放在ViewGroup中使用,则移动的是ViewGroup里面所有的子View。

所以说,我们为了移动View,那我们就来移动View所在的ViewGroup,但是要注意的是,移动的偏移量要取反,为什么呢?这是因为本来是该View移动dx、dy,现在View保持不动,让ViewGroup移动,则根据相对运动原理,就相当于ViewGroup移动了-dx、-dy。

Scroller

前面我们使用的不管是 scrollBy 还是 scrollTo ,移动其实都是在一瞬间完成的,只是因为我们的触摸动作不断触发,View 不断改变位置,造成了一个过度动画的假象。
假如我们有一个按钮,比如点击按钮就会把 View 往右移动 100 像素,那么你会发现此时的 View 是瞬间变换位置,并不是慢慢移动到指定位置。
Scroller可以根据需要移动的总距离,以及设置的移动时间,计算出每一次需要移动的距离,然后不断的进行移动,这样就实现了一个动画的效果。

VelocityTracker

属性动画

ViewDragHelper

结论

1、View提供了分别提供了getLeft()、getRight、getRight()、getBottom()四个方法获取对于的信息。除此之外3.0之后View还提供了四个比较重要的位置参数信息,X、Y、translationX、translationY。View的宽高是有top、left、right、bottom参数决定的
而X,Y和translationX,和translationY则负责View位置的改变。

参考

Android实现滑动的几种方法
Android Scroller完全解析,关于Scroller你所需知道的一切
Android Scroller大揭秘
Android中实现滑动的七种方式
[Android] 滑动 View 的原理及处理
Android View 滑动和弹性滑动读书笔记
实现View滑动的七种方法
深入理解Android View(一)
Android View 的滚动原理和 Scroller、VelocityTracker 类的使用

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注