Android ViewDragHelper仿淘宝拖动加载效果

 更新时间:2017年08月11日 11:46:08   作者:Maximilian_M  
这篇文章主要为大家详细介绍了Android ViewDragHelper仿淘宝拖动加载效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

拖动加载是我在淘宝的商品详情界面发现的,感觉很实用。于是就分析它的实现方式,感觉用ViewDragHelper可以很方便的实现这种效果。下面大致把我的思路分步骤写一下。先上图吧。

首先建工程什么的我就不多说了。咱从ViewDragHelper的实现开始说吧,ViewDragHelper一般用在一个自定义ViewGroup的内部,可以对其子View进行移动操作。

创建自定义ViewGroup:

package com.maxi.viewdraghelpertest.widget; 
 
import android.content.Context; 
import android.support.v4.widget.ViewDragHelper; 
import android.util.AttributeSet; 
import android.view.View; 
import android.widget.LinearLayout; 
 
public class DragHelperLayout extends LinearLayout{ 
  private ViewDragHelper mDragHelper; 
  @SuppressWarnings("static-access") 
  public DragHelperLayout(Context context, AttributeSet attrs) { 
    super(context, attrs); 
    // TODO Auto-generated constructor stub 
    /* 
     * 创建带回调接口的ViewDragHelper 
     */ 
    mDragHelper = ViewDragHelper.create(this, 10.0f,new DragHelperCallback());// 参数一:该类生成的对象(当前的ViewGroup) 
                      // 参数二:敏感度(越大越敏感) 
  } 
  class DragHelperCallback extends ViewDragHelper.Callback { 
 
    @Override 
    public boolean tryCaptureView(View arg0, int arg1) { 
      // TODO Auto-generated method stub 
      return false; 
    } 
     
  } 
} 

然后将触摸事件传递给ViewDragHelper:

@Override 
public boolean onInterceptTouchEvent(MotionEvent event) 
{ 
  return mDragHelper.shouldInterceptTouchEvent(event);//是否应该打断MotionEvent的传递 
} 
 
@Override 
public boolean onTouchEvent(MotionEvent event) 
{ 
  mDragHelper.processTouchEvent(event); 
  return true; 
} 

接着我们开始实现DragHelperCallback,这个ViewDragHelper.Callback回调中可以对ViewGroup中的一些View进行操作,在此我们只对本项目涉及到的相关用法做解析,详细点请自行查阅资料。

class DragHelperCallback extends ViewDragHelper.Callback { 
 
    @Override 
    public boolean tryCaptureView(View arg0, int arg1) { 
      // TODO Auto-generated method stub 
      return true;  //返回true表示可以捕捉ViewGroup中的View 
    } 
    /* 
     * (non-Javadoc) 
     * @see android.support.v4.widget.ViewDragHelper.Callback#clampViewPositionVertical(android.view.View, int, int) 
     * 限定View竖直方向上的活动区域,防止滑出ViewGroup 
     */ 
    @Override 
    public int clampViewPositionVertical(View child, int top, int dy) { 
       int topBound = getPaddingTop(); 
       int bottomBound = getHeight() - child.getHeight() - topBound; 
       int newHeight = Math.min(Math.max(top, topBound), bottomBound); 
       return newHeight; 
    } 
     
  } 

在上面的代码段中我已经做了注释,在clampViewPositionVertical中我们对View的竖直方向活动区域做了限制,防止滑出ViewGroup,当然你可以直接return top;不过为了效果我先这么限定一下。还有一个clampViewPositionHorizontal方法,同样是对其水平边界进行控制的,先不多说啦。这个时候咱们自定义的ViewGroup初期已经完成,先去试试水。

在activity_main.xml中加入

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  tools:context="com.maxi.viewdraghelpertest.MainActivity" > 
 
 
  <com.maxi.viewdraghelpertest.widget.DragHelperLayout 
    android:id="@+id/dhl" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical" 
    android:background="@android:color/darker_gray" 
    > 
  <TextView  
    android:layout_width="match_parent" 
    android:layout_height="100dp" 
    android:background="@android:color/holo_blue_bright" 
  /> 
  <TextView  
    android:layout_width="match_parent" 
    android:layout_height="100dp" 
    android:background="@android:color/holo_orange_dark" 
  /> 
  </com.maxi.viewdraghelpertest.widget.DragHelperLayout> 
 
 
</RelativeLayout> 

运行后的效果:

大家是不是都急了,做个拖动加载怎么搞起这东西了,不要急,这才刚刚开始,大家想想拖动加载是不是就是两个View在同一个ViewGroup里通过ViewDragHelper的滑动操作然后实现的?是不是有思路的?没有思路也没关系,咱慢慢来,想要两个View相关联,就是拖动一个View然后另一个View跟着它走该怎么实现呢?首先我们需要ViewDragHelper回调里的另一个方法onViewPositionChanged,该方法是在View位置发生改变时回调的。为的就是在上面的View上拉的时候让下面的View跟着往上走。来看看我们的实现方法:
首先先将两个View初始化:

private View t1, t2; 
/* 
 * (non-Javadoc) 
 * @see android.view.View#onFinishInflate() 
 * 初始化两个View 
 */ 
@Override 
protected void onFinishInflate() { 
  t1 = getChildAt(0); 
  t2 = getChildAt(1); 
} 

得到两个View后我们在回调中判断哪个位置发生了改变,

@Override 
public void onViewPositionChanged(View changedView, int left, int top, 
    int dx, int dy) { 
  // TODO Auto-generated method stub 
  int childIndex = 1; 
  if (changedView == t2) { 
    childIndex = 2; 
  } 
  viewFollowChanged(childIndex, top); 
} 

上面的代码段中有个方法viewFollowChanged,主要实现的就是View跟着动。

private void viewFollowChanged(int viewIndex, int posTop) { 
  viewH = t1.getMeasuredHeight(); 
  if (viewIndex == 1) { 
    int offsetTopBottom = viewH + t1.getTop() - t2.getTop(); 
    t2.offsetTopAndBottom(offsetTopBottom); 
  } else if (viewIndex == 2) { 
    int offsetTopBottom = t2.getTop() - viewH - t1.getTop(); 
    t1.offsetTopAndBottom(offsetTopBottom); 
  } 
  invalidate(); 
} 

 

在运行是不是发现没有被点击拖动的View会跟着View一起移动,像一个整体双宿双飞。图我就不加了,大家运行看吧。因为我们要获取View的实际大小所以需要以下代码段的支持:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
  measureChildren(widthMeasureSpec, heightMeasureSpec); 
 
  int maxWidth = MeasureSpec.getSize(widthMeasureSpec); 
  int maxHeight = MeasureSpec.getSize(heightMeasureSpec); 
  setMeasuredDimension( 
      resolveSizeAndState(maxWidth, widthMeasureSpec, 0), 
      resolveSizeAndState(maxHeight, heightMeasureSpec, 0)); 
} 
 
public static int resolveSizeAndState(int size, int measureSpec, 
    int childMeasuredState) { 
  int result = size; 
  int specMode = MeasureSpec.getMode(measureSpec); 
  int specSize = MeasureSpec.getSize(measureSpec); 
  switch (specMode) { 
  case MeasureSpec.UNSPECIFIED: 
    result = size; 
    break; 
  case MeasureSpec.AT_MOST: 
    if (specSize < size) { 
      result = specSize | MEASURED_STATE_TOO_SMALL; 
    } else { 
      result = size; 
    } 
    break; 
  case MeasureSpec.EXACTLY: 
    result = specSize; 
    break; 
  } 
  return result | (childMeasuredState & MEASURED_STATE_MASK); 
} 

然后我们可以尝试将两个View满屏,android:layout_height="match_parent",把clampViewPositionVertical方法里限制的边界去掉吧,暂时先return top;这样试一下是不是有点像拖动加载了,呵呵哒,可是第一个View下拉的时候由于上面没有View怎么办?我们可以在clampViewPositionVertical中将它限定边界啊!

@Override 
    public int clampViewPositionVertical(View child, int top, int dy) { 
      int slideTop = top; 
      if (child == t1) { 
        if (top > 0) { 
          slideTop = 0; 
        } 
      } else if (child == t2) { 
        if (top < 0) { 
          slideTop = 0; 
        } 
      } 
      return child.getTop() + (slideTop - child.getTop()); 
    } 
 

已经大致成型了,然后就是拖动的时候将View自动置顶或置底,因为自动置顶或置底是在滑动松开之后,所以就需要用到ViewDragHelper回调里的onViewReleased方法,该方法就是在滑动松开之后调用,接下来实现它:

@Override 
    public void onViewReleased(View releasedChild, float xvel, float yvel) { 
      putStickOrDown(releasedChild);// 滑动松开后,需要置顶或置底 
    } 
    private void putStickOrDown(View releasedChild, float yvel) { 
    int finalTop = 0; // 默认是粘到最顶端 
    if (releasedChild == t1) { 
      // 滑动第一个view松开 
      if (yvel < 0)//灵敏度自己调吧 
        finalTop = -viewH; 
    } else { 
      // 滑动第二个view松开 
      if (yvel > 0)//同上 
        finalTop = viewH; 
    } 
    if (mDragHelper.smoothSlideViewTo(releasedChild, 0, finalTop)) { 
      ViewCompat.postInvalidateOnAnimation(this);// 会在下一个Frame开始的时候,发起一些invalidate操作 
    } 
    } 
 
    @Override 
    public void computeScroll() { 
      if (mDragHelper.continueSettling(true)) { 
        ViewCompat.postInvalidateOnAnimation(this); 
      } 
    } 

ok,是可以自动置顶或置底了。对了,那种拖动粘滞效果可以设置clampViewPositionVertical里的返回值,return child.getTop() + (finalTop - child.getTop()) / num;num值越大越粘滞。

然后淘宝第一个View是可以滑动的滑动到最底部然后才把手势事件交给ViewDragHelper处理的。这块试想如果用ScrollView的话,手势事件肯定会优先被它消费,这样肯定达不到我们想要的效果,所以在此我们需要对ScrollView进行自定义,大致的实现思路是当用户用户从触发屏幕开始判断是不是ScrollView在最底端,如果在最底端然后判断手势是否是向上滑动的如果也是则满足条件将touch事件交给父View就可以了,即requestDisallowInterceptTouchEvent该方法。然后自定义的ViewGroup中的onInterceptTouchEvent方法也要做相应修改,这里用GestureDetectorCompat处理事件,其回调用来判断是否是上下滑动。先声明private GestureDetectorCompat gestureDC;然后再gestureDC = new GestureDetectorCompat(context,new YSlideDetector());

class YSlideDetector extends SimpleOnGestureListener { 
 
  @Override 
  public boolean onScroll(MotionEvent e1, MotionEvent e2, 
      float distanceX, float distanceY) { 
    // TODO Auto-generated method stub 
    return Math.abs(distanceY) > Math.abs(distanceX);//Y方向绝对值大于X方向,上下滑动 
  } 
}<pre name="code" class="java">  @Override 
public boolean onInterceptTouchEvent(MotionEvent event) { 
  boolean is_y_slide = gestureDC.onTouchEvent(event); 
  boolean shouldIntercept = mDragHelper.shouldInterceptTouchEvent(event); 
  int action = event.getActionMasked(); 
  if (action == MotionEvent.ACTION_DOWN) { 
    mDragHelper.processTouchEvent(event);// action_down时就让mDragHelper开始工作,否则有时候导致异常 
  } 
  return shouldIntercept && is_y_slide; 
} 

OK。就这样。是不是达到了想要的效果了?

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

相关文章

  • Android 使用Vitamio打造自己的万能播放器(6)——在线播放(播放列表)

    Android 使用Vitamio打造自己的万能播放器(6)——在线播放(播放列表)

    本文主要介绍Android Vitamino在线播放列表,这里给大家提供效果图和实例代码以便大家参考学习,希望能帮助开发Android视频播放的朋友
    2016-07-07
  • Android开发之图形图像与动画(二)Animation实现图像的渐变/缩放/位移/旋转

    Android开发之图形图像与动画(二)Animation实现图像的渐变/缩放/位移/旋转

    Android 平台提供了两类动画,一类是Tween动画,就是对场景里的对象不断的进行图像变化来产生动画效果;旋转、平移、放缩和渐变等等,感兴趣的朋友可以了解下啊,希望本文对你有所帮助
    2013-01-01
  • Android中给按钮同时设置背景和圆角示例代码

    Android中给按钮同时设置背景和圆角示例代码

    相信每位Android开发者们都遇到过给按钮设置背景或者设置圆角的需求,但是如果要同时设置背景和圆角该怎么操作才是方便快捷的呢?这篇文章通过示例代码给大家演示了Android中给按钮同时设置背景和圆角的方法,有需要的朋友们可以参考借鉴。
    2016-10-10
  • 详解Android UI更新的几种方法

    详解Android UI更新的几种方法

    本篇文章主要介绍了Android UI更新的几种方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • Android Rsa数据加解密的介绍与使用示例

    Android Rsa数据加解密的介绍与使用示例

    RSA是第一个既能用于数据加密也能用于数字签名的算法。它易于理解和操作,也很流行。想起自己曾经使用过的Rsa非对称加密算法,闲下来总结一下。方便自己和大家以后使用的时候参考借鉴。下面来一起看看吧。
    2016-09-09
  • 深入理解Android中Scroller的滚动原理

    深入理解Android中Scroller的滚动原理

    今天给大家讲解的是Scroller类的滚动实现原理,可能很多朋友不太了解该类是用来干嘛的,但是研究Launcher的朋友应该对他很熟悉,Scroller类是滚动的一个封装类,可以实现View的平滑滚动效果,而我们今天就来探究一下为什么Scroller能够实现平滑滚动。
    2016-08-08
  • Android 快速实现状态栏透明样式的示例代码

    Android 快速实现状态栏透明样式的示例代码

    下面小编就为大家分享一篇Android 快速实现状态栏透明样式的示例代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-01-01
  • OKHttp使用详解

    OKHttp使用详解

    OkHttp 是一套处理 HTTP 网络请求的依赖库,由 Square 公司设计研发并开源,目前可以在 Java 和 Kotlin 中使用,这篇文章主要介绍了OKHttp详解,需要的朋友可以参考下
    2024-01-01
  • Kotlin可见性修饰符详解

    Kotlin可见性修饰符详解

    本文详细讲解了Kotlin可见性修饰符,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-11-11
  • Android Studio中使用SQLite的操作方法

    Android Studio中使用SQLite的操作方法

    这篇文章主要介绍了Android Studio中使用SQLite的操作方法的相关资料,需要的朋友可以参考下
    2023-06-06

最新评论