如何仿qq聊天功能android,Android仿QQ消息提示点拖拽功能

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

不多说 先上效果

f3071721a92f519690e88734e9fd2730.gif

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

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.

*

* 一个可以随意拖动的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 {

@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的区别

5ab5f7705d575a4e5eee6538c6f067f8.png

getX和getY 是相对于view自身的,getRawX和getRawY是像对屏幕的,这里还要扣除掉状态栏的高度。

2.移动

windowManager.updateViewLayout(iv, mWindowLayoutParams);

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

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

class MyTypeEvaluator implements TypeEvaluator {

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

}

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值