使用ViewDragHelper 来处理移动的操作
1.ViewDragHelper 在高版本中v4 包才有
2.ViewDragHelper 主要用于ViewGroup中对子控件的拖拽处理
3.ViewDragHelper 主要封装了 View 的触摸位置,触摸速度和移动距离等的监控和scroller,
通过接口回调的方式,告诉我们,只需要指定是否需要移动 ,移动多少 回调接口
ViewDragHelper.Callback
package com.yifei.myapplication;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
public class MyDrawViewGroup extends FrameLayout {
View redView;
View blueView;
private ViewDragHelper viewDragHelper;
public MyDrawViewGroup(Context context) {
super(context);
}
public MyDrawViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
private void initView() {
viewDragHelper = ViewDragHelper.create(this, callback);//Param1 viewParent Param2 回调函数
}
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
//用于判断,是否捕获当前child的触摸事件
// child:当前的子View
// return : true; 就获得并解析 false不处理
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == redView;
}
//当View 被开始获得和解析的回调Captured
//capturedChild 当前获得的子View
@Override
public void onViewCaptured(@NonNull View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
}
//获取View 水平方向的拖拽范围 不能限制边界
//
@Override
public int getViewHorizontalDragRange(@NonNull View child) {
return super.getViewHorizontalDragRange(child);
}
//获取View竖值方向的拖拽范围
@Override
public int getViewVerticalDragRange(@NonNull View child) {
return super.getViewVerticalDragRange(child);
}
//控制child 在水平方向的滚动
//left 表示ViewDragHelper 认为你想让当前child的left改变的值 left = child.getLeft+dx
//dx 本次child在水平方向的距离
//return 表示你真正想让child的left 变成的值
@Override
public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
return super.clampViewPositionHorizontal(child, left, dx);
}
//控制child在竖直方向的滚动
//top 表示ViewDragHelper 认为你想让当前child的top改变的值 top = child.getTop+dy
//return 表示你真正想让child的Top 变成的值
@Override
public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
return super.clampViewPositionVertical(child, top, dy);
}
//当child的位置改变的时候执行 一般用于做其他子View的伴随移动
//Params changedView ://位置改变的child
//left child 当前最新的left
//top child 当前最新的top
//dx 本次水平移动的距离
//dy 本次垂直移动的距离
@Override
public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
}
//手指抬起的方法 相当与 Motion.Action_Up
//Params releasedChild 当前抬起的view
//xvel x 方向移动的速度 正 为向右移动 负 为向左移动
// yvel y 方向 移动的速度 正 为向下移动 负 为向 上移动
@Override
public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
}
};
//初始化子View的引用 当ViewGroup 的xml结束标签被读取完成,会执行该方法
//此时会知道自己有几个子控件
//onFinishInflate() 方法一般用来初始化子控件
@Override
protected void onFinishInflate() {
super.onFinishInflate();
redView = getChildAt(0);
blueView = getChildAt(1);
}
}
对滑动的范围进行限制
package com.yifei.myapplication;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
public class MyDrawViewGroup extends FrameLayout {
View redView;
View blueView;
private ViewDragHelper viewDragHelper;
public MyDrawViewGroup(Context context) {
super(context);
initView();
}
public MyDrawViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView();
}
private void initView() {
viewDragHelper = ViewDragHelper.create(this, callback);//Param1 viewParent Param2 回调函数
}
//
@Override
public void computeScroll() {
super.computeScroll();
if (viewDragHelper.continueSettling(true)) { //判断子View 是否在移动
ViewCompat.postInvalidateOnAnimation(this);//传递当前的位置
}
}
//事件的分发
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
//事件的拦截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return viewDragHelper.shouldInterceptTouchEvent(ev);//判断是否应该拦截 让ViewDragHelper 来处理
}
//事件的处理
@Override
public boolean onTouchEvent(MotionEvent event) {
viewDragHelper.processTouchEvent(event);//将事件交给ViewDragHelper 来处理
return true;
}
private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
//用于判断,是否捕获当前child的触摸事件
// child:当前的子View
// return : true; 就获得并解析 false不处理
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == redView;
}
//当View 被开始获得和解析的回调Captured
//capturedChild 当前获得的子View
@Override
public void onViewCaptured(@NonNull View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
}
//获取View 水平方向的拖拽范围 不能限制边界
// 返回的值 目前用在手指抬起的时候的View缓慢移动的动画时间的计算
//不要返回0
@Override
public int getViewHorizontalDragRange(@NonNull View child) {
return getMeasuredWidth() - child.getMeasuredWidth(); //限制范围
}
//获取View竖值方向的拖拽范围
// 返回的值 目前用在手指抬起的时候的View缓慢移动的动画时间的计算
//不要返回0
@Override
public int getViewVerticalDragRange(@NonNull View child) {
return getMeasuredHeight() - child.getMeasuredHeight();
}
//控制child 在水平方向的滚动
//left 表示ViewDragHelper 认为你想让当前child的left改变的值 left = child.getLeft+dx
//dx 本次child在水平方向的距离
//return 表示你真正想让child的left 变成的值
@Override
public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
if(left<0){
left =0;
}else if(left>(getMeasuredWidth() - child.getMeasuredWidth())){
left = getMeasuredWidth() - child.getMeasuredWidth();
}
return left;
}
//控制child在竖直方向的滚动
//top 表示ViewDragHelper 认为你想让当前child的top改变的值 top = child.getTop+dy
//return 表示你真正想让child的Top 变成的值
@Override
public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
if(top<0){
top = 0;
}else if(top>(getMeasuredHeight() - child.getMeasuredHeight())){
top=getMeasuredHeight() - child.getMeasuredHeight();
}
return top;
}
//当child的位置改变的时候执行 一般用于做其他子View的伴随移动
//Params changedView ://位置改变的child
//left child 当前最新的left
//top child 当前最新的top
//dx 本次水平移动的距离
//dy 本次垂直移动的距离
@Override
public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
}
//手指抬起的方法 相当与 Motion.Action_Up
//Params releasedChild 当前抬起的view
//xvel x 方向移动的速度 正 为向右移动 负 为向左移动
// yvel y 方向 移动的速度 正 为向下移动 负 为向 上移动
@Override
public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
}
};
//初始化子View的引用 当ViewGroup 的xml结束标签被读取完成,会执行该方法
//此时会知道自己有几个子控件
//onFinishInflate() 方法一般用来初始化子控件
@Override
protected void onFinishInflate() {
super.onFinishInflate();
redView = getChildAt(0);
blueView = getChildAt(1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//没有特殊要求 可以这样做
measureChild(redView,widthMeasureSpec,heightMeasureSpec);
measureChild(blueView,widthMeasureSpec,heightMeasureSpec); //另外一种测量方式
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left =getPaddingLeft()+getWidth()/2-redView.getMeasuredWidth()/2;
int top =0+redView.getPaddingTop();
redView.layout(left, top, left+redView.getMeasuredWidth(),top+ redView.getMeasuredHeight());//把红色的View摆放上去
blueView.layout(left, redView.getBottom(), left+redView.getMeasuredWidth(), redView.getBottom() +redView.getHeight());
}
}
让蓝色的控件也可以移动
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == redView||child ==blueView;
}
通过layout方法让红色view移动时,蓝色View 也一起移动
//当child的位置改变的时候执行 一般用于做其他子View的伴随移动
//Params changedView ://位置改变的child
//left child 当前最新的left
//top child 当前最新的top
//dx 本次水平移动的距离
//dy 本次垂直移动的距离
@Override
public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if (changedView == redView) {//单红色view移动时,让蓝色View 也移动
blueView.layout(left, top + blueView.getMeasuredHeight(), left + blueView.getMeasuredWidth(), top + blueView.getMeasuredHeight() * 2);
}
}
当蓝色View移动时,让红色View 一起移动
//当child的位置改变的时候执行 一般用于做其他子View的伴随移动
//Params changedView ://位置改变的child
//left child 当前最新的left
//top child 当前最新的top
//dx 本次水平移动的距离
//dy 本次垂直移动的距离
@Override
public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
if (changedView == redView) {//单红色view移动时,让蓝色View 也移动
blueView.layout(left, top + blueView.getMeasuredHeight(), left + blueView.getMeasuredWidth(), top + blueView.getMeasuredHeight() * 2);
}else if(changedView==blueView){
redView.layout(left,top-redView.getHeight(),left+blueView.getMeasuredWidth(),top);
}
}
当手指抬起时候,view如果靠近左边, 就向左边滑动
//手指抬起的方法 相当与 Motion.Action_Up
//Params releasedChild 当前抬起的view
//xvel x 方向移动的速度 正 为向右移动 负 为向左移动
// yvel y 方向 移动的速度 正 为向下移动 负 为向 上移动
@Override
public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
int centerleft = getMeasuredWidth()/2 -redView.getWidth()/2;//得到中间线的距离
if(centerleft>releasedChild.getLeft()){
//在左边滑动,应该向左缓慢滑动
viewDragHelper.smoothSlideViewTo(releasedChild,0,0);//缓慢滑到左边
ViewCompat.postInvalidateOnAnimation(MyDrawViewGroup.this); //调用方法,滑到左边
}else if(centerleft
引入jar包
implementation 'com.nineoldandroids:library:2.4.0'
一个缩放动画
private void executeAnim(float fraction) {
//执行一个伴随动画 动画效果
ViewHelper.setScaleX(redView,(float) (1+0.5*fraction));//一个缩放动画
ViewHelper.setScaleY(redView,(float) (1+0.5*fraction));
}