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

 更新时间:2019年01月27日 11:52:12   作者:CSDN_韦振斌  
这篇文章主要为大家详细介绍了Android仿QQ消息提示点拖拽功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

很久以前,发现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的区别

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);
    }

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

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

相关文章

  • Kotlin遍历集合导致并发修改异常的原因和解决方法

    Kotlin遍历集合导致并发修改异常的原因和解决方法

    这篇文章主要介绍了Kotlin遍历集合导致并发修改异常的原因和解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Android开发控制ScrollView滑动速度的方法

    Android开发控制ScrollView滑动速度的方法

    这篇文章主要介绍了Android开发控制ScrollView滑动速度的方法,结合实例形式分析了Android编程中ScrollView滑动事件相关操作技巧,需要的朋友可以参考下
    2017-02-02
  • Android多媒体应用使用MediaPlayer播放音频

    Android多媒体应用使用MediaPlayer播放音频

    这篇文章主要为大家详细介绍了Android多媒体应用使用MediaPlayer播放音频,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • Android通过AIDL在两个APP之间Service通信

    Android通过AIDL在两个APP之间Service通信

    这篇文章主要为大家详细介绍了Android通过AIDL在两个APP之间Service通信,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • Android Studio 3.0 新功能全面解析和旧项目适配问题

    Android Studio 3.0 新功能全面解析和旧项目适配问题

    Android Studio是Android的官方IDE。接下来通过本文给大家分享Android Studio 3.0 新功能全面解析和旧项目适配问题,需要的朋友可以参考下
    2017-11-11
  • Android NTP 时间同步机制详解

    Android NTP 时间同步机制详解

    这篇文章主要为大家介绍了Android NTP时间同步机制实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 详解Android应用中DialogFragment的基本用法

    详解Android应用中DialogFragment的基本用法

    Android App中建议使用DialogFragment作为对话框的容器,DialogFragment类提供了创建对话框并管理其外观需要的所有控件,本文主要内容便为详解Android应用中DialogFragment的基本用法,而不再需要调用Dialog的方法需要的朋友可以参考下
    2016-05-05
  • Android自定义开关按钮源码解析

    Android自定义开关按钮源码解析

    这篇文章主要为大家详细解析了Android自定义开关源码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Android 登录页面的实现代码(密码显示隐藏、EditText 图标切换、限制输入长度)

    Android 登录页面的实现代码(密码显示隐藏、EditText 图标切换、限制输入长度)

    这篇文章主要介绍了Android 登录页面的实现代码(密码显示隐藏、EditText 图标切换、限制输入长度),本文通过两种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • Android实现记事本功能(26)

    Android实现记事本功能(26)

    这篇文章主要为大家详细介绍了Android实现记事本功能的相关资料,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-02-02

最新评论