Android -- 浮动组建
在开发Android应用时,加新功能是必不可少的,我们加入了新的功能,有的一看界面就可以看出来,但是有的新功能就比较隐蔽,也就是用户很难知道你添加了这个新功能,这个时候就需要用户在打开我们的应用时给出一些提示,说明我们在哪里添加了新功能,点击哪里可以看到这个新功能。这时我们第一时间想到的可能是Toast,因为它用法简单,又不影响用户操作,但是它有个缺点,就是不能明确的指示是哪里添加了新功能,除非你用文字描述出来。
基本思路
- 首先你要有一个处理好的9 PNG的图片,用于自适应文字显示,关于9 PNG处理可以参考Android Doc
- 要显示在哪个View的下面,就要知道这个目标View的位置
- 把要显示的文本放在一个TextView里,使用Toast的setView方法设置Toast要显示的View。
- 根据得到的位置,最后就是使用Toast的setGravity方法把要显示的内容放到正确的位置显示出来即可。
总的来说首先就是要知道目标View,根据targetView计算出要显示提示的位置,然后根据位置使用Toast把提示的文本显示出来。但是这里有几个难点,下面就一一解决
Activity加载完成时获取targetVIew的宽高和位置属性
我们加入了新的功能提示,自然会在用户打开这个界面的时候就提示,但是在UI没有渲染完成绑定倒Window上的时候,是不能获取倒targetView的width、height和position的,那么我们怎么才能知道targetView的这些属性呢?Activity的onAttachedToWindow回调方法是不能用的,况且它是在API 5加上的,以前的API中并没有。不过我们还有一种方法,那就是在显示提示的时候获取targetView的属性,如果获取不到(为0)就一直获取,直到获取到为止,这其实是一个轮询。为了达到这一目的,我们在开发者调用FloatTextToast.show()的时候使用Android的Message机制轮询获取一个targetView的属性,如果获取到,就会显示提示文字了。在此之前先看下FloatTextToast构造函数,可以对它有个大概的了解,防止后面的代码中出现的成员变量不认识。
/**
* 私有的构造函数
*
* @param context
* 上下文
* @param targetView
* 目标view
* @param content
* 内容
* @param time
* 显示时间
*/
private MyToast(Context context, View targetView, String content, int time) {
this.targetView = targetView;
this.context = context;
this.content = content;
this.time = time;
// 初始化Toast
toast = new Toast(this.context);
textView = new TextView(this.context);
textView.setTextColor(Color.BLACK);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
textView.setBackgroundResource(R.drawable.float_text_toast_bg);
textView.setText(this.content);
toast.setView(textView);// 设置背景 toast.setDuration(time);// 设置时间
// 开始handlerthread
handlerThread = new HandlerThread("MyToast");
handlerThread.start();
// 构造一个自己的looper
handler = new MyHandler(handlerThread.getLooper());
}自定义自己的消息循环机制
要想在一个自定义的组件中使用Message机制,一定要有自己的Looper机制,我们不能使用Activity的Looper,因为主Looper可能会有其他的Message需要处理,这就会导致我们的show方法会延迟调用,这样效果就不好了,所以要有一个专门的Looper来处理此Message。要声明自己的Looper,就需要HandlerThread这个类的配合了,这可是个好东西,使用它你会很容易的创建一个自己的线程用于处理你Message。使用方法很简单,如下代码:
// 开始handlerthread
handlerThread = new HandlerThread("MyToast");
handlerThread.start();
// 构造一个自己的looper
handler = new MyHandler(handlerThread.getLooper());这样就声明了一个HandlerThread并且让它运行,运行之后我们就可以获取一个属于该Thread的Looper,然后把Message发送给这个Looper,那么这个线程就可以处理你发送的消息了。。看看我们的自定义Handler
/**
* 自定义的Handler
*
* @author sansung
*
*/
class MyHandler extends Handler { public MyHandler(Looper looper) {
super(looper);
// TODO 自动生成的构造函数存根
} @Override
public void handleMessage(Message msg) {
// TODO 自动生成的方法存根
super.handleMessage(msg);
switch (msg.what) {
case WHAT_SHOW:
showInHandler();
break;
default:
break;
}
} }它需要传递一个Looper作为构造参数声明,意思就是使用这个Looper处理我发send的Message的意思。上面的代码
handler = new MyHandler(handlerThread.getLooper());
正是我们使用自己开启的线程处理我们的Message的意思。下面看下我们的showInHandler()方法是怎么处理的。
/**
* Handler调用的show方法,主要为了等待targetView的位置
* 如果targetView的位置没有得到,handler looper继续循环获取
*/
private void showInHandler() {
int[] targetPos = getTargetViewPos();
if(targetPos[0]==0&&targetPos[1]==0){
handler.sendEmptyMessageDelayed(WHAT_SHOW, 100);
}else{
final Rect contentPos=getSize(targetPos);
toast.setGravity(Gravity.LEFT|Gravity.TOP, contentPos.left, contentPos.top);
toast.show();
}
}该方法其实就是在获取targetVIew的位置,如果获取不到,则向自定义的Looper里发送一个Message重新调用该函数,如果得到了位置,那么就调用Toast的setGravity方法设置好要显示文本的位置,然后显示即可。
获取要显示文本的位置
要获取显示的位置,就要知道targetVIew的位置以及它的宽、高,这样就能计算要显示文本的位置了。View组件都有一个函数,可以把自己在Window里的坐标转换为一个数组。
/**
* 得到目标View的位置
* @return
*/
private int[] getTargetViewPos() {
final int[] targetPos = new int[2];
targetView.getLocationInWindow(targetPos);
return targetPos;
}这样,就返回了targetView的xy坐标了。光有targetView的坐标还不够,还要有contentView最终要显示的位置。
/**
* 计算获取浮动文本显示的位置,把浮动文本放在targetView的中心处
*
* @param targetPos
* @return 一个包含top和left的Rect
*/
private Rect getSize(int[] targetPos) {
final Rect windowVisibleRect = new Rect();
final View targetView = this.targetView;
final TextView contentView = textView;
// 状态栏高度
targetView.getWindowVisibleDisplayFrame(windowVisibleRect);
int statusBarHeight = windowVisibleRect.top;
// 背景图那个三角箭头的位置
final TextPaint textPaint = contentView.getPaint();
int contentW = (int) textPaint.measureText((String) contentView
.getText());
int arrowPos = (int) (contentW * (30.0 / 160)); final Rect rect = new Rect();
rect.left = targetPos[0] + targetView.getWidth() / 2 - arrowPos;
rect.top = targetPos[1] - statusBarHeight + targetView.getHeight();
return rect;
}这个函数的功能就是让文本显示在targetView的下方的横向中间的位置,也就是文本的背景尖角三角要指向targetView横向中间的位置,这样才好看些。为了这个才需要使用Paint测量文本的宽度,所以这也是该组件的一个缺陷,不能显示String格式之外的字符,比如SpannableString。
完整的组件代码
上面是对组件代码的拆分讲解,是为了说明我们当时实现这个组件的想法以及步骤,下面就整体把代码列出来,明了的看一下。
public class MyToast {
private static final int WHAT_SHOW = 1;
private View targetView = null;// 目标view
private Context context = null;// Toast的上下文
private Toast toast = null;// 显示的土司
private String content = null;// 土司显示内容
private TextView textView = null;// 土司中的textview
private int time = 0;// 土司显示时间
private HandlerThread handlerThread = null;// handlerThread
private static MyToast myToast = null;
private Handler handler = null; /**
* 私有的构造函数
*
* @param context
* 上下文
* @param targetView
* 目标view
* @param content
* 内容
* @param time
* 显示时间
*/
private MyToast(Context context, View targetView, String content, int time) {
this.targetView = targetView;
this.context = context;
this.content = content;
this.time = time;
// 初始化Toast
toast = new Toast(this.context);
textView = new TextView(this.context);
textView.setTextColor(Color.BLACK);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
textView.setBackgroundResource(R.drawable.float_text_toast_bg);
textView.setText(this.content);
toast.setView(textView);// 设置背景 toast.setDuration(time);// 设置时间
// 开始handlerthread
handlerThread = new HandlerThread("MyToast");
handlerThread.start();
// 构造一个自己的looper
handler = new MyHandler(handlerThread.getLooper());
} /**
* 显示Toast
*
* @param context
* 上下文
* @param targetView
* 目标view
* @param content
* 内容
* @param time
* 显示时间
*/
public static MyToast makeText(Context context, View targetView,
String content, int time) {
myToast = new MyToast(context, targetView, content, time);
return myToast; } public void show() {
handler.sendEmptyMessage(WHAT_SHOW);
} /**
* 计算获取浮动文本显示的位置,把浮动文本放在targetView的中心处
*
* @param targetPos
* @return 一个包含top和left的Rect
*/
private Rect getSize(int[] targetPos) {
final Rect windowVisibleRect = new Rect();
final View targetView = this.targetView;
final TextView contentView = textView;
// 状态栏高度
targetView.getWindowVisibleDisplayFrame(windowVisibleRect);
int statusBarHeight = windowVisibleRect.top;
// 背景图那个三角箭头的位置
final TextPaint textPaint = contentView.getPaint();
int contentW = (int) textPaint.measureText((String) contentView
.getText());
int arrowPos = (int) (contentW * (30.0 / 160)); final Rect rect = new Rect();
rect.left = targetPos[0] + targetView.getWidth() / 2 - arrowPos;
rect.top = targetPos[1] - statusBarHeight + targetView.getHeight();
return rect;
}
/**
* Handler调用的show方法,主要为了等待targetView的位置
* 如果targetView的位置没有得到,handler looper继续循环获取
*/
private void showInHandler() {
int[] targetPos = getTargetViewPos();
if(targetPos[0]==0&&targetPos[1]==0){
handler.sendEmptyMessageDelayed(WHAT_SHOW, 100);
}else{
final Rect contentPos=getSize(targetPos);
toast.setGravity(Gravity.LEFT|Gravity.TOP, contentPos.left, contentPos.top);
toast.show();
}
}
/**
* 得到目标View的位置
* @return
*/
private int[] getTargetViewPos() {
final int[] targetPos = new int[2];
targetView.getLocationInWindow(targetPos);
return targetPos;
} /**
* 自定义的Handler
*
* @author sansung
*
*/
class MyHandler extends Handler { public MyHandler(Looper looper) {
super(looper);
// TODO 自动生成的构造函数存根
} @Override
public void handleMessage(Message msg) {
// TODO 自动生成的方法存根
super.handleMessage(msg);
switch (msg.what) {
case WHAT_SHOW:
showInHandler();
break;
default:
break;
}
} } }此组件和Toast的实现方法一样,所以上手不难,只需使用makeText静态方法生成一个即可
MyToast.makeText(MainActivity.this, v, "12222222222222221111111111", 1).show();
我是天王盖地虎的分割线
源代码:http://pan.baidu.com/s/1dD1Qx01
自定义Toast.zip
参考:http://flysnow.iteye.com/blog/1760962
Android -- 浮动组建的更多相关文章
- Android浮动窗口的实现
1.浮动窗口的实现原理 看到上图的那个小Android图标了吧,它不会被其他组建遮挡,也可以响应用户的点击和拖动事件,它的显示和消失由WindowManager直接管理,它就是Android浮动窗口. ...
- android浮动搜索框
android浮动搜索框的配置比较繁琐,需要配置好xml文件才能实现onSearchRequest()方法. 1.配置搜索的XML配置文件,新建文件searchable.xml,保存在res/xml ...
- 用Xamarin 实现园友的 :Android浮动小球与开机自启动
原文:用Xamarin 实现园友的 :Android浮动小球与开机自启动 前两天看园子里有筒子写了个 Android浮动小球与开机自启动 , 感觉这种被 360 玩烂的功能原来是如此的简单啊... ...
- Android浮动小球与开机自启动
看着手机上的360浮动小球,不评价其具体的功能与实用性,至少在UI设计与交互方面是个不小的创新. 如图片左上角所示,球中还会显示当前手机的运行状况,向下拉动还会有弹射来达到加速.清理等目的. 那好,先 ...
- 总结如何实现Android浮动层,主要是dialog的使用
自定义一个类继承自Dialog类,然后在构造方法中,定义这个dialog的布局和一些初始化信息. 查看源码打印? 01 public class MenuDialog extends Dialog { ...
- Android 浮动窗口进阶——画中画,浮动视频(附Demo)
今天继续上一篇Android顶层窗口.浮动窗口的进阶应用.上一篇主要讲解了WindowManager服务和如何使用WindowManager编写一个顶层窗口.今天主要是讲讲如何在顶层窗口里面播放视频, ...
- 浅谈Android四大组建之一Service---Service与Activity的绑定
从上一篇文章我们学会了如何创建Service,我们通过监听一个按钮,然后再按钮里面通过意图来启动Service.但是我们有没有发现,启动服务以后,Activity和Service之间的联系好像就断开了 ...
- Android 浮动按钮的伸缩效果
在做项目时想增加点动感,于是就有如下效果: 实现起来也很简单,通过属性动画和recyclerview 滑动结合就很好实现了. 通过给recycleview添加一个滑动监听:通过滚动的差值来处理动画 m ...
- android 浮动窗体学习笔记及个人理解(仿360手机助手)
很感谢原文作者 http://blog.csdn.net/guolin_blog/article/details/8689140 经自己理解 程序执行界面例如以下图: 1.程序入口界面 2.小浮动窗体 ...
随机推荐
- 调试时屏蔽JavaScript库代码 –Chrome DevTools Blackbox功能介绍
代码难免会有Bug,每次我们在Chrome调试代码时,总是会进入各种各样的库代码(比如jQuery.Zepto),但实际上很多时候我们并不希望这样,要是能把这些库代码“拉黑”多好啊. 广大码农喜闻乐见 ...
- Android IOS WebRTC 音视频开发总结(三四)-- windows.20150706
最近好不容易更新了PC版的WEBRTC,总结下有哪些调整,文章来自博客园RTC.Blacker,支持原创,转载请说明出处. 图1:解决方案工程结构对比: 说明: 1, 最大的调整就是移除了VideoE ...
- sql server 分组后字段拼接
- C# DataGridViewComboBoxColumn 数据绑定
dataGridView1.Columns.Clear(); dataGridView1.AutoGenerateColumns = false; dataGridView1.DataSource = ...
- [leetcode]_Roman to Integer
题目:给定一个罗马数字串,转换为一个整数. 一开始没理解,以为是string to int.后来理解:罗马数字与阿拉伯数字的映射关系,见下图: 至此,题目的意思才掌握明白,用程序模拟这张表. 无可置否 ...
- 原生jdbc执行存储过程
//定时任务,结转 . //表名 fys_sch_lvyou2 ,存储过程名:fys_sch_lvyou2_carrayover //无参调用:{call insertLine} //有参调用:{ca ...
- Android环境配置Sencha Touch
转自http://www.phonegap100.com/portal.php?mod=view&aid=19 作为你开发的一部分,为安卓设备开发的 Sencha Touch框架应该在安卓虚拟 ...
- a 标签 跳转4种类型
<a href='' target=''>中的target有4种参数: '_self' , '_parent' , '_top' 和 '_blank' 在没有使用框架 ...
- C基础 北京大公司面试简单总结
作者有话说 这是关于程序员面试的一篇文章, 希望对你有帮助. 干了快3年了. 可以简单参考, 对比总结.虽然本人很水. 很喜欢当前做的手游项目.做的很认真.后端每个人技术都很好.但是结果都不如意.在死 ...
- hdu 1548 A strange lift
题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1548 A strange lift Description There is a strange li ...