Android中实现滑动的七种方式总结

在Android中想要实现实现滑动有很多方法,这篇博客将提供一些实现滑动的思路,希望可以帮助到有需要的人。

一、Android坐标体系

在讲解滑动之前,我们有必要简单提一下Android的坐标体系,因为滑动的实质就是坐标的不断改变,所以我们先来了解一下Android坐标系和视图坐标系两个概念。直接放上两张图片吧,一目了然。

Android坐标系

视图坐标系

从上面的两张图可以看出,Android坐标系的坐标原点位于屏幕的左上角,而视图坐标系的原点位于父视图的左上角,既然提供了两种不同的坐标系,那么我们如何来获取坐标呢,Android已经给我们提供了一些方法用于获取这些坐标,看下面的图便一目了然。

Android获取坐标的各种方法

二、layout方法

在View进行绘制时,是调用onLayout()方法来确定View的位置的,同样我们也可以调用layout()方法来传入我们滑动后的坐标便可以实现View的滑动,当然坐标的获取我们可以在触控事件中进行获取,下面我们做一个View随手指进行滑动的小例子来进行说明。

public class DragView extends View {
  private int mLastX;
  private int mLastY;
  public DragView(Context context) {
    this(context, null);
  }

  public DragView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public DragView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    int x = (int) event.getX();
    int y = (int) event.getY();
    int lastX = 0, lastY = 0;
    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(getLeft() + offsetX, getTop() + offsetY,
            getRight() + offsetX, getBottom() + offsetY);
        break;
    }
    return true;
  }
}

上面我们在触控事件中获取到获取到手指按下时的坐标(lastX, lastY),然后在手指移动时不断计算X和Y方向上的偏移量,然后再调用layout()方法来改变View的位置从而实现滑动。当然上面我们是通过getX()和getY()来获取视图坐标来进行修改,我们也可以通过getRawX()和getRawY()来获取绝对坐标来实现上面的效果。代码如下:

private int mLastX;
private int mLastY;
@Override
public boolean onTouchEvent(MotionEvent event) {
  int x = (int) event.getRawX();
  int y = (int) event.getRawY();
  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(getLeft() + offsetX, getTop() + offsetY,
          getRight() + offsetX, getBottom() + offsetY);
      //重新设置初始坐标
      mLastX = x;
      mLastY = y;
      break;
  }
  return true;
}

上面一定要注意,我们在改变完View的位置后必须调用设置初始坐标,这样才能准确获取偏移量。

三、offsetLeftAndRight和offsetTopAndBottom

这一种方法和上一种方法大部分步骤都是相同的,只是在移动View上有所差别,代码如下:

offsetLeftAndRight(offsetX);
offsetTopAndBottom(offsetY);

上面的这种方法只是多了一层封装,可以实现比上面实现同样的效果。

四、设置LayoutParams

LayoutParams可以通过改变的布局参数,我们可以通过下面的代码实现上面同样的效果。

LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);

注意:我们的LayoutParams可以通过getLayoutParams()方法来获取,但是要注意,如果View的父布局是LinearLayout,那么我们的LayoutParams就是LinearLayout.LayoutParams,如果View的父布局是RelativeLayout,则我们的LayoutParams就是RelativeLayout.LayoutParams。当然我们还有一种简单的方法,不用再管父布局的布局方式。代码如下:

ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
marginLayoutParams.leftMargin = getLeft() + offsetX;
marginLayoutParams.topMargin = getTop() + offsetY;
setLayoutParams(marginLayoutParams);

上面的这种方法不用管父布局的类型,使用起来更加方便。

五、scrollTo和scrollBy方法

关于这两个方法我们需要仔细说一下其中的一些注意事项

1 . scrollTo的参数是具体的一个坐标点(x, y), 而scrollBy的参数是在x, y方向上的坐标偏移

2 . scrollTo和scrollBy移动的是View的内容。这一点很重要!!!!

如果我们对ViewGroup使用scrollTo和scrollBy则移动的是内部的所有子View, 如果对TextView使用scrollTo和scrollBy则移动的是其中额文本。

3 . 视图移动还有一个不太好理解的地方在于坐标,我们下面结合图片来说明一下:

视图移动1

视图移动2

我们可以这样理解,我们的手机屏幕作为一个盖板,在手机屏幕下面是一个巨大的画布,我们的手机屏幕这个盖板是透明的,导致只有和手机屏幕重合的画布部分才会被我们看到,我们调用scrollToscrollBy也可以理解为是在移动手机上面的盖板。如图中所示,按钮在ViewGroup中的坐标是(20, 10),当我们调用scrollBy(20, 10)之后,就相当于移动了屏幕上的盖板,然后我们看到的按钮就到了ViewGroup的左上角。这样如果我们想让按钮在水平和竖直方向上各移动2010个单位,我们就必须调用scrollBy(-20, -10)

经过了上面的知识准备,我们这里也使用scrollBy来实现前面实现的那个View随手指移动的小例子:

((View)getParent()).scrollBy(-offsetX, -offsetY);

六、使用Scroller

Scroller也是滑动中很重要的一个角色,进过前面的scrollTo和scrollBy大家也会发现,它们的移动时瞬间完成的,滑动显得十分突兀,Google为了改善用户体验,便给出了Scroller,它可以实现平滑的移动,从而使滑动过程更加真实,用户体验更好,下面我们先简单说说Scroller的实现原理。

Scroller也是滑动中很重要的一个角色,进过前面的scrollToscrollBy大家也会发现,它们的移动时瞬间完成的,滑动显得十分突兀,Google为了改善用户体验,便给出了Scroller,它可以实现平滑的移动,从而使滑动过程更加真实,用户体验更好,下面我们先简单说说Scroller的实现原理。

Scroller的实现方式类似于scrollToscrollByscrollToscrollBy的移动都是从一个坐标点瞬间移动到另一个左边点,而Scroller则是将移动的这段距离切分成好几段的微小的位移,然后每一段调用scrollTo来不断移动这些微小的位移,由于人眼的视觉暂留效果,就会给人平滑移动的视觉效果。

下面我们在上一步的基础上增加一个小功能,第一部分还是View随手指移动,但是当我们松开手指时,让View自己平滑移动到最初始的位置(屏幕左上角),下面我们就来一步步介绍Scroller的用法

1 . 声明Scroller变量,并在构造方法中进行初始化

2 . 在触控事件的ACTION_UP(手指抬起)事件中传入开始滑动的坐标和需要滑动的距离并触发Scroller的滑动事件

3 . 重写computeScroll(),实现真正的滑动

下面是完整的代码示例:

public class DragView extends View {
  private int mLastX;
  private int mLastY;
  //声明Scroller变量
  private Scroller mScroller;
  public DragView(Context context) {
    this(context, null);
  }

  public DragView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public DragView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    //在构造方法中初始化Scroller变量
    mScroller = new Scroller(context);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    int x = (int) event.getRawX();
    int y = (int) event.getRawY();
    switch (event.getAction()){
      case MotionEvent.ACTION_DOWN:
        mLastX = x;
        mLastY = y;
        break;
      case MotionEvent.ACTION_MOVE:
        int offsetX = x - mLastX;
        int offsetY = y - mLastY;
        //实现View跟随手指移动的效果
        ((View)getParent()).scrollBy(-offsetX, -offsetY);
        //重新设置初始坐标
        mLastX = x;
        mLastY = y;
        break;
      case MotionEvent.ACTION_UP:
        //当手指抬起时执行滑动过程
        View view = (View) getParent();
        mScroller.startScroll(view.getScrollX(), view.getScrollY(),
            view.getScrollX(), view.getScrollY(), 5000);
        //调用重绘来间接调用computeScroll()方法
        invalidate();
        break;
    }
    return true;
  }

  @Override
  public void computeScroll() {
    super.computeScroll();
    //判断滑动过程是否完成
    if (mScroller.computeScrollOffset()){
      ((View)getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
      //通过重绘来不断调用computeScroll()方法
      invalidate();
    }
  }
}

上面的代码View随手指移动的代码部分是与前面相同的,我们只说说Scroller的部分以及一些注意事项

1 .  startScroll()方法各参数的意义,我们可以看看下面的源码:

/**
 * Start scrolling by providing a starting point, the distance to travel,
 * and the duration of the scroll.
 *
 * @param startX Starting horizontal scroll offset in pixels. Positive
 *    numbers will scroll the content to the left.
 * @param startY Starting vertical scroll offset in pixels. Positive numbers
 *    will scroll the content up.
 * @param dx Horizontal distance to travel. Positive numbers will scroll the
 *    content to the left.
 * @param dy Vertical distance to travel. Positive numbers will scroll the
 *    content up.
 * @param duration Duration of the scroll in milliseconds.
 */
public void startScroll(int startX, int startY, int dx, int dy, int duration)

可以看出startXstartY参数就是开始滚动的(x, y)坐标,那么我们就可以通过ViewGroup(子View的父视图)getScrollX()getScrollY()来获取,这里一定要注意,我们在滑动时的content就是子View,所以我们通过子View的父视图(ViewGroup)的getScrollX()getScrollY()获取到的就是子View在X和Y方向上滑动的距离,即就是我们需要的当我们手指抬起时子View的(x, y)坐标。而如果我们对子View调用getScrollX()getScrollY()方法,则获得的是子View内部的视图的滑动距离及坐标。

dxdy分别是在X和Y方向上的偏移量,而且注释中说了,如果我们传入的dxdy的值是正值,那么将会向上向左移动这个content(其实就是我们这里的View),即我们就可以让子View回到左上角,这里我们还是可以借助于上一小节中提到的视图移动的概念,我们想让子View向坐上方移动,其实就是想让覆盖在上面的盖板向右下角移动,我们可以将dxdy理解为父视图(覆盖在上面的盖板)的偏移量。

假设我们刚开始是让子View随手指向右下方移动,那么相当于覆盖在上面的盖板是向左上方移动,所以我们通过getScrollX()getScrollY()获得的值是负值,我们现在松开手指想让子View向左上方移动(即回到屏幕左上角),那么就相当于盖板向右下角移动,所以我们的dxdy的值必须是-getScrollX()-getScrollY(),此时的两个值都是正值。

2 . 由于我们的computeScroll()方法不会主动调用,但是我们又需要它不断调用从而不断进行微小移动从而实现平滑的滑动,所以我们可以通过下面的方法。

这三个按照以下顺序进行调用 invalidate()--->onDraw()--->computeScroll(),所以我们可以可以在ACTION_UP中调用完startScroll()方法后调用invalidate()方法,然后在computeScroll()方法中判断滑动是否结束,如果没结束,则通过getCurrX()getCurrY()来获得当前需要移动的微小的位移的坐标点,然后传入scrollTo()方法中,这时候子View还只是移动了一小段距离,然后我们再次调用invalidate()方法,然后接着调用onDraw()方法,然后再次进入computeScroll()中再次让子View移动一小段距离,直到滑动结束,computeScrollOffset()返回false,则这个循环调用的过程结束,从而完成平滑移动的过程。

七、属性动画

属性动画一样可以实现View的滑动,但是由于属性动画涉及到的知识点也是众多,这里不再展开来写,只是提供一个思路,后续后专门写一篇博客来说。

八、ViewDragHelper

ViewDragHelper可以帮助我们实现各种滑动需求,但是它的使用也相对较复杂,所以准备专门写一篇博客来介绍他,这里只是给出一个概念

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • android post请求接口demo

    本文实例为大家分享了android post请求接口demo测试代码,供大家参考,具体内容如下 MainActivity.java package com.tsh.test; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.URL; import android.app.Ac

  • Android 中ListView setOnItemClickListener点击无效原因分析

    前言 最近在做项目的过程中,在使用listview的时候遇到了设置item监听事件的时候在没有回调onItemClick 方法的问题.我的情况是在item中有一个Button按钮.所以不会回调.上百度找到了解决办法有两种,如下: 1.在checkbox.button对应的view处加android:focusable="false" 复制代码 代码如下: android:clickable="false" android:focusableInTouchMode=&

  • Android ListView的OnItemClickListener详解

    我们在使用ListView的时候,一般都会为ListView添加一个响应事件android.widget.AdapterView.OnItemClickListener.本文主要在于对OnItemClickListener的position和id参数做详细的解释,我相信有些人在这上面走了些弯路. 先来看一下官方的文档 position The position of the view in the adapter. id The row id of the item that was click

  • 超简单实现Android自定义Toast示例(附源码)

    Bamboy的自定义Toast,(以下称作"BToast") 特点在于使用简单, 并且自带两种样式: 1)普通的文字样式: 2)带图标样式. 其中图标有√和×两种图标. BToast还有另外一个特点就是: 系统自带Toast采用的是队列的方式,当前Toast消失后,下一个Toast才能显示出来: 而BToast会把当前Toast顶掉, 直接显示最新的Toast. 那么,简单三步,我们现在就开始自定义一下吧! (一).Layout: 要自定义Toast, 首先我们需要一个XML布局. 但

  • Android中Fab(FloatingActionButton)实现上下滑动的渐变效果

    前言 Promoted Actions是指一种操作按钮,它不是放在actionbar中,而是直接在可见的UI布局中(当然这里的UI指的是setContentView所管辖的范围).因此它更容易在代码中被获取到(试想如果你要在actionbar中获取一个菜单按钮是不是很难?),Promoted Actions往往主要用于一个界面的主要操作,比如在email的邮件列表界面,promoted action可以用于接受一个新邮件.promoted action在外观上其实就是一个悬浮按钮,更常见的是漂浮

  • Android实现屏蔽微信拉黑和删除联系人功能示例

    Android实现屏蔽微信拉黑和删除联系人功能,废话不多说,具体如下: 实现效果: 让微信永远弹不出那个删除的对话框不就相当于屏蔽掉该功能了吗?哈哈效果如图: 实现原理: 1.我们知道,其实微信每次删除联系人都会弹出此页面 2.如果你对AccessibilityService有过了解或者有看过我之前的两篇博客,你会知道,其实每次弹出这个框,都会触发AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED这个事件,所以我们只要在每次触发该事件的时候进行判断当前页面是

  • Android 仿微信聊天时间格式化显示功能

    本文给大家分享android仿微信聊天时间格式化显示功能. 在同一年的显示规则: 如果是当天显示格式为 HH:mm 例:14:45 如果是昨天,显示格式为 昨天 HH:mm 例:昨天 13:12 如果是在同一周 显示格式为 周一 HH:mm 例:周一14:05 如果不是同一周则显示格式为 M月d日 早上或者其它 HH:mm 例: 2月5日 早上10:10 不在同一年的显示规则: 显示格式为 yyyy年M月d日 晚上或者其它 HH:mm 例:2016年2月5日 晚上18:05 代码中如果有误,请留

  • Android 处理OnItemClickListener时关于焦点颜色的设置问题

    Android 处理OnItemClickListener时关于焦点颜色的设置问题 当我们使用OnItemClickListener来改变Item以使其比较突出时,我们一般采用如下的代码. public void onItemClick(AdapterView<?> parent, View view, int arg2, long arg3) { //恢复每个单元格背景色 TextView categoryTitle; for(int i=0;i<parent.getCount();i

  • Android中实现滑动的七种方式总结

    在Android中想要实现实现滑动有很多方法,这篇博客将提供一些实现滑动的思路,希望可以帮助到有需要的人. 一.Android坐标体系 在讲解滑动之前,我们有必要简单提一下Android的坐标体系,因为滑动的实质就是坐标的不断改变,所以我们先来了解一下Android坐标系和视图坐标系两个概念.直接放上两张图片吧,一目了然. Android坐标系 视图坐标系 从上面的两张图可以看出,Android坐标系的坐标原点位于屏幕的左上角,而视图坐标系的原点位于父视图的左上角,既然提供了两种不同的坐标系,那

  • Android实现View滑动的几种方式

    什么是View?实现View滑动的方式有哪些? 1. 关于View我们需要知道的 (1)什么是View? Android中的View类是所有UI控件的基类(Base class),也就是说我们平时所有到的各种UI控件,比如Button.ImagView等等都继承自View类.LinearLayout.FrameLayout等布局管理器的直接父类是ViewGroup,而ViewGroup也有View类派生.总的来说,View是对UI控件的抽象,它代表了屏幕上的一个矩形区域.通过继承View,并重写

  • Android实现View滑动的6种方式

    本文实例为大家分享了Android实现View滑动的具体方法,供大家参考,具体内容如下 1.View的滑动简介 View的滑动是Android实现自定义控件的基础,同时在开发中我们也难免会遇到View的滑动的处理.其实不管是那种滑动的方式基本思想都是类似的:当触摸事件传到View时,系统记下触摸点的坐标,手指移动时系统记下移动后的触摸的坐标并算出偏移量,并通过偏移量来修改View的坐标. 实现View滑动有很多种方法,这篇文章主要讲解六种滑动的方法,分别是:layout().offsetLeft

  • Android中数据解析的五种方式

    这里有一份XML文件,接下来我们用不同的解析方式来解析这份文件 <?xml version="1.0" encoding="UTF-8"?> <Movies> <Movie id="1"> <name>愤怒的小鸟</name> <type>Animation</type> <year>2016</year> </Movie> &

  • 在Android TextView中显示图片的4种方式详解

    我们知道,TextView控件一般是用来显示文本的,而图片一般是用ImageView控件来显示. 那TextView能否显示图片呢?答案是肯定的!下面列出常见的4种方式. 1.XML文件中指定属性值 这种方式应该是最常用的了,在TextView的左上右下显示图片,可用  android:drawableLeft  android:drawableTop  android:drawableRight  android:drawableBottom 比如我们要在TextView的顶部设置图片,代码如

  • java中String字符串删除空格的七种方式

    目录 trim() strip() stripLeading() 和 stripTrailing() replace replaceAll replaceFirst 总结 在Java中从字符串中删除空格有很多不同的方法,如trim,replaceAll等.但是,在JDK 11添加了一些新的功能,如strip.stripLeading.stripTrailing等. 想要从String中移除空格部分,有多少种方法,下面介绍JDK原生自带的方法,不包含第三方工具类库中的类似方法 trim() : 删

  • 记录Android studio JNI开发的三种方式(推荐)

    概述 在Andorid Studio不支持JNI开发之前大家一般都是使用Eclipse开发JNI,各种配置让人觉得很蛋疼.从Andorid Studio支持JNI开发后,让我们开发JNI变的如此简单. NDK 和 JNI介绍 JNI (Java Native Interface)是一套编程接口,用来实现Java代码和其他语言(c.C++或汇编)进行交互.这里需要注意的是JNI是JAVA语言自己的特性,也就是说JNI和Android没有关系.在Windows下面用JAVA做开发也经常会用到JNI,

  • JavaScript创建对象的七种方式(推荐)

    JavaScript创建对象的方式有很多,通过Object构造函数或对象字面量的方式也可以创建单个对象,显然这两种方式会产生大量的重复代码,并不适合量产.接下来介绍七种非常经典的创建对象的方式,他们也各有优缺点. 工厂模式 function createPerson(name, job) { var o = new Object() o.name = name o.job = job o.sayName = function() { console.log(this.name) } return

  • ionic实现滑动的三种方式

    在移动端受屏幕大小所限,展示内容很多的时候,就要使部分区域进行滑动.本文展示项目中所有到的几种方式,大家可以看自己的需求选择合适的滑动方式.实现滑动的基本原理,有两个容器A.B,假如A在外层,B在内层:外层的A宽度或高度固定,内层容器B宽度或者高度大于A即可实现滚动. 实现方式 1). ion-scroll 利用ionic自带的滑动指令 <ion-view view-title="Dashboard"> <ion-content class="padding

  • Android通过Handler与AsyncTask两种方式动态更新ListView(附源码)

    本文实例讲述了Android通过Handler与AsyncTask两种方式动态更新ListView的方法.分享给大家供大家参考,具体如下: 有时候我们需要修改已经生成的列表,添加或者修改数据,notifyDataSetChanged()可以在修改适配器绑定的数组后,不用重新刷新Activity,通知Activity更新ListView.今天的例子就是通过Handler AsyncTask两种方式来动态更新ListView. 布局main.xml: <?xml version="1.0&qu

随机推荐