当前位置: 首页 > 编程笔记 >

Android ViewDragHelper仿淘宝拖动加载效果

周奇文
2023-03-14
本文向大家介绍Android ViewDragHelper仿淘宝拖动加载效果,包括了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控件SeekBar仿淘宝滑动验证效果,包括了Android控件SeekBar仿淘宝滑动验证效果的使用技巧和注意事项,需要的朋友参考一下 SeekBar是一个拖动条控件,最简单的案例就是我们的调节音量,还有音频视频的播放,传统的SeekBar样式,如图 传统的实现太简单,不足以让我们到能装逼的地步。本来是打算实现滴滴出行滑动完成订单的效果,可惜找不到效果图,今天也就用淘宝

  • 本文向大家介绍JS实现的仿淘宝交易倒计时效果,包括了JS实现的仿淘宝交易倒计时效果的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了JS实现的仿淘宝交易倒计时效果。分享给大家供大家参考,具体如下: 希望本文所述对大家JavaScript程序设计有所帮助。

  • 模拟淘宝客户端在进行网络连接时,状态栏上的PageController滚动效果。可用于表示app的loading或者等待状态。 [Code4App.com]

  • 本文向大家介绍jQuery仿淘宝网产品品牌隐藏与显示效果,包括了jQuery仿淘宝网产品品牌隐藏与显示效果的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了jQuery仿淘宝网产品品牌隐藏与显示效果。分享给大家供大家参考。具体如下: 这里演示jQuery实现产品品牌隐藏与显示效果,仿淘宝网商品列表的品牌显示与折叠功能,很实用的代码,看了就知道了。 运行效果截图如下: 在线演示地址如下: ht

  • 本文向大家介绍Android中利用动态加载实现手机淘宝的节日特效,包括了Android中利用动态加载实现手机淘宝的节日特效的使用技巧和注意事项,需要的朋友参考一下 相信去年圣诞节打开过手机淘宝的童鞋都会对当时的特效记忆犹新吧:全屏飘雪,旁边还有个小雪人来控制八音盒背景音乐的播放,让人有种身临其境的感觉,甚至忍不住想狠狠购物了呢(误),大概就是下面这个样子滴: 嗯,确实很炫,那么我们一步步去分析是如

  • 本文向大家介绍js仿淘宝评价评分功能,包括了js仿淘宝评价评分功能的使用技巧和注意事项,需要的朋友参考一下 效果图: 图(1)初始图 图(2)点击效果 代码如下: 以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持呐喊教程!