当前位置: 移动技术网 > IT编程>移动开发>Android > ViewDragHelper实现QQ侧滑效果

ViewDragHelper实现QQ侧滑效果

2019年07月24日  | 移动技术网IT编程  | 我要评论

兔子补药,lol琴瑟仙女的新春福袋,张晓风散文

前言

       侧滑的实现方式有很多方式来实现,这次总结的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))  
 }
}

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网