Android中View的滑动

在生活中我们使用 Android 的 App 经常会看到一些炫丽的动态界面,出现这种效果很多是因为 View 的滑动,比如 RecyclerView 的滑动,掌握 View 的滑动方式,我们也能做出这样的效果;实现 View 的滑动常见的有4种方法,它们分别是 使用 scrollTo/scrollBy、使用动画、改变布局参数和使用 layout,下面将对它们一一进行介绍。

1、使用 scrollTo/scrollBy
View提供了 scrollTo(int x,int y) 和 scrollBy(int x,int y) 这两个方法实现了它的滑动,具体实现代码(基于 Android Api 27)如下所示:

public void scrollTo(int x, int y) {
  if (mScrollX != x || mScrollY != y) {
    int oldX = mScrollX;
    int oldY = mScrollY;
    mScrollX = x;
    mScrollY = y;
    invalidateParentCaches();
    onScrollChanged(mScrollX, mScrollY, oldX, oldY);
    if (!awakenScrollBars()) {
        postInvalidateOnAnimation();
    }
   }
 }

public void scrollBy(int x, int y) {
  scrollTo(mScrollX + x, mScrollY + y);
}

从scrollTo(int x,int y) 和 scrollBy(int x,int y) 这两个方法总结得出,scrollTo(int x,int y) 实现了所传递参数的绝对滑动,而 scrollBy(int x,int y) 实现了当前位置的相对滑动,因为它调用了 scrollTo(int x,int y) 方法。在这2个方法中,出现了 mScrollX 和 mScrollY 这两个成员变量,其中 mScrollX 表示 View 的左边缘和 View 内容左边缘在水平方向的距离,mScrollY 表示 View 上边缘和 View 内容上边缘在垂直方向上的距离。下面写一个例子来使用 scrollBy(int x,int y) 方法;首先新建一个布局文件 activity_view_general_slide.xml,它的代码如下所示:






然后再新建一个Java文件,名字为 ViewGeneralSlideActivity,代码如下所示:

private TextView mTv;
private MyHandler mHandler = null;
private int mSlideY = 20;
private int mSlideX = 20;
private MyThread thread = null;
private boolean isRunThread = true;

class MyHandler extends Handler {
   @Override
   public void handleMessage(Message msg) {
      super.handleMessage(msg);
      mTv.scrollBy(0,mSlideY);
      mSlideY += 10;
   }
 }
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_view_general_slide);
    mTv = findViewById(R.id.tv_scroll);
    mHandler = new MyHandler();
    thread = new MyThread();
}
public void onClickView(View view) {
    thread.start();
}
class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        while (isRunThread) {
           try {
               Thread.sleep(500);
               sendMessage();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
        }
      }
  }
@Override
protected void onDestroy() {
     super.onDestroy();
     isRunThread = false;
     if (mHandler != null) {
          mHandler.removeCallbacksAndMessages(null);
     }
 }
 
 private void sendMessage() {
     Message message = Message.obtain();
     mHandler.sendMessage(message);
 }
 

没点击“scrollBy滑动”控件之前的效果如下所示:
image

点击“scrollBy滑动”控件之后的效果如下所示:
image

注意:不管是 scrollBy(int x,int y) 还是 scrollTo(int x,int y) 方法,滑动的并非是 View 本身,而是 View 的内容。

2、使用动画

使用动画来移动 View ,是通过 View 的 translationX 和 translationY 这两个属性来实现的,它即可以采用传统的 View 动画,也可以采用属性动画;在使用属性动画方面,如果要在 Android 3.0以下版本使用,那么就要使用到开源动画库 nineoldandroids,它的官方地址为:http://nineoldandroids.com/ 。下面举个例子用代码实现使用 View 动画来移动。

2、1 采用传统的 View 动画

(1) 在 res 目录新建 anim 文件夹并在 anim 文件夹下创建 view_translate.xml:



   

 

(2) 新建一个 Java 文件,名字叫 AnimationActivity,并实现它的代码,如下所示:

private TextView mTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_animation);
    mTv = findViewById(R.id.tv);
}
public void onClick(View view) {
    Animation animation = AnimationUtils.loadAnimation(this, R.anim.view_translate);
    mTv.startAnimation(animation);
}

(3) 在 layout 文件夹下新建一个 activity_animation.xml 文件,代码如下所示:



     

没点击 “点击该按钮可移动view” 这个按钮之前,它运行的效果图如下所示:

image

点击 “点击该按钮可移动view” 这个按钮之后,它运行时移动的过程中,某一瞬间的效果图如下所示:

image

2、2 使用属性动画移动

(1) 为了兼容 Android 3.0 以下版本的手机,在项目 app 目录下的 build.gradle文件中添加 nineoldandroids 的依赖:

implementation 'com.nineoldandroids:library:2.4.0'

(2) 新建一个 Java 文件,名字叫 Animation2Activity,它的代码如下所示:

private TextView mTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_animation2);
    mTv = findViewById(R.id.tv);
}
public void onClick(View view) {
    ObjectAnimator.ofFloat(mTv,"translationY",0,300).setDuration(1000).start();
}

public void onClick2(View view) {
    Toast.makeText(this, "点击了“可移动的属性view”",   Toast.LENGTH_SHORT).show();
}

(3) 在 layout 文件夹下新建一个 activity_animation2.xml 文件,代码如下所示:



     

没点击 “点击该按钮可属性动画移动view” 之前,它的运行效果图如下所示:

image

点击 “点击该按钮可属性动画移动view” 这个按钮之后,它运行时移动的过程中,某一瞬间的效果图如下所示:

image

对以上使用动画的例子进行总结得出:(1) 传统的 View 动画只是对影像做移动,并不是真正的改变 View 的位置,它的 fillAfter 属性默认值为 false,也就等同于做完动画之后会回到原位置;当 fillAfter 属性设置为 true 的时候,View 做完动画后,影像不会回到原始的位置,但如果在该 View 做一个事件监听,点击做完动画后的 View 是不会响应事件的,点击 View 的原始位置就会响应事件,因为在系统眼里 View 做完动画之后它的位置没有发生改变。(2) 属性动画在 Android 3.0版本以下的手机不可以用,要想使用,必须添加 nineoldandroids 这个依赖酷;属性动画不是对影像做移动,而是真正的改变了 View 的位置,如果在该 View 做一个事件监听,点击 View 的原始位置是不会有事件响应的,而点击做完动画之后的位置就会有事件响应。

3、改变布局参数

通过改变布局参数来实现 View 的滑动的思路有2个:(1) 向右移动一个View,只需要把它的 marginLeft 参数增大,向其它方向移动同理,只需改变相应的 margin 参数;(2) 要移动的 View 的旁边预先放一个 View,它的初始化宽度为0,然后要想右移动View,只需把预先放置的那个View的宽度增大,这样就把要移动的 View “推”到右边了。示例如下所示:

(1) 新建一个布局文件,名字叫 activity_change_layout_parameter.xml



     

新建一个 Java 文件,名字叫 ChangeLayoutParameterActivity,它的代码如下所示:

TextView mTv;
View mView;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_change_layout_parameter);
    mTv = findViewById(R.id.tv);
    mView = findViewById(R.id.view);
}
public void onClick(View view) {
    ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mTv.getLayoutParams();
    params.leftMargin += 200;
    params.width += 200;
    mTv.requestLayout();
}
public void onClick2(View view) {
    ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mView.getLayoutParams();
    params.width += 200;
    mView.requestLayout();
}
public void onClick3(View view) {
    Toast.makeText(this,"点击了“改变布局参数可移动的第一个View” ",Toast.LENGTH_SHORT).show();
}
public void onClick4(View view) {
    Toast.makeText(this,"点击了“改变布局参数可移动的第二个View” ",Toast.LENGTH_SHORT).show();
}

没点击 “点击我可移动第一个View” 按钮之前,效果图如下所示:

image

点击 “点击我可移动第一个View” 按钮之后,效果图如下所示:

image

对以上改变布局参数移动 View 进行总结:不管是对 View 的 marginLeft 参数进行增大;还是要移动的 View 的旁边预先放一个 View,然后要向右移动View;他们两种方法都实现了 View 真正位置的移动,而不是 View 影像的移动;如果在 移动的 View 做事件监听,点击原始位置不会事件响应,点击移动后的位置会有事件响应。

4、使用 layout

使用 layout 移动 View 的思路是这样的:在View的onTouchEvent方法中对MotionEvent中的坐标进行记录,记录按下的时候记录,在移动的时候计算他们的偏移量,调用layout()对view的位置进行重绘制;下面举个例子:

(1) 新建一个布局文件,名叫 activity_use_layout.xml :



     

(2) 新建一个 Java 文件,名叫 UseLayoutActivity,它的实现代码如下所示:

TextView mTv;
private int lastX,lastY;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_use_layout);
    mTv = findViewById(R.id.tv);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
    int x = (int) event.getRawX();
    int y = (int) event.getRawY();
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            lastX = x;
            lastY = y;
            break; 
         case MotionEvent.ACTION_MOVE:
            int offsetX = x - lastX;
            int offsetY = y - lastY;
            mTv.layout(mTv.getLeft()+offsetX,
            mTv.getTop()+offsetY,
            mTv.getRight()+offsetX,
            mTv.getBottom()+offsetY);
            lastX = x;
            lastY = y;
            break;
     }
    return true;
}
public void onClick(View v){
    Toast.makeText(this," 点击了 “使用 Layout移动View” 按钮 ",Toast.LENGTH_SHORT).show();
}

没滑动屏幕之前的效果图如下所示:

image

滑动屏幕某一瞬间的效果图如下所示:

image

对以上使用 layout 移动 View 进行总结:使用 layout 移动 View,改变的不是 View 的影像,而是 View 真正的位置,所以如果要设置 View 的事件监听,点击移动后的 View 才会响应事件;使用 layout 移动 View 是和 Activity 的触摸事件结合使用的,触摸的时候获取相对于屏幕触摸的x 和 y 坐标,然后手指移动的过程中计算出移动的距离再加上 View 相对于父视图的位置,得到 View 移动后的位置,最后 View 使用 layout 方法 重新布局再进行重新绘画。

好了,本篇文章写到这里就结束了,由于本人技术水平有限,难免会有出错的地方,欢迎批评指正,谢谢大家的阅读,另外附上Android的View滑动demo

image关注微信公众号,阅读更多有趣的技术文章

你可能感兴趣的:(javaandroid)