前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android仿QQ消息提示点拖拽功能

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

作者头像
砸漏
发布2020-11-04 11:14:14
7030
发布2020-11-04 11:14:14
举报
文章被收录于专栏:恩蓝脚本

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

不多说 先上效果

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

代码语言:javascript
复制
<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"/ 

然后先看下源码

代码语言:javascript
复制
**
* 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

代码语言:javascript
复制
/**
* 构建一个窗口 用于存放和移动镜像
* */
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.移动

代码语言:javascript
复制
windowManager.updateViewLayout(iv, mWindowLayoutParams);

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

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

代码语言:javascript
复制
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.触发消失的时机

代码语言:javascript
复制
/**
* 计算两点的距离
*/
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;
}

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

代码语言:javascript
复制
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);
}

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-09-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档