首页 > 系统 > Android > 正文

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

2019-10-21 21:26:29
字体:
来源:转载
供稿:网友

很久以前,发现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);    }

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

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


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表