高仿qq‘一键下班’—让你的view‘黏’起来

博客介绍了如何实现QQ聊天消息数view的拖动效果,类似于'一键下班'功能。通过分析QQ布局和事件分发机制,利用自定义View实现了消息view在拖动时的黏性移动、回弹动画以及手指抬起后的不同响应。详细讨论了关键的实现细节和绘制逻辑,提供了实现思路。
摘要由CSDN通过智能技术生成

qq手机客户端自5.0起有一个‘一键下班’的功能,qq聊天的消息数view可以拖拽,有一种黏黏的视觉效果,让手机控件更加生动,也增加了交互时的趣味性。最近在学习自定义控件的知识,所以试着实现了一下这个功能,来看看整体的一个预览效果:
这里写图片描述
然后看一下view的拖动特写:
这里写图片描述

主要要实现的功能:

显示消息的view被手指按住的时候随着手指移动而移动,如果触点和原位置的距离在某个距离A内,移动的view和原位置之间仍然有些‘黏黏的东西’黏住,如果距离大于A,view随手指移动而没有中间的连接部分。然后是手指抬起的情况,如果手指抬起的点到原位置距离小于距离B(B小于A),view会立即回到初始位置,这个时候如果连接还没有断开,view也会回到初始位置但是会有一个回弹的效果,如果手指抬起点到原位置距离大于B,view就不会回到原位置,且在手指抬起的位置有一个爆炸的效果。如果移除了总消息数的view,消息列表的所有view都会在原位置一一爆炸消失。

下边分析一下实现逻辑:

工具分析下qq地布局,这个显示消息数的view是Textview
这里写图片描述,但是拖动的时候是这个样子的:
这里写图片描述
发现这个textview消失了,可见拖动的时候的绘制不是发生在这个textview上的,当然拖动发生得时候view的形状改变也不好处理。
又看了下整个布局的父控件
这里写图片描述
我靠,居然是复写的view,而且还是个容器view,直觉里边有海量的代码,虽然ViewGroup可以聊作参考,但是这又得花费哥们儿大量时间,而且有点偏离目标,只好另辟蹊径。
那么重点回到了刚才拖动的时候textview的消失,既然这玩意消失了,那么拖动的是什么鬼?直觉是海量代码容器view里边对这个拖动事件做了处理,但是我暂时还不太好复写这样一个view,于是我想到了一个替代品,使用一个占满整个布局的一个子view来代替。按住的时候隐藏textview,同时让这个替代的view显示,并接着处理以后的事件。但是有个问题当手指移出这个textview按在其他控件上的时候又会被别的控件把手指的事件拦截掉了。所以这个事件的处理应该是在最最开始就被处理掉,这个由涉及到了Android的事件分发机制,这个参考一个很直观的介绍博客事件分发,所以对事件的处理就放在了activity的dispatchTouchEvent方法中。
那么问题来了怎么判定控件触点是不是落在view内,这用到了View类的一个方法getLocationInWindow,传入一个长度为2的数组,调用之后会得到view的位置的横纵坐标,控件的宽高又可以get得到,所以就可以判断触点是不是落在了这个view内部,决定要不要做接下来的处理。

下边讲一下关键的实现细节

绘制逻辑参考了这篇文章QQ手机版 5.0“一键下班”设计小结,这个主要是一些高中几何的知识,绘制的API可以参考一下aige的自定义控件专栏(强烈推荐),也可以看一下稍后给出的demo。

SnotView的一些主要属性

    private final long KICK_BACK_DURATION = 200;// 鼻涕回弹的时长 单位ms
    private final int BOOM_DURATION = 300;// 爆炸效果时长
    public float oriX, oriY;// “钉住”的鼻涕部分的中心点
    private int oriR;// “钉住”的鼻涕部分的中心点

    public int MAX_DISTANCE;// 最大距离 超过这个距离鼻涕被扯断

    private float fingerX, fingerY;// 手指按住的点 坐标
    private int fingerR;// 拖出来的园的半径
    private int snotColor;// 鼻涕的颜色 
    private Paint snotPaint;// 鼻涕画笔

    private Paint textPaint;// 文字画笔
    private int textColor;// 文字颜色
    private String text;// 文字内容

    private double newR;// 鼻涕被拖动时候 钉住部分的半径 变化的
    private double dist;// 手指和钉住点之间的距离

    // newR变化区间
    private int oriRMax;// “钉住”的鼻涕部分的最大半径
    private int oriRMin;// “钉住”的鼻涕部分做小半径
    private float textSize;// 文字的大小

    // 手指松开的一刻记录的坐标
    private float recordX;
    private float recordY;//

    private double SAFE_DISTANCE;// 安全距离
    volatile boolean hasCut;// 鼻涕是不是被扯断
    private 
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值