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

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

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

(0)

相关推荐

  • Android仿qq消息拖拽效果

    本文实例为大家分享了Android仿qq消息拖拽效果展示的具体代码,供大家参考,具体内容如下 这是一个仿qq消息拖拽效果,View和拖拽实现了分离,TextView.Button.Imageview等都可以实现相应的拖拽效果:在触发的地方调用 MessageBubbleView.attach(findViewById(R.id.text_view), new MessageBubbleView.BubbleDisappearListener() { @Override public void d

  • Android贝塞尔曲线初步学习第二课 仿QQ未读消息气泡拖拽黏连效果

    上一节初步了解了Android端的贝塞尔曲线,这一节就举个栗子练习一下,仿QQ未读消息气泡,是最经典的练习贝塞尔曲线的东东,效果如下 附上github源码地址:https://github.com/MonkeyMushroom/DragBubbleView 欢迎star~ 大体思路就是画两个圆,一个黏连小球固定在一个点上,一个气泡小球跟随手指的滑动改变坐标.随着两个圆间距越来越大,黏连小球半径越来越小.当间距小于一定值,松开手指气泡小球会恢复原来位置:当间距超过一定值之后,黏连小球消失,气泡小球

  • android自定义view之模拟qq消息拖拽删除效果

    这个模拟功能的实现主要依靠了PATH和二阶贝塞尔曲线.首先上一张图来简单看一下: 这个模拟功能有以下几个特点: 在开始的时候点击圆以外的区域不会触发拖动事件 点击圆的时候可以拖拽,此时会有一个拉伸效果,连接大圆和小圆 拉伸到一定距离(自己设定)以后两个圆会断开,此时即使再拖拽进距离之内的时候也不会再产生已经断开的连接 在距离之内松手的时候会回弹会原位置,并伴有一个弹跳动画 介绍了这么多,看过我前边文章的朋友应该会有一个基本思路. 暴露接口 这个模拟功能共分为三部分,一个是那个小圆,固定的位置,一

  • Android贝塞尔曲线实现消息拖拽消失

    写在前头 写消息拖拽效果的文章不少,但是大部分都把自定义View写死了,我们要实现的是传入一个View,每个View都可以实现拖拽消失爆炸的效果,当然我也是站在巨人的肩膀上来学习的.但个人觉得程序员本就应该敢于学习和借鉴. 源码地址:源码Github地址 效果图 分析(用到的知识点):  (1)ValueAnimator (数值生成器) 用于生成数值,可以设置差值器来改变数字的变化幅度. (2)ObjectAnimator (动画生成器) 用于生成各种属性,布局动画,同样也可以设置差值器来改变效

  • Android仿QQ未读消息--红点拖拽删除【源代码】

    本Demo是一款仿qq未读消息拖拽删除的例子,继承RelativeLayout的WaterDrop实现了圆形图标功能.继承ImageView的CircleImageView圆形图片功能.效果非常不错.很适合有圆形设计的哥们.效果图片如下 CircleImageView核心代码 private void updateShaderMatrix() { float scale; float dx = 0; float dy = 0; mShaderMatrix.set(null); if (mBitm

  • Android使用贝塞尔曲线仿QQ聊天消息气泡拖拽效果

    本文实例为大家分享了Android仿QQ聊天消息气泡拖拽效果展示的具体代码,供大家参考,具体内容如下 先画圆,都会吧.代码如下: public class Bezier extends View { private final Paint mGesturePaint = new Paint(); private final Path mPath = new Path(); private float mX1 = 100, mY1 = 150; private float mX2 = 300, m

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

    很久以前,发现QQ有一个很有趣的功能,就是未读消息的红点是可以拖拽的,而且在任何地方都可以随意拖拽,并且有一个弹性的动画,非常有趣,而且也是一个非常方便的功能,于是总想仿制一个,虽说仿制,但也只是他的拖拽功能,弹性效果还是能力有限. 不多说 先上效果 一个自定义的view 使用方式也很简单 <com.weizhenbin.show.widget.VanishView android:layout_width="30dp" android:layout_height="3

  • Android仿QQ消息提示实现弹出式对话框

    本文在<7种形式的Android Dialog使用实例>在这篇文章的基础进行学习,具体内容如下 1.概述 android原生控件向来以丑著称(新推出的Material Design当另说),因此几乎所有的应用都会特殊定制自己的UI样式.而其中弹出式提示框的定制尤为常见,本篇我们将从模仿QQ退出提示框来看一下常见的几种自定义提示框的实现方式. 这里使用的几种弹出框实现方法概括为以下几种: 自定义Dialog 自定义PopupWindow 自定义Layout View Activity的Dialo

  • Android 仿微信发动态九宫格拖拽、删除功能

    1.完美1比1 仿照微信仿微信发动态 九宫格拖拽.删除 暴力拖拽ui有点问题,不影响使用,资源文件自己找个+号 2.微信发动态拖拽bug 当选择完图片,长按图片拖拽过程中按下屏幕home键盘,再次进入这时候就不能点击输入文字,点击输入文字的时候会触发选择相册事件 3.拖拽事件用的basequickadapter implementation 'com.android.support:recyclerview-v7:28.0.0' implementation "com.github.CymCha

  • jQuery的ztree仿windows文件新建和拖拽功能的实现代码

    前面的话:zTree 是一个依靠 jQuery 实现的多功能 "树插件".优异的性能.灵活的配置.多种功能的组合是 zTree 最大优点.专门适合项目开发,尤其是 树状菜单.树状数据. ztree官方文档:http://www.treejs.cn/v3/api.php 想要实现的效果: 需要的功能: 1:首先实现一颗jQuery的ztree的树形菜单,这个很简单,直接引用官方文档即可 2:点击新建组的按钮,会出现一个input对话框,填写想要新建的名称,在树形菜单上添加了一个父节点菜单

  • Android仿QQ个人标签添加与删除功能

    最近在公司项目开发中,有一个类似于QQ个人标签的需求要完成,具体包括个人标签的添加,删除,添加过程中重复的标签会提示用户,不能够进行添加.先给大家看一下效果图. 点击标签按钮,弹出标签选择的页面,显示所有的标签:点击全部标签里面的item,选择的标签会显示在上方:再次点击相同的标签进行添加,会提示用户"标签已存在,请重新添加":点击上方已经选择好的标签,进行删除操作 业务逻辑就是这样,下面是具体实现过程: 一.界面布局: <LinearLayout android:layout_

  • Android仿QQ附近的人搜索展示功能

     1.概述 老规矩,先上图 原装货(就不录制gif了,大家可以自己在Q群助手开启共享地理位置,返回群聊天页面就看到看到附近的人): 看起来还是挺像的吧. 通过观察,我们可以获取得到如下关系 1.下面展示列表我们可以使用ViewPager来实现(当然如果你不觉得麻烦,你也可以用HorizontalScrollView来试试) 2.上面的扫描图,肯定是个ViewGroup(因为里面的小圆点是可以点击的,如果是View的话,对于这些小圆点的位置的判断,以及对小圆点缩放动画的处理都会超级麻烦,难以实现)

  • Android仿QQ首页ListView左滑置顶、删除功能

    Android 仿QQ首页ListView左滑置顶.删除等实现源码,具体内容如下 效果图 实现源码:package com.duguang.baseanimation.ui.listivew.deletelistview; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.

  • Android开发实现仿QQ消息SwipeMenuListView滑动删除置顶功能【附源码下载】

    本文实例讲述了Android开发实现仿QQ消息SwipeMenuListView滑动删除置顶功能.分享给大家供大家参考,具体如下: 一.先来效果图 二.实现步骤: 1. 在项目build.gradle里面添加包 compile 'com.baoyz.swipemenulistview:library:1.3.0' 2. xml布局文件 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  • Android 实现带角标的ImageView(微博,QQ消息提示)

    角标绘制过程:用画笔量出一个字符的宽度作为角标背景的半径(R),然后判断传入字符串的总长度 如果只有一位字符:那么就以 R 为半径,画一个圆,然后在圆中写上数字 如果有两位以上的字符,就不能单纯用一个圆了,用画笔测量字符串的完整长度( len ),然后在右上角画一个圆,在这个圆的圆心左边 len 长度的位置 作为圆心再画一个圆,最后以这个两个圆的上下顶点(一共四个)构成一个矩形,进行填充 源码地址:https://github.com/SiKang123/AndroidToolBox 效果如下:

随机推荐