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

Android仿QQ消息提示点拖拽功能

程序员文章站 2022-03-23 13:53:44
很久以前,发现qq有一个很有趣的功能,就是未读消息的红点是可以拖拽的,而且在任何地方都可以随意拖拽,并且有一个弹性的动画,非常有趣,而且也是一个非常方便的功能,于是总想仿制...

很久以前,发现qq有一个很有趣的功能,就是未读消息的红点是可以拖拽的,而且在任何地方都可以随意拖拽,并且有一个弹性的动画,非常有趣,而且也是一个非常方便的功能,于是总想仿制一个,虽说仿制,但也只是他的拖拽功能,弹性效果还是能力有限。

不多说 先上效果

Android仿QQ消息提示点拖拽功能

一个自定义的view 使用方式也很简单

<com.weizhenbin.show.widget.vanishview
  android:layout_width="30dp"
  android:layout_height="30dp"
  android:text="5"
  android:layout_alignparentbottom="true"
  android:gravity="center"
  android:textcolor="#fff"
  android:id="@+id/vv"
  android:layout_marginbottom="35dp"
  android:layout_marginleft="80dp"
  android:background="@drawable/shape_red_bg"/>

然后先看下源码

**
 * created by weizhenbin on 16/6/1.
 * <p/>
 * 一个可以随意拖动的view
 */
public class vanishview extends textview {
 private context context;
 /**窗口管理器*/
 private windowmanager windowmanager;

 /**用来存储镜像的imageview*/
 private imageview iv;

 /** 状态栏高度*/
 private int statusheight = 0;

 /**按下的坐标x 相对于view自身*/
 private int dx = 0;

 /**按下的坐标y 相对于view自身*/
 private int dy = 0;

 /**镜像bitmap*/
 private bitmap tmp;

 /**按下的坐标x 相对于屏幕*/
 private float downx = 0;

 /**按下的坐标y 相对于屏幕*/
 private float downy = 0;

 /**属性动画 用于回弹效果*/
 private valueanimator animator;

 /**窗口参数*/
 private windowmanager.layoutparams mwindowlayoutparams;

 /**接口对象*/
 private onlistener listener;
 public vanishview(context context) {
  super(context);
  init(context);
 }

 public vanishview(context context, attributeset attrs) {
  super(context, attrs);
  init(context);
 }

 public vanishview(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
  init(context);
 }

 private void init(context context) {
  this.context = context;
  windowmanager = ((activity) context).getwindowmanager();
  statusheight = getstatusheight(context);
 }

 @override
 public boolean ontouchevent(motionevent event) {
  switch (event.getaction()) {
   case motionevent.action_down:
    dx = (int) event.getx();
    dy = (int) event.gety();
    downx = event.getrawx();
    downy = event.getrawy();
    addwindow(context, event.getrawx(), event.getrawy());
    setvisibility(invisible);
    break;
   case motionevent.action_move:
    mwindowlayoutparams.x = (int) (event.getrawx() - dx);
    mwindowlayoutparams.y = (int) (event.getrawy() - statusheight - dy);
    windowmanager.updateviewlayout(iv, mwindowlayoutparams);
    break;
   case motionevent.action_up:
    int distance=distance(new mypoint(event.getrawx(), event.getrawy()), new mypoint(downx, downy));
    if(distance<400) {
     scroll(new mypoint(event.getrawx(), event.getrawy()), new mypoint(downx, downy));
    }else {
     if(listener!=null){
      listener.ondismiss();
     }
     windowmanager.removeview(iv);
    }
    break;
  }
  return true;
 }

 /**
  * 构建一个窗口 用于存放和移动镜像
  * */
 private void addwindow(context context, float downx, float dowmy) {
  mwindowlayoutparams = new windowmanager.layoutparams();
  mwindowlayoutparams.width = windowmanager.layoutparams.wrap_content;
  mwindowlayoutparams.height = windowmanager.layoutparams.wrap_content;
  iv = new imageview(context);
  mwindowlayoutparams.format = pixelformat.rgba_8888;
  mwindowlayoutparams.gravity = gravity.top | gravity.left;
  mwindowlayoutparams.x = (int) (downx - dx);
  mwindowlayoutparams.y = (int) (dowmy - statusheight - dy);
  //获取view的镜像bitmap
  this.setdrawingcacheenabled(true);
  tmp = bitmap.createbitmap(this.getdrawingcache());
  //释放缓存
  this.destroydrawingcache();
  iv.setimagebitmap(tmp);
  windowmanager.addview(iv, mwindowlayoutparams);
 }


 /**
  * 使用属性动画 实现缓慢回弹效果
  * */
 private void scroll(mypoint start, mypoint end) {
  animator = valueanimator.ofobject(new mytypeevaluator(), start, end);
  animator.setduration(200);
  animator.addupdatelistener(new valueanimator.animatorupdatelistener() {
   @override
   public void onanimationupdate(valueanimator animation) {
    mypoint point = (mypoint) animation.getanimatedvalue();
    mwindowlayoutparams.x = (int) (point.x - dx);
    mwindowlayoutparams.y = (int) (point.y - statusheight - dy);
    windowmanager.updateviewlayout(iv, mwindowlayoutparams);
   }
  });
  animator.addlistener(new animatorlisteneradapter() {
   @override
   public void onanimationend(animator animation) {
    super.onanimationend(animation);
    windowmanager.removeview(iv);
    setvisibility(visible);
   }

  });
  animator.start();
 }

 /**
  * 计算两点的距离
  */
 private int distance(mypoint point1, mypoint point2) {
  int distance = 0;
  if (point1 != null && point2 != null) {
   float dx = point1.x - point2.x;
   float dy = point1.y - point2.y;
   distance = (int) math.sqrt(dx * dx + dy * dy);
  }
  return distance;

 }

 /**
  * 获取状态栏的高度
  */
 private static int getstatusheight(context context) {
  int statusheight = 0;
  rect localrect = new rect();
  ((activity) context).getwindow().getdecorview().getwindowvisibledisplayframe(localrect);
  statusheight = localrect.top;
  if (0 == statusheight) {
   class<?> localclass;
   try {
    localclass = class.forname("com.android.internal.r$dimen");
    object localobject = localclass.newinstance();
    int i5 = integer.parseint(localclass.getfield("status_bar_height").get(localobject).tostring());
    statusheight = context.getresources().getdimensionpixelsize(i5);
   } catch (exception e) {
    e.printstacktrace();
   }
  }
  return statusheight;
 }

 class mypoint {
  float x;
  float y;

  public mypoint(float x, float y) {
   this.x = x;
   this.y = y;
  }

  @override
  public string tostring() {
   return "mypoint{" +
     "x=" + x +
     ", y=" + y +
     '}';
  }
 }

 class mytypeevaluator implements typeevaluator<mypoint> {

  @override
  public mypoint evaluate(float fraction, mypoint startvalue, mypoint endvalue) {
   mypoint point = startvalue;
   point.x = startvalue.x + fraction * (endvalue.x - startvalue.x);
   point.y = startvalue.y + fraction * (endvalue.y - startvalue.y);
   return point;
  }
 }

 /**事件回调借口*/
 public interface onlistener{
  void ondismiss();
 }

 public void setlistener(onlistener listener) {
  this.listener = listener;
 }

实现这一功能其实也不难,这个功能涉及到以下几个知识点

使用windowmanager添加一个view
使用valueanimator属性动画实现回弹效果
getx和getrawx,gety和getrawy的区别

1.使用windowmanager添加一个view

 /**
  * 构建一个窗口 用于存放和移动镜像
  * */
 private void addwindow(context context, float downx, float dowmy) {
  mwindowlayoutparams = new windowmanager.layoutparams();
  mwindowlayoutparams.width = windowmanager.layoutparams.wrap_content;
  mwindowlayoutparams.height = windowmanager.layoutparams.wrap_content;
  iv = new imageview(context);
  mwindowlayoutparams.format = pixelformat.rgba_8888;
  mwindowlayoutparams.gravity = gravity.top | gravity.left;
  mwindowlayoutparams.x = (int) (downx - dx);
  mwindowlayoutparams.y = (int) (dowmy - statusheight - dy);
  //获取view的镜像bitmap
  this.setdrawingcacheenabled(true);
  tmp = bitmap.createbitmap(this.getdrawingcache());
  //释放缓存
  this.destroydrawingcache();
  iv.setimagebitmap(tmp);
  windowmanager.addview(iv, mwindowlayoutparams);
 }

这一步是为了投影一个镜像来达到拖动view的一个假像效果,使用imageview来显示。这里为了使投影没用偏移需要了解getx getrawx gety getrawy的区别

Android仿QQ消息提示点拖拽功能

getx和gety 是相对于view自身的,getrawx和getrawy是像对屏幕的,这里还要扣除掉状态栏的高度。

2.移动

 windowmanager.updateviewlayout(iv, mwindowlayoutparams);

3.使用valueanimator属性动画实现回弹效果

这里自定义了typeevaluator实现点的位移动画

class mytypeevaluator implements typeevaluator<mypoint> {

  @override
  public mypoint evaluate(float fraction, mypoint startvalue, mypoint endvalue) {
   mypoint point = startvalue;
   point.x = startvalue.x + fraction * (endvalue.x - startvalue.x);
   point.y = startvalue.y + fraction * (endvalue.y - startvalue.y);
   return point;
  }
 }


 /**
  * 使用属性动画 实现缓慢回弹效果
  * */
 private void scroll(mypoint start, mypoint end) {
  animator = valueanimator.ofobject(new mytypeevaluator(), start, end);
  animator.setduration(200);
  animator.addupdatelistener(new valueanimator.animatorupdatelistener() {
   @override
   public void onanimationupdate(valueanimator animation) {
    mypoint point = (mypoint) animation.getanimatedvalue();
    mwindowlayoutparams.x = (int) (point.x - dx);
    mwindowlayoutparams.y = (int) (point.y - statusheight - dy);
    windowmanager.updateviewlayout(iv, mwindowlayoutparams);
   }
  });
  animator.addlistener(new animatorlisteneradapter() {
   @override
   public void onanimationend(animator animation) {
    super.onanimationend(animation);
    windowmanager.removeview(iv);
    setvisibility(visible);
   }

  });
  animator.start();
 }

通过属性动画实现一个回弹效果

4.触发消失的时机

 /**
  * 计算两点的距离
  */
 private int distance(mypoint point1, mypoint point2) {
  int distance = 0;
  if (point1 != null && point2 != null) {
   float dx = point1.x - point2.x;
   float dy = point1.y - point2.y;
   distance = (int) math.sqrt(dx * dx + dy * dy);
  }
  return distance;
 }

计算两点之间的距离来触发一个回调事件。

int distance=distance(new mypoint(event.getrawx(), event.getrawy()), new mypoint(downx, downy));
    if(distance<400) {
     scroll(new mypoint(event.getrawx(), event.getrawy()), new mypoint(downx, downy));
    }else {
     if(listener!=null){
      listener.ondismiss();
     }
     windowmanager.removeview(iv);
    }

代码分析就到这里,实现这个功能的核心代码都在这里。

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