Android仿QQ消息提示点拖拽功能
程序员文章站
2022-03-23 13:53:44
很久以前,发现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); }
代码分析就到这里,实现这个功能的核心代码都在这里。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。