package me.chenfuduo.myviewdraghelperusage.drag; import android.content.Context; import android.graphics.Color; import android.graphics.PorterDuff; import android.support.v4.view.ViewCompat; 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 android.widget.FrameLayout; /** * Created by Administrator on 2015/6/29. */ public class DragLayout extends FrameLayout { private ViewDragHelper dragHelper; private ViewGroup mLeftContent; private ViewGroup mMainContent;//红色 int mHeight; int mWidth; int mRange; private onDragStatusChangedListener listener; Status mStatus = Status.Close; /** * 状态枚举 */ public enum Status { Close, Open, Draging } public interface onDragStatusChangedListener { void onClose(); void onOpen(); void onDraging(float percent); } public void setDragStatusListener(onDragStatusChangedListener listener) { this.listener = listener; } public Status getStatus() { return mStatus; } public void setStatus(Status mStatus) { this.mStatus = mStatus; } public DragLayout(Context context) { this(context, null); } public DragLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DragLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //创建ViewDragHelper的静态工厂方法有两个,一个带float sensitivity参数,一个不带,这个参数的默认值是1.0f //第一个参数是父view,也就是这里的DragLayout,给个this即可 //第二个参数(若有),是灵敏度,默认值是1.0f //第三个参数是回调接口 //ViewDragHelper.create(this,1.0f,mCallback); dragHelper = ViewDragHelper.create(this, mCallback); } /** * 该接口默认实现了public boolean tryCaptureView(View child, int pointerId)方法 * 当然还需要实现很多其他的方法 */ private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() { /** * 根据返回结果决定当前child是否可以拖拽 * @param child 当前被拖拽的view * @param pointerId 区分多点触摸的id * @return */ @Override public boolean tryCaptureView(View child, int pointerId) { Log.e("Test", "child view:" + child.toString()); // return child == mMainContent; return true; } /** * 当capturedChild被捕获的时候调用 * @param capturedChild * @param activePointerId */ @Override public void onViewCaptured(View capturedChild, int activePointerId) { Log.d("Test", "onViewCaptured--->capturedChild:" + capturedChild); super.onViewCaptured(capturedChild, activePointerId); } /** * 返回拖拽的范围,不对拖拽进行真正的限制,仅仅决定了动画执行速度 * @param child * @return */ @Override public int getViewHorizontalDragRange(View child) { return mRange; } /** * 根据建议值修正将要移动到的位置(水平) * 此时没有发生真正的移动 * @param child 当前拖拽的view * @param left 新的位置的建议值 left = oldLeft + dx; 其中oldLeft = child.getLeft(). * @param dx 位置变化量 * @return */ @Override public int clampViewPositionHorizontal(View child, int left, int dx) { super.clampViewPositionHorizontal(child, left, dx); //向左 负的 // Log.e("Test","left:" + left + " dx:" + dx); if (child == mMainContent) { left = fixLeft(left); } return left; } /* @Override public int clampViewPositionVertical(View child, int top, int dy) { super.clampViewPositionVertical(child, top, dy); //向上 负Log.e("Test","top:" + top + " dy:" + dy); return top; }*/ /** * 当View位置改变的时候,处理要做的事情(更新状态,伴随动画,重绘界面) * @param changedView * @param left * @param top * @param dx * @param dy */ @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); int newLeft = left; if (changedView == mLeftContent) { //把当前的变化量传递给mMainContent newLeft = mMainContent.getLeft() + dx; } newLeft = fixLeft(newLeft); if (changedView == mLeftContent) { //当左面板移动之后,再强制放回去 mLeftContent.layout(0, 0, 0 + mWidth, 0 + mHeight); mMainContent.layout(newLeft, 0, newLeft + mWidth, 0 + mHeight); } dispatchDragEvent(newLeft); //为了兼容低版本,每次修改值,进行重绘 invalidate(); } /** * 当View被释放的时候,执行动画 * @param releasedChild 被释放的View * @param xvel 水平方向的速度 向右为正 * @param yvel 竖直方向的速度 向下为正 */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); if (xvel == 0 && mMainContent.getLeft() > mRange / 2.0f) { open(); } else if (xvel > 0) { open(); } else { close(); } } }; /** * 更新状态,执行动画 * * @param newLeft */ public void dispatchDragEvent(int newLeft) { float percent = newLeft * 1.0f / mRange; if (listener!=null){ listener.onDraging(percent); } //上一个状态 Status preStatus = mStatus; //更新状态,执行回调 mStatus = updateStatus(percent); if (mStatus != preStatus){ //状态发生变化 if (mStatus == Status.Close){ if (listener != null){ listener.onClose(); } }else if (mStatus == Status.Open){ if (listener != null){ listener.onOpen(); } } } animViews(percent); } private Status updateStatus(float percent) { if (percent == 0.0f) { return Status.Close; } else if (percent == 1.0f) { return Status.Open; } return Status.Draging; } private void animViews(float percent) { //左面板缩放动画 //mLeftContent.setScaleX(0.5f + 0.5f*percent); //上面注释的效果和下面的一模一样 mLeftContent.setScaleX(evaluate(percent, 0.5f, 1.0f)); mLeftContent.setScaleY(0.5f + 0.5f * percent); //左面板平移动画 mLeftContent.setTranslationX(evaluate(percent, -mWidth / 2.0f, 0)); //左面板透明度变化 mLeftContent.setAlpha(evaluate(percent, 0.5f, 1.0f)); //主面板缩放动画 mMainContent.setScaleX(evaluate(percent, 1.0f, 0.8f)); mMainContent.setScaleY(evaluate(percent, 1.0f, 0.8f)); //背景:亮度变化(颜色变化) getBackground().setColorFilter((Integer) evaluateColor(percent, Color.BLACK, Color.TRANSPARENT), PorterDuff.Mode.SRC_OVER); } public Float evaluate(float fraction, Number startValue, Number endValue) { float startFloat = startValue.floatValue(); return startFloat + fraction * (endValue.floatValue() - startFloat); } public Object evaluateColor(float fraction, Object startValue, Object endValue) { int startInt = (Integer) startValue; int startA = (startInt >> 24) & 0xff; int startR = (startInt >> 16) & 0xff; int startG = (startInt >> 8) & 0xff; int startB = startInt & 0xff; int endInt = (Integer) endValue; int endA = (endInt >> 24) & 0xff; int endR = (endInt >> 16) & 0xff; int endG = (endInt >> 8) & 0xff; int endB = endInt & 0xff; return (int) ((startA + (int) (fraction * (endA - startA))) << 24) | (int) ((startR + (int) (fraction * (endR - startR))) << 16) | (int) ((startG + (int) (fraction * (endG - startG))) << 8) | (int) ((startB + (int) (fraction * (endB - startB)))); } @Override public void computeScroll() { super.computeScroll(); //持续平滑动画(高频率调用) if (dragHelper.continueSettling(true)) { //如果返回true,动画还需要继续执行 ViewCompat.postInvalidateOnAnimation(this); } } public void close() { close(true); } /** * 关闭 */ public void close(boolean isSmooth) { int finalLeft = 0; if (isSmooth) { //触发一个平滑的动画 if (dragHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)) { //返回true表示还没有移动到指定的位置,需要刷新界面 //参数传this,(child所在的ViewGroup) ViewCompat.postInvalidateOnAnimation(this); } } else { mMainContent.layout(finalLeft, 0, finalLeft + mWidth, 0 + mHeight); } } public void open() { open(true); } /** * 打开 */ public void open(boolean isSmooth) { int finalLeft = mRange; if (isSmooth) { //触发一个平滑的动画 if (dragHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)) { //返回true表示还没有移动到指定的位置,需要刷新界面 //参数传this,(child所在的ViewGroup) ViewCompat.postInvalidateOnAnimation(this); } } else { mMainContent.layout(finalLeft, 0, finalLeft + mWidth, 0 + mHeight); } } /** * 根据范围修正左边值 * * @param left * @return */ private int fixLeft(int left) { if (left < 0) { return 0; } else if (left > mRange) { return mRange; } return left; } //下面重写的方法[onInterceptTouchEvent,onTouchEvent]可以理解为传递触摸事件(没有重写dispatchTouchEvent) /* @Override public boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev); }*/ /** * 将事件拦截下来,相当于把自定义控件的事件交给ViewDragHelper去处理 * * @param ev * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //传递给ViewDragHelper return dragHelper.shouldInterceptTouchEvent(ev); } /** * 将拦截下来的事件做处理 * * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { //对多点触摸有点问题 try { dragHelper.processTouchEvent(event); } catch (Exception e) { e.printStackTrace(); } //返回true,持续接收事件 return true; } @Override protected void onFinishInflate() { super.onFinishInflate(); mLeftContent = (ViewGroup) getChildAt(0); mMainContent = (ViewGroup) getChildAt(1); } /** * 当尺寸有变化的时候调用,在onMeasure(...)方法后调用 * * @param w * @param h * @param oldw * @param oldh */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mHeight = getMeasuredHeight(); mWidth = getMeasuredWidth(); mRange = (int) (mWidth * 0.6); } }