好了,还是老规矩,先给出效果图,这里就绘制了一个简单框架,各位看官可以任意添加自己的东西。
下面我来解释一下怎么使用我们的ViewDragHelper来实现这个效果
先给出我们的布局
<?xml version="1.0" encoding="UTF-8"?> <com.jeason.qqmenudemo.widget.SideslipLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_frame" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/bg" > <FrameLayout android:id="@+id/left_panel" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:ems="1" android:layout_height="match_parent" android:layout_width="wrap_content" android:textColor="#FFFFFF" android:layout_marginTop="56dp" android:textSize="24dp" android:layout_gravity="center" android:text="@string/menu_block" /> </FrameLayout> <com.jeason.qqmenudemo.widget.MyFrameLayout android:id="@+id/main_panel" android:background="@drawable/bg_3" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <include layout="@layout/custom_actionbar" /> <FrameLayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent"> <com.jeason.qqmenudemo.widget.MyTextView android:id="@+id/content_block" android:layout_width="260dp" android:layout_height="wrap_content" android:layout_gravity="center" android:text="@string/content_bolock" android:padding="8dp" android:textSize="18dp" /> </FrameLayout> </LinearLayout> </com.jeason.qqmenudemo.widget.MyFrameLayout> </com.jeason.qqmenudemo.widget.SideslipLayout>
我们目前认为所有的童鞋对于ViewDragHelper都有一定的了解,不了解的可以看一下我的上一篇博文。
强大的ViewDragHelper和ViewDragHelper的妙用
一
这里,我们先给出代码,再给大家慢慢解释。
package com.jeason.qqmenudemo.widget; import com.jeason.qqmenudemo.R; import com.jeason.qqmenudemo.view.ViewHelper; import android.annotation.SuppressLint; import android.content.Context; 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.widget.FrameLayout; public class SideslipLayout extends FrameLayout{ private static final String TAG = "SideslipLayout"; @SuppressWarnings("unused") private Context mContext; private ViewDragHelper mDragHelper; private DragListener mDragListener; private int range; private int width; private int height; private int mainLeft; private FrameLayout mLeftPanel; private MyFrameLayout mMainPanel; private Status mDefaultStatus = Status.Close; private Status status = mDefaultStatus; public enum Status { Drag, Open, Close } public SideslipLayout(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub mContext = context; mDragHelper = ViewDragHelper.create(this, mDragHelperCallback); } @Override protected void onFinishInflate() { mMainPanel = (MyFrameLayout) findViewById(R.id.main_panel); mMainPanel.setDragLayout(this); mLeftPanel = (FrameLayout) findViewById(R.id.left_panel); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = mLeftPanel.getMeasuredWidth(); //which meaning Full_Window height = mLeftPanel.getMeasuredHeight(); range = (int) (width * 0.6f); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mDragHelper.shouldInterceptTouchEvent(ev); } @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent e) { try { mDragHelper.processTouchEvent(e); } catch (Exception ex) { ex.printStackTrace(); } return true; } public Status getStatus() { if (mainLeft == 0) { status = Status.Close; } else if (mainLeft == range) { status = Status.Open; } else { status = Status.Drag; } return status; } public void open() { open(true); } public void open(boolean animate) { if (animate) { if (mDragHelper.smoothSlideViewTo(mMainPanel, range, 0)) { ViewCompat.postInvalidateOnAnimation(this); } } else { mMainPanel.layout(range, 0, range * 2, height); dispatchDragEvent(range); } } public void close() { close(true); } public void close(boolean animate) { if (animate) { if (mDragHelper.smoothSlideViewTo(mMainPanel, 0, 0)) { ViewCompat.postInvalidateOnAnimation(this); } } else { mMainPanel.layout(0, 0, width, height); dispatchDragEvent(0); } } private void dispatchDragEvent(int mainLeft) { if (mDragListener == null) { return; } float percent = mainLeft / (float) range; animateView(percent); mDragListener.onDrag(percent); Status lastStatus = status; if (lastStatus != getStatus() && status == Status.Close) { mDragListener.onClose(); } else if (lastStatus != getStatus() && status == Status.Open) { mDragListener.onOpen(); } } private void animateView(float percent) { float f1 = 1 - percent * 0.3f; ViewHelper.setScaleX(mMainPanel, f1); ViewHelper.setScaleY(mMainPanel, f1); ViewHelper.setTranslationX(mLeftPanel, -mLeftPanel.getWidth() / 2.3f + mLeftPanel.getWidth() / 2.3f * percent); ViewHelper.setScaleX(mLeftPanel, 0.5f + 0.5f * percent); ViewHelper.setScaleY(mLeftPanel, 0.5f + 0.5f * percent); ViewHelper.setAlpha(mLeftPanel, percent); } @Override public void computeScroll() { if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } public interface DragListener { public void onOpen(); public void onClose(); public void onDrag(float percent); } public void setDragListener(DragListener dragListener) { mDragListener = dragListener; } private ViewDragHelper.Callback mDragHelperCallback = new ViewDragHelper.Callback() { @Override public int clampViewPositionHorizontal(View child, int left, int dx) { if (mainLeft + dx < 0) { return 0; } else if (mainLeft + dx > range) { return range; } else { return left; } } @Override public boolean tryCaptureView(View child, int pointerId) { return true; } @Override public int getViewHorizontalDragRange(View child) { return range; } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); if (xvel > 0) { open(); } else if (xvel < 0) { close(); } else if (releasedChild == mMainPanel && mainLeft > range * 0.3) { open(); } else if (releasedChild == mLeftPanel && mainLeft > range * 0.7) { open(); } else { close(); } } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { if (changedView == mMainPanel) { mainLeft = left; } else { mainLeft = mainLeft + left; } if (mainLeft < 0) { mainLeft = 0; } else if (mainLeft > range) { mainLeft = range; } if (changedView == mLeftPanel) { mLeftPanel.layout(0, 0, width, height); mMainPanel.layout(mainLeft, 0, mainLeft + width, height); } dispatchDragEvent(mainLeft); } }; }
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mDragHelper.shouldInterceptTouchEvent(ev); } @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent e) { try { mDragHelper.processTouchEvent(e); } catch (Exception ex) { ex.printStackTrace(); } return true; }这里,我们一般最好采用OnTouchEvent中return true;当然如果我们的ChildView有可能需要消耗MotionEvent,那么我们需要在ChildView的OnTouchEvent 中Action_Down 中return true;(如果ChildView在Action_Down return false ;那么最终 MotionEvent走到ParentView always return true;那么ChildView 将无法得到后续的MotionEvent);
当然,考虑另一种情况,如果我们持续让我们的ParentView 中return false;那么我们可以假设如果此次的MotionEvent 最终没有被消耗(所有的ChildView都 return false ,那么我们的ParentView最终会得到Action_Down但是由于我们在Action_Down 返回了false;name 在得到Action_Down之后不会继续得到Action_UP(这里假设我们的ParentVIew是最上层ViewGroup),那么dispatchTouchEvent返回了false,事件被取消了,那么你会发现我们的ChildView不能够再移动了),这里可以说是ViewDragHelper需要考虑的逻辑问题。
当让如果我们需要我们的ChildView可能也要Action_Move时,那么就需要我们严格把控onInterceptTouchEvent,因为如果我们直接将我们的MotionEvent交给我们的ViewDragHelper,在这种情况下,因为我们的ChildView是可以移动的,所以onInterceptTouchEvent直接就返回了true,ChildView就没有机会了。那么我们可以怎么做呢。比如
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean result; if(ev.getAction() == MotionEvent.ACTION_MOVE){ if(mChildView.requestTouchEvent(ev)){ result = false; }else{ result = mDragHelper.shouldInterceptTouchEvent(ev); } }else{ result = mDragHelper.shouldInterceptTouchEvent(ev); } return result; }
这里仅仅给大家做一个说明,避免复杂情况下大家发现我们的ViewDragHelper出现各种不是那么灵敏的情形,或者不是那么容易掌控。反正这里还是要提醒大家一定要充分理解onInterceptTouchEvent和onTouchEvent和dispatchTouchEvent之间的逻辑关系。
好的,走到这里我们才完成了将我们的移动处理逻辑交给了ViewDragHelper。按照我上次分析的我们的移动实现要交给我们的CallBack
private ViewDragHelper.Callback mDragHelperCallback = new ViewDragHelper.Callback() { @Override public int clampViewPositionHorizontal(View child, int left, int dx) { if (mainLeft + dx < 0) { //计算ChildView左边界 return 0; } else if (mainLeft + dx > range) { return range; } else { return left; } } @Override public boolean tryCaptureView(View child, int pointerId) { //这里表示我们的Left和Main都可以移动 return true; } @Override public int getViewHorizontalDragRange(View child) { //框定水平移动范围 return range; } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { //移动结束后判断开合状态自动切换状态 super.onViewReleased(releasedChild, xvel, yvel); if (xvel > 0) { open(); } else if (xvel < 0) { close(); } else if (releasedChild == mMainPanel && mainLeft > range * 0.3) { open(); } else if (releasedChild == mLeftPanel && mainLeft > range * 0.7) { open(); } else { close(); } } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { if (changedView == mMainPanel) { mainLeft = left; } else { mainLeft = mainLeft + left; } if (mainLeft < 0) { mainLeft = 0; } else if (mainLeft > range) { mainLeft = range; } if (changedView == mLeftPanel) { //移动Left时候,保持Left不动改变Main的位置 mLeftPanel.layout(0, 0, width, height); mMainPanel.layout(mainLeft, 0, mainLeft + width, height); } dispatchDragEvent(mainLeft); //开启移动的时候的动画效果缩放等 } };
分析完了CallBack,好像就没有什么说的了,源码下载
http://download.csdn.net/download/jaysong2012/8961751
下面继续给大家分享的更复杂一点的ViewDragHelper的妙用,有兴趣的话大家可以尝试一下。重点提示一下在onInterceptTouchEvent中的处理,有机会的话再把源码共享给大家。
版权声明:本文为博主原创文章,未经博主允许不得转载。
ViewDragHelper的妙用二 --QQ侧滑菜单的实现
原文地址:http://blog.csdn.net/jaysong2012/article/details/47253799