基于Android浮动组件 可用于应用中新功能展示等
16lz
2021-01-23
基于Android浮动组件 可用于应用中新功能展示等
[日期:2011-12-11] | 来源:Linux社区 作者:michael__li |
基于Android的浮动组件,可以用于应用中的新功能展示等等。
前言
在开发Android应用时,加新功能是必不可少的,我们加入了新的功能,有的一看界面就可以看出来,但是有的新功能就比较隐蔽,也就是用户很难知道你添加了这个新功能,这个时候就需要用户在打开我们的应用时给出一些提示,说明我们在哪里添加了新功能,点击哪里可以看到这个新功能。这时我们第一时间想到的可能是Toast,因为它用法简单,又不影响用户操作,但是它有个缺点,就是不能明确的指示是哪里添加了新功能,除非你用文字描述出来。为此,我基于Toast编写了一个小组件FloatTextToast(下面遇到的这个名字代替我写的这个组件),他和Toast的用法一样简单,并且弥补了Toast的缺点,也更显得更好看。 组件源代码和效果图的Demo下载免费下载地址在 http://linux.linuxidc.com/
用户名与密码都是www.linuxidc.com
具体下载目录在 /pub/Android源码集锦/2011年/12月/基于Android浮动组件 可用于应用中新功能展示等/
效果图
你可以学到
- Toast的基本用法
- Android的消息机制,如何创建自己的消息队列
- 怎样在Activity启动时获取一个View的width、height、top、left等属性
基本思路
- 首先你要有一个处理好的9 PNG的图片,用于自适应文字显示,关于9 PNG处理可以参考Android Doc
- 要显示在哪个View的下面,就要知道这个目标View的位置
- 把要显示的文本放在一个TextView里,使用Toast的setView方法设置Toast要显示的View。
- 根据得到的位置,最后就是使用Toast的setGravity方法把要显示的内容放到正确的位置显示出来即可。
Activity加载完成时获取targetVIew的宽高和位置属性
我们加入了新的功能提示,自然会在用户打开这个界面的时候就提示,但是在UI没有渲染完成绑定倒Window上的时候,是不能获取倒targetView的width、height和position的,那么我们怎么才能知道targetView的这些属性呢?Activity的onAttachedToWindow回调方法是不能用的,况且它是在API 5加上的,以前的API中并没有。不过我们还有一种方法,那就是在显示提示的时候获取targetView的属性,如果获取不到(为0)就一直获取,直到获取到为止,这其实是一个轮询。为了达到这一目的,我们在开发者调用FloatTextToast.show()的时候使用Android的Message机制轮询获取一个targetView的属性,如果获取到,就会显示提示文字了。在此之前先看下FloatTextToast构造函数,可以对它有个大概的了解,防止后面的代码中出现的成员变量不认识。- privateFloatTextToast(Contextcontext,ViewtargetView){
- this.mTargetView=targetView;
- this.mContext=context;
- mToast=newToast(mContext);
- mContentView=newTextView(mContext);
- mContentView.setBackgroundResource(R.drawable.float_text_toast_bg);
- mContentView.setTextColor(Color.BLACK);
- mContentView.setTextSize(TypedValue.COMPLEX_UNIT_DIP,16);
- mToast.setView(mContentView);
- //初始化一个Handler线程
- mHandlerThread=newHandlerThread("FloatTextToast");
- mHandlerThread.start();
- mHandler=newFloatTextToastHandler(mHandlerThread.getLooper());
- }
自定义自己的消息循环机制
要想在一个自定义的组件中使用Message机制,一定要有自己的Looper机制,我们不能使用Activity的Looper,因为主Looper可能会有其他的Message需要处理,这就会导致我们的show方法会延迟调用,这样效果就不好了,所以要有一个专门的Looper来处理此Message。要声明自己的Looper,就需要HandlerThread这个类的配合了,这可是个好东西,使用它你会很容易的创建一个自己的线程用于处理你Message。使用方法很简单,如下代码:- //初始化一个Handler线程
- mHandlerThread=newHandlerThread("FloatTextToast");
- mHandlerThread.start();
- mHandler=newFloatTextToastHandler(mHandlerThread.getLooper());
- privateclassFloatTextToastHandlerextendsHandler{
- publicFloatTextToastHandler(Looperlooper){
- super(looper);
- }
- @Override
- publicvoidhandleMessage(Messagemsg){
- switch(msg.what){
- caseWHAT_SHOW:
- showInHandler();
- }
- }
- }
- mHandler=newFloatTextToastHandler(mHandlerThread.getLooper());
- /**在Handler调用的show方法,主要为了等待{@link#mTargetView}的位置*/
- privatevoidshowInHandler(){
- int[]targetPos=getTargetViewPos();
- if(targetPos[0]==0&&targetPos[1]==0){
- mHandler.sendEmptyMessageDelayed(WHAT_SHOW,100);
- }else{
- finalRectcontentPos=getContentViewPos(targetPos);
- mToast.setGravity(Gravity.LEFT|Gravity.TOP,contentPos.left,contentPos.top);
- mToast.show();
- }
- }
获取要显示文本的位置
要获取显示的位置,就要知道targetVIew的位置以及它的宽、高,这样就能计算要显示文本的位置了。View组件都有一个函数,可以把自己在Window里的坐标转换为一个数组。- privateint[]getTargetViewPos(){
- finalint[]targetPos=newint[2];
- mTargetView.getLocationInWindow(targetPos);
- returntargetPos;
- }
- /**
- *计算获取浮动文本显示的位置,把浮动文本放在targetView的中心处
- *@return一个包含top和left的Rect
- */
- privateRectgetContentViewPos(int[]targetPos){
- finalRectwindowVisibleRect=newRect();
- finalViewtargetView=mTargetView;
- finalTextViewcontentView=mContentView;
- //状态栏高度
- targetView.getWindowVisibleDisplayFrame(windowVisibleRect);
- intstatusBarHeight=windowVisibleRect.top;
- //背景图那个三角箭头的位置
- finalTextPainttextPaint=contentView.getPaint();
- intcontentW=(int)textPaint.measureText((String)contentView.getText());
- intarrowPos=(int)(contentW*(30.0/160));
- finalRectrect=newRect();
- rect.left=targetPos[0]+targetView.getWidth()/2-arrowPos;
- rect.top=targetPos[1]-statusBarHeight+targetView.getHeight();
- returnrect;
- }
完整的组件代码
上面是对组件代码的拆分讲解,是为了说明我们当时实现这个组件的想法以及步骤,下面就整体把代码列出来,明了的看一下。
- /**
- *浮动的文本显示。根据一个提供的View,可以把文本显示到该View的下面.
- *可以设置显示的时间,多了该时间后自动消失。目前只支持纯文本{@linkString}类型的显示
- *因为要计算显示文本的宽度。
- *@authormichael_li(飞雪无情)
- *@since2011-12-10下午04:57:36
- */
- publicclassFloatTextToast{
- publicstaticfinalintLENGTH_LONG=Toast.LENGTH_LONG;
- publicstaticfinalintLENGTH_SHORT=Toast.LENGTH_SHORT;
- privatestaticfinalintWHAT_SHOW=1;
- privateContextmContext;
- privateViewmTargetView;
- privateToastmToast;
- privateTextViewmContentView;
- privateHandlerThreadmHandlerThread;
- privateFloatTextToastHandlermHandler;
- privateFloatTextToast(Contextcontext,ViewtargetView){
- this.mTargetView=targetView;
- this.mContext=context;
- mToast=newToast(mContext);
- mContentView=newTextView(mContext);
- mContentView.setBackgroundResource(R.drawable.float_text_toast_bg);
- mContentView.setTextColor(Color.BLACK);
- mContentView.setTextSize(TypedValue.COMPLEX_UNIT_DIP,16);
- mToast.setView(mContentView);
- //初始化一个Handler线程
- mHandlerThread=newHandlerThread("FloatTextToast");
- mHandlerThread.start();
- mHandler=newFloatTextToastHandler(mHandlerThread.getLooper());
- }
- /**
- *生成一个FloatTextToast
- *@paramcontextActivity上下文
- *@paramtargetView目标View,浮动文本要显示在哪个View下面
- *@paramtext要显示的文本
- *@paramduration浮动文本显示的时间{@link#LENGTH_LONG}{@link#LENGTH_SHORT}
- *@return一个FloatTextToast,可以调用{@link#show()}显示
- */
- publicstaticFloatTextToastmakeText(Contextcontext,ViewtargetView,Stringtext,intduration){
- finalFloatTextToastfloatToast=newFloatTextToast(context,targetView);
- finalTextViewcontentView=floatToast.mContentView;
- contentView.setText(text);
- floatToast.mToast.setDuration(duration);
- returnfloatToast;
- }
- /**
- *显示浮动文本
- */
- publicvoidshow(){
- mHandler.sendEmptyMessage(WHAT_SHOW);
- }
- /**在Handler调用的show方法,主要为了等待{@link#mTargetView}的位置*/
- privatevoidshowInHandler(){
- int[]targetPos=getTargetViewPos();
- if(targetPos[0]==0&&targetPos[1]==0){
- mHandler.sendEmptyMessageDelayed(WHAT_SHOW,100);
- }else{
- finalRectcontentPos=getContentViewPos(targetPos);
- mToast.setGravity(Gravity.LEFT|Gravity.TOP,contentPos.left,contentPos.top);
- mToast.show();
- }
- }
- privateint[]getTargetViewPos(){
- finalint[]targetPos=newint[2];
- mTargetView.getLocationInWindow(targetPos);
- returntargetPos;
- }
- /**
- *计算获取浮动文本显示的位置,把浮动文本放在targetView的中心处
- *@return一个包含top和left的Rect
- */
- privateRectgetContentViewPos(int[]targetPos){
- finalRectwindowVisibleRect=newRect();
- finalViewtargetView=mTargetView;
- finalTextViewcontentView=mContentView;
- //状态栏高度
- targetView.getWindowVisibleDisplayFrame(windowVisibleRect);
- intstatusBarHeight=windowVisibleRect.top;
- //背景图那个三角箭头的位置
- finalTextPainttextPaint=contentView.getPaint();
- intcontentW=(int)textPaint.measureText((String)contentView.getText());
- intarrowPos=(int)(contentW*(30.0/160));
- finalRectrect=newRect();
- rect.left=targetPos[0]+targetView.getWidth()/2-arrowPos;
- rect.top=targetPos[1]-statusBarHeight+targetView.getHeight();
- returnrect;
- }
- privateclassFloatTextToastHandlerextendsHandler{
- publicFloatTextToastHandler(Looperlooper){
- super(looper);
- }
- @Override
- publicvoidhandleMessage(Messagemsg){
- switch(msg.what){
- caseWHAT_SHOW:
- showInHandler();
- }
- }
- }
- }
- FloatTextToast.makeText(Contextcontext,ViewtargetView,Stringtext,intduration).show()
小结
这里主要是通过类之间的组合编写一个一个FloatTextToast组件,便于在应用中提示一些信息,不光局限于新功能的提示,还有其他的点击查看个人信息等等,就如上面的效果图一样。这里主要的难点就在于Activity启动获取targetView的状态,这里采用了不受影响的自定义的消息机制,能及时的获取targetView的状态。这里也采用的Toast的队列机制,这样就能够更好的一个个的提示,让用户看完一个再显示另外一个,不至于一下子全显示出来,而用户没有时间看。这里还采用了Paint用于测量文本的真实宽度,所以也有了一些缺陷,如果哪位有更好的方法,也可以留言告知我,不胜感激。
更多相关文章
- 在android中通过gps wifi定位 位置
- android 图片处理 (滤镜,图片位置)
- Android中对文本框里的值进行过滤
- Android UI组件进阶(1)——带进度条的按钮
- Android自定义组件之ListPopWindow
- 【Android API指南】App组件(8) - Services(2) - AIDL
- Android文档(二)-应用基础(1)应用组件(Application Components)
- Android 自定义跑马灯 实现超长文本,滚动完当前在继续切换下一条