欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

ViewDragHelper实现QQ侧滑效果

程序员文章站 2023-12-10 14:54:58
前言        侧滑的实现方式有很多方式来实现,这次总结的viewdraghelper就是其中一种方式,v...

前言

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

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