史上最全的Android ViewDragHelper解析
2016-05-16 18:44
260 查看
简介: 一般我们在自定义ViewGroup 的时候会通常都会用到onInterceptTouchEvent ,onTouchEvent 这些方法去进行距离的判断然后利用scroller 去进行目标的移动,从而实现ViewGroup 的自定义。此方法不但判断麻烦,而且逻辑复杂,不易操作,今天给大家要价讲的这个工具ViewDragHelper 是谷歌IO大会上推出的触摸辅助开发工具,极大的简化了开发自定义VIewGroup 的难度。
![](http://img.blog.csdn.net/20160516184324019)
1.废话不多说 ,首先要初始化 ViewDragHelper 这个工具类:
2.初始化 ViewDragHelper.Callback 这个触摸操作类:并初始化 要操作VIew的位置控制方法回掉类:
3.接下来我们会操作要移动VIew 的滑动限制距离 方法:
限制View活动的范围后 ,但我们释放手指的时候我们会操作另外一个方法:
这样一个基本的VIew滑动类就基本实现了。
接下来我贴出一个完整的示例:
所需要的xml布局:
完整的自定义侧滑类代码:
最后附上demo的下载地址 : 猛戳这里–https://github.com/g1258624735/MyApplication2
5. 所有的代码已经全部上传,注释很详细,包你看懂,已经详细的不能在详细了,看不懂的可以板砖拍我。
6. 大家在看的过程 当中,如果有什么不懂得话可以发邮件给我 ,1258624735@qq.com
7. ps: 如果大家觉得文章对你有帮助:不妨大赏一下支持原创,你的支持是我最大的动力:
1.废话不多说 ,首先要初始化 ViewDragHelper 这个工具类:
final float density = getResources().getDisplayMetrics().density; final float minVel = MIN_FLING_VELOCITY * density; //指定好需要处理拖动的ViewGroup和回调 就可以开始使用了 mViewDragHelper = ViewDragHelper.create(this, new DefaultDragHelper()); mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT); //设置minVelocity mViewDragHelper.setMinVelocity(minVel);
2.初始化 ViewDragHelper.Callback 这个触摸操作类:并初始化 要操作VIew的位置控制方法回掉类:
onViewPositionChanged() @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); mLeft = left; mLeftSize =(-left)/ (dip2px(getContext(),80) * 1.0f); Log.d(TAG, "onViewPositionChanged()--" + "left:" + left + ",top:" + top + ",dx:" + dx + ",dy:" + dy + "mLeftSize--" + mLeftSize); invalidate(); }
3.接下来我们会操作要移动VIew 的滑动限制距离 方法:
/** * 限制子View水平拖拉操作。默认不支持水平操作,重写该方法提供新的水平坐标(根据提供的渴望的水平坐标) * 不重写就不会支持水平坐标变化 * * @param child Child view being dragged * @param left Attempted motion along the X axis * @param dx Proposed change in position for left * @return The new clamped position for left */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { final int leftBound =v_content.getPaddingLeft()-dip2px(getContext(),80); final int rightBound =v_content.getPaddingRight(); final int newLeft = Math.min(Math.max(left, leftBound), rightBound); Log.d(TAG, "clampViewPositionHorizontal()--left:" + left + ",dx:" + dx+"v_content.getPaddingLeft(),"+v_content.getPaddingLeft()); return newLeft; } }
限制View活动的范围后 ,但我们释放手指的时候我们会操作另外一个方法:
/** * 手指离开屏幕 * 后续View 的坐标处理 * 比如 滑到超过一半 直接滑到满屏 又或者滑到不到一半的时候 * 还原坐标 * * @param releasedChild * @param xvel * @param yvel * */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); Log.d(TAG, "onViewReleased()--xv:" + xvel + ",yv:" + yvel); //上拉 if (mLeftSize > 0.5) { openCover(); } else { closeCover(); } invalidate(); }
这样一个基本的VIew滑动类就基本实现了。
接下来我贴出一个完整的示例:
package com.example.administrator.myapplication.fragment; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; import com.example.administrator.myapplication.R; import com.example.administrator.myapplication.widget.BotomDragLayout; import com.example.administrator.myapplication.widget.LeftScrollDeleteDragLayout; /** * 侧滑删除列表 * 2016/4/21 * gxj */ public class LeftScrollDeleteLayoutFragment extends Fragment { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getActivity().setTitle("LeftScrollDeleteLayoutFragment"); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { final LeftScrollDeleteDragLayout view = (LeftScrollDeleteDragLayout) inflater.inflate(R.layout.left_scroll_delete_layout, container, false); View view_bg =view.findViewById(R.id.del); view_bg.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(view.STATE == BotomDragLayout.CLOSEING){ view.openCover(); }else{ view.closeCover(); Toast.makeText(getActivity(),"删除",Toast.LENGTH_SHORT).show(); } } }); return view; } }
所需要的xml布局:
<com.example.administrator.myapplication.widget.LeftScrollDeleteDragLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/white"> <LinearLayout android:id="@+id/view_bg" android:layout_width="match_parent" android:layout_height="200dp" android:orientation="horizontal" android:gravity="right"> <Button android:id="@+id/del" android:layout_width="80dp" android:layout_height="match_parent" android:text="删除" android:textColor="@color/white" android:background="@color/red" android:gravity="center" /> </LinearLayout> <Button android:id="@+id/view_content" android:layout_width="match_parent" android:layout_height="200dp" android:text="向左滑出删除按钮" android:gravity="center" android:background="@color/colorPrimary" android:textColor="@android:color/white" /> </com.example.administrator.myapplication.widget.LeftScrollDeleteDragLayout>
完整的自定义侧滑类代码:
package com.example.administrator.myapplication.widget;
import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import com.example.administrator.myapplication.R;
/**
*列表侧滑删除效果
* author gxj
* date 2016.5.22
*/
public class LeftScrollDeleteDragLayout extends ViewGroup {
private final String TAG = this.getClass().getSimpleName();
private ViewDragHelper mViewDragHelper;
/**
* Minimum velocity that will be detected as a fling
*/
private static final int MIN_FLING_VELOCITY = 400; // dips per second
/**
* 菜单栏的状态
*/
public int STATE = 0;
public static final int CLOSEING = 0;
public static final int OPENED = 1;
private View v_content;
private int viewWidth = 0;
/**
* 滑动view 的顶部位置
*/
private int mLeft;
/**
* 滑动view 的滑动距离占自身高度的比例
*/
private float mLeftSize;
private View view_bg;
public LeftScrollDeleteDragLayout(Context context) {
this(context, null);
init();
}
public LeftScrollDeleteDragLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
init();
}
public LeftScrollDeleteDragLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
v_content = findViewById(R.id.view_content);
view_bg = findViewById(R.id.view_bg);
viewWidth = v_content.getHeight();
}
@Override
public void computeScroll() {
if (mViewDragHelper.continueSettling(true)) {
postInvalidateOnAnimation();
}
}
public void openCover() {
STATE = LeftScrollDeleteDragLayout.OPENED;
mViewDragHelper.smoothSlideViewTo(v_content, -dip2px(getContext(),80), 0);
postInvalidateOnAnimation();
}
public void closeCover() {
STATE = LeftScrollDeleteDragLayout.CLOSEING;
mViewDragHelper.smoothSlideViewTo(v_content, 0, 0);
postInvalidateOnAnimation();
}
private void init() {final float density = getResources().getDisplayMetrics().density; final float minVel = MIN_FLING_VELOCITY * density; //指定好需要处理拖动的ViewGroup和回调 就可以开始使用了 mViewDragHelper = ViewDragHelper.create(this, new DefaultDragHelper()); mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT); //设置minVelocity mViewDragHelper.setMinVelocity(minVel);
}
private class DefaultDragHelper extends ViewDragHelper.Callback {
@Override
public boolean tryCaptureView(View view, int i) {
return view == v_content;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
mLeft = left;
mLeftSize =(-left)/ (dip2px(getContext(),80) * 1.0f);
Log.d(TAG, "onViewPositionChanged()--" + "left:" + left + ",top:" + top + ",dx:" + dx + ",dy:" + dy + "mLeftSize--" + mLeftSize);
invalidate();
}
/**
* 当captureview被捕获时回调
*
* @param capturedChild
* @param activePointerId
*/
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
Log.d(TAG, "onViewCaptured()--:");
}
/**
* 手指离开屏幕
* 后续View 的坐标处理
* 比如 滑到超过一半 直接滑到满屏 又或者滑到不到一半的时候
* 还原坐标
*
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
Log.d(TAG, "onViewReleased()--xv:" + xvel + ",yv:" + yvel);
//上拉
if (mLeftSize > 0.5) {
openCover();
} else {
closeCover();
}
invalidate();
}
/**
* 当触摸到边缘的时候会调用
* @param edgeFlags
* @param pointerId
*/
@Override
public void onEdgeTouched(int edgeFlags, int pointerId) {
super.onEdgeTouched(edgeFlags, pointerId);
Log.d(TAG, "onEdgeTouched()");
}
@Override
public boolean onEdgeLock(int edgeFlags) {
Log.d(TAG, "onEdgeLock()");
return super.onEdgeLock(edgeFlags);
}
/**
* 当触摸到边缘的时候会调用
*
* @param edgeFlags
* @param pointerId 可以指定触摸边缘的子View
*/
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
super.onEdgeDragStarted(edgeFlags, pointerId);
Log.d(TAG, "onEdgeDragStarted()");
mViewDragHelper.captureChildView(v_content, pointerId);
}
/**
* 返回当前移动的View 的position
*
* @param index
* @return
*/
@Override
public int getOrderedChildIndex(int index) {
return super.getOrderedChildIndex(index);
}
/**
* 限制水平移动范围
* Return the magnitude of a draggable child view's horizontal range of motion in pixels.
* 似乎作用不大,其他情况只用于判断是否可以拖动
* 具体返回值真正起作用在于{@link ViewDragHelper#smoothSlideViewTo(View, int, int)}
*
* @param child Child view to check
* @return range of horizontal motion in pixels
*/
@Override
public int getViewHorizontalDragRange(View child) {
return child ==v_content?dip2px(getContext(),80):0;
}
/** * 限制子View水平拖拉操作。默认不支持水平操作,重写该方法提供新的水平坐标(根据提供的渴望的水平坐标) * 不重写就不会支持水平坐标变化 * * @param child Child view being dragged * @param left Attempted motion along the X axis * @param dx Proposed change in position for left * @return The new clamped position for left */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { final int leftBound =v_content.getPaddingLeft()-dip2px(getContext(),80); final int rightBound =v_content.getPaddingRight(); final int newLeft = Math.min(Math.max(left, leftBound), rightBound); Log.d(TAG, "clampViewPositionHorizontal()--left:" + left + ",dx:" + dx+"v_content.getPaddingLeft(),"+v_content.getPaddingLeft()); return newLeft; } }
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
final int action = MotionEventCompat.getActionMasked(event);
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mViewDragHelper.cancel();
return false;
}
//通过这个方法判断是否拦截 滑动事件
boolean flag = mViewDragHelper.shouldInterceptTouchEvent(event);
return flag;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//通过这个方法判断是否处理拦截的触摸事件
mViewDragHelper.processTouchEvent(event);
return true;
}
/**
* 丈量所有控件的高度
* 可以得到每个控件的最终高度
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@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));
viewWidth = v_content.getMeasuredWidth();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.e("onLayout", l + "|" + t + "|" + r + "|" + b);
v_content.layout(mLeft, t, mLeft+viewWidth,dip2px(getContext(),200) );
view_bg.layout(0, 0, r, dip2px(getContext(),200));
}
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
private int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
最后附上demo的下载地址 : 猛戳这里–https://github.com/g1258624735/MyApplication2
5. 所有的代码已经全部上传,注释很详细,包你看懂,已经详细的不能在详细了,看不懂的可以板砖拍我。
6. 大家在看的过程 当中,如果有什么不懂得话可以发邮件给我 ,1258624735@qq.com
7. ps: 如果大家觉得文章对你有帮助:不妨大赏一下支持原创,你的支持是我最大的动力:
相关文章推荐
- 从头学android_短信发送器
- Android dex分包方案
- android的消息处理机制——Looper,Handler,Message
- Android 源码编译相关
- Android必知必会-自定义Scrollbar样式
- Android必知必会-自定义Scrollbar样式
- Android文件系统深入剖析
- 怎样使一个Android应用不被杀死?
- Android设备测试应用总结
- Android侧滑菜单之DrawerLayout用法详解
- Android Framework 基于 IPC Binder驱动使用 RPC
- Android Studio运行程序时显示apk not exists
- Vim简明教程
- AlterDialog简单使用一
- android 基础 handler message runnable thread looper
- 获取Android设备上的所有存储设备
- Android 日常开发总结的技术经验 60 条
- Android 仿美团网,大众点评购买框悬浮效果之修改版
- 玩转Android Handler
- Android--中国象棋