首页 > 系统 > Android > 正文

ViewDragHelper实现QQ侧滑效果

2019-12-12 02:09:09
字体:
来源:转载
供稿:网友

前言

       侧滑的实现方式有很多方式来实现,这次总结的ViewDragHelper就是其中一种方式,ViewDragHelper是2013年谷歌I/O大会发布的新的控件,为了解决界面控件拖拽问题。下面就是自己学习写的一个实现类似于QQ侧滑效果的实现。
activity_main.xml:

<com.yctc.drag.DragLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/dl" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/bg" tools:context=".MainActivity" > <LinearLayout  android:layout_width="match_parent"  android:layout_height="match_parent"  android:orientation="vertical"  android:paddingBottom="50dp"  android:paddingLeft="10dp"  android:paddingRight="50dp"  android:paddingTop="50dp" >  <ImageView   android:layout_width="50dp"   android:layout_height="50dp"   android:src="@drawable/head" />  <ListView   android:id="@+id/lv_left"   android:layout_width="match_parent"   android:layout_height="match_parent" >  </ListView> </LinearLayout> <com.yctc.drag.MyLinearLayout  android:id="@+id/mll"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:background="#ffffff"  android:orientation="vertical" >  <RelativeLayout   android:layout_width="match_parent"   android:layout_height="50dp"   android:background="#18B6EF"   android:gravity="center_vertical" >   <ImageView    android:id="@+id/iv_header"    android:layout_width="30dp"    android:layout_height="30dp"    android:layout_marginLeft="15dp"    android:src="@drawable/head" />   <TextView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_centerHorizontal="true"    android:text="Header" />  </RelativeLayout>  <ListView   android:id="@+id/lv_main"   android:layout_width="match_parent"   android:layout_height="match_parent" >  </ListView> </com.yctc.drag.MyLinearLayout></com.yctc.drag.DragLayout>


DragLayout.Java:

public class DragLayout extends FrameLayout { private static final String TAG = "TAG"; private ViewDragHelper mDragHelper; private ViewGroup mLeftContent; private ViewGroup mMainContent; private OnDragStatusChangeListener mListener; private Status mStatus = Status.Close; /**  * 状态枚举  */ public static enum Status {  Close, Open, Draging; } public interface OnDragStatusChangeListener{  void onClose();  void onOpen();  void onDraging(float percent); } public Status getStatus() {  return mStatus; } public void setStatus(Status mStatus) {  this.mStatus = mStatus; } public void setDragStatusListener(OnDragStatusChangeListener mListener){  this.mListener = mListener; } public DragLayout(Context context) {  this(context, null); } public DragLayout(Context context, AttributeSet attrs) {  this(context, attrs, 0); } public DragLayout(Context context, AttributeSet attrs, int defStyle) {  super(context, attrs, defStyle);  // a.初始化 (通过静态方法)   mDragHelper = ViewDragHelper.create(this , mCallback); } ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {  // c. 重写事件  // 1. 根据返回结果决定当前child是否可以拖拽  // child 当前被拖拽的View  // pointerId 区分多点触摸的id  @Override  public boolean tryCaptureView(View child, int pointerId) {   Log.d(TAG, "tryCaptureView: " + child);   return true;  };  @Override  public void onViewCaptured(View capturedChild, int activePointerId) {   Log.d(TAG, "onViewCaptured: " + capturedChild);   // 当capturedChild被捕获时,调用.   super.onViewCaptured(capturedChild, activePointerId);  }  @Override  public int getViewHorizontalDragRange(View child) {   // 返回拖拽的范围, 不对拖拽进行真正的限制. 仅仅决定了动画执行速度   return mRange;  }  // 2. 根据建议值 修正将要移动到的(横向)位置 (重要)  // 此时没有发生真正的移动  public int clampViewPositionHorizontal(View child, int left, int dx) {   // child: 当前拖拽的View   // left 新的位置的建议值, dx 位置变化量   // left = oldLeft + dx;   Log.d(TAG, "clampViewPositionHorizontal: "      + "oldLeft: " + child.getLeft() + " dx: " + dx + " left: " +left);   if(child == mMainContent){    left = fixLeft(left);   }   return left;  }  // 3. 当View位置改变的时候, 处理要做的事情 (更新状态, 伴随动画, 重绘界面)  // 此时,View已经发生了位置的改变  @Override  public void onViewPositionChanged(View changedView, int left, int top,    int dx, int dy) {   // changedView 改变位置的View   // left 新的左边值   // dx 水平方向变化量   super.onViewPositionChanged(changedView, left, top, dx, dy);   Log.d(TAG, "onViewPositionChanged: " + "left: " + left + " dx: " + dx);   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();  }  // 4. 当View被释放的时候, 处理的事情(执行动画)  @Override  public void onViewReleased(View releasedChild, float xvel, float yvel) {   // View releasedChild 被释放的子View    // float xvel 水平方向的速度, 向右为+   // float yvel 竖直方向的速度, 向下为+   Log.d(TAG, "onViewReleased: " + "xvel: " + xvel + " yvel: " + yvel);   super.onViewReleased(releasedChild, xvel, yvel);   // 判断执行 关闭/开启   // 先考虑所有开启的情况,剩下的就都是关闭的情况   if(xvel == 0 && mMainContent.getLeft() > mRange / 2.0f){    open();   }else if (xvel > 0) {    open();   }else {    close();   }  }  @Override  public void onViewDragStateChanged(int state) {   // TODO Auto-generated method stub   super.onViewDragStateChanged(state);  } }; /**  * 根据范围修正左边值  * @param left  * @return  */ private int fixLeft(int left) {  if(left < 0){   return 0;  }else if (left > mRange) {   return mRange;  }  return left; } protected void dispatchDragEvent(int newLeft) {  float percent = newLeft * 1.0f/ mRange;  //0.0f -> 1.0f  Log.d(TAG, "percent: " + percent);  if(mListener != null){   mListener.onDraging(percent);  }  // 更新状态, 执行回调  Status preStatus = mStatus;  mStatus = updateStatus(percent);  if(mStatus != preStatus){   // 状态发生变化   if(mStatus == Status.Close){    // 当前变为关闭状态    if(mListener != null){     mListener.onClose();    }   }else if (mStatus == Status.Open) {    if(mListener != null){     mListener.onOpen();    }   }  }//  * 伴随动画:  animViews(percent); } private Status updateStatus(float percent) {  if(percent == 0f){   return Status.Close;  }else if (percent == 1.0f) {   return Status.Open;  }  return Status.Draging; } private void animViews(float percent) {  //  > 1. 左面板: 缩放动画, 平移动画, 透明度动画     // 缩放动画 0.0 -> 1.0 >>> 0.5f -> 1.0f >>> 0.5f * percent + 0.5f   //  mLeftContent.setScaleX(0.5f + 0.5f * percent);   //  mLeftContent.setScaleY(0.5f + 0.5f * percent);     ViewHelper.setScaleX(mLeftContent, evaluate(percent, 0.5f, 1.0f));     ViewHelper.setScaleY(mLeftContent, 0.5f + 0.5f * percent);     // 平移动画: -mWidth / 2.0f -> 0.0f     ViewHelper.setTranslationX(mLeftContent, evaluate(percent, -mWidth / 2.0f, 0));     // 透明度: 0.5 -> 1.0f     ViewHelper.setAlpha(mLeftContent, evaluate(percent, 0.5f, 1.0f));  //  > 2. 主面板: 缩放动画     // 1.0f -> 0.8f     ViewHelper.setScaleX(mMainContent, evaluate(percent, 1.0f, 0.8f));     ViewHelper.setScaleY(mMainContent, evaluate(percent, 1.0f, 0.8f));  //  > 3. 背景动画: 亮度变化 (颜色变化)     getBackground().setColorFilter((Integer)evaluateColor(percent, Color.BLACK, Color.TRANSPARENT), Mode.SRC_OVER); } /**  * 估值器  * @param fraction  * @param startValue  * @param endValue  * @return  */ public Float evaluate(float fraction, Number startValue, Number endValue) {  float startFloat = startValue.floatValue();  return startFloat + fraction * (endValue.floatValue() - startFloat); } /**  * 颜色变化过度  * @param fraction  * @param startValue  * @param endValue  * @return  */ 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();  // 2. 持续平滑动画 (高频率调用)  if(mDragHelper.continueSettling(true)){   // 如果返回true, 动画还需要继续执行   ViewCompat.postInvalidateOnAnimation(this);  } } public void close(){  close(true); } /**  * 关闭  */ public void close(boolean isSmooth) {  int finalLeft = 0;  if(isSmooth){   // 1. 触发一个平滑动画   if(mDragHelper.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){   // 1. 触发一个平滑动画   if(mDragHelper.smoothSlideViewTo(mMainContent, finalLeft, 0)){    // 返回true代表还没有移动到指定位置, 需要刷新界面.    // 参数传this(child所在的ViewGroup)    ViewCompat.postInvalidateOnAnimation(this);   }  }else {   mMainContent.layout(finalLeft, 0, finalLeft + mWidth, 0 + mHeight);  } } private int mHeight; private int mWidth; private int mRange; // b.传递触摸事件 @Override public boolean onInterceptTouchEvent(MotionEvent ev) {  // 传递给mDragHelper  return mDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) {  try {   mDragHelper.processTouchEvent(event);  } catch (Exception e) {   e.printStackTrace();  }  // 返回true, 持续接受事件  return true; } @Override protected void onFinishInflate() {  super.onFinishInflate();  // Github  // 写注释  // 容错性检查 (至少有俩子View, 子View必须是ViewGroup的子类)  if(getChildCount() < 2){   throw new IllegalStateException("布局至少有俩孩子. Your ViewGroup must have 2 children at least.");  }  if(!(getChildAt(0) instanceof ViewGroup && getChildAt(1) instanceof ViewGroup)){   throw new IllegalArgumentException("子View必须是ViewGroup的子类. Your children must be an instance of ViewGroup");  }  mLeftContent = (ViewGroup) getChildAt(0);  mMainContent = (ViewGroup) getChildAt(1); } @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.6f); }}

MyLineatLayout.java:

public class MyLinearLayout extends LinearLayout { private DragLayout mDragLayout; public MyLinearLayout(Context context) {  super(context); } public MyLinearLayout(Context context, AttributeSet attrs) {  super(context, attrs); } public void setDraglayout(DragLayout mDragLayout){  this.mDragLayout = mDragLayout; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) {  // 如果当前是关闭状态, 按之前方法判断  if(mDragLayout.getStatus() == Status.Close){   return super.onInterceptTouchEvent(ev);  }else {   return true;  } } @Override public boolean onTouchEvent(MotionEvent event) {  // 如果当前是关闭状态, 按之前方法处理  if(mDragLayout.getStatus() == Status.Close){   return super.onTouchEvent(event);  }else {   // 手指抬起, 执行关闭操作   if(event.getAction() == MotionEvent.ACTION_UP){    mDragLayout.close();   }   return true;  } }}

MainActivity.java:

public class MainActivity extends Activity { private static final String TAG = "TAG"; @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  requestWindowFeature(Window.FEATURE_NO_TITLE);  setContentView(R.layout.activity_main);  final ListView mLeftList = (ListView) findViewById(R.id.lv_left);  final ListView mMainList = (ListView) findViewById(R.id.lv_main);  final ImageView mHeaderImage = (ImageView) findViewById(R.id.iv_header);  MyLinearLayout mLinearLayout = (MyLinearLayout) findViewById(R.id.mll);  // 查找Draglayout, 设置监听  DragLayout mDragLayout = (DragLayout) findViewById(R.id.dl);  // 设置引用  mLinearLayout.setDraglayout(mDragLayout);  mDragLayout.setDragStatusListener(new OnDragStatusChangeListener() {   @Override   public void onOpen() {    Utils.showToast(MainActivity.this, "onOpen");    // 左面板ListView随机设置一个条目    Random random = new Random();    int nextInt = random.nextInt(50);    mLeftList.smoothScrollToPosition(nextInt);   }   @Override   public void onDraging(float percent) {    Log.d(TAG, "onDraging: " + percent);// 0 -> 1    // 更新图标的透明度    // 1.0 -> 0.0    ViewHelper.setAlpha(mHeaderImage, 1 - percent);   }   @Override   public void onClose() {    Utils.showToast(MainActivity.this, "onClose");    // 让图标晃动//    mHeaderImage.setTranslationX(translationX)    ObjectAnimator mAnim = ObjectAnimator.ofFloat(mHeaderImage, "translationX", 15.0f);    mAnim.setInterpolator(new CycleInterpolator(4));    mAnim.setDuration(500);    mAnim.start();   }  });  mLeftList.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, Cheeses.sCheeseStrings){   @Override   public View getView(int position, View convertView, ViewGroup parent) {    View view = super.getView(position, convertView, parent);    TextView mText = ((TextView)view);    mText.setTextColor(Color.WHITE);    return view;   }  });  mMainList.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, Cheeses.NAMES))   }}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表