本篇文章为大家展示了Android中怎么利用ViewDragHelper实现一个淘宝拖动加载效果,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
创建自定义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;
}
上述内容就是Android中怎么利用ViewDragHelper实现一个淘宝拖动加载效果,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。