基于Android的浮动组件,可以用于应用中的新功能展示等等。

  • 格式:docx
  • 大小:73.18 KB
  • 文档页数:12

前言在开发Android应用时,加新功能是必不可少的,我们加入了新的功能,有的一看界面就可以看出来,但是有的新功能就比较隐蔽,也就是用户很难知道你添加了这个新功能,这个时候就需要用户在打开我们的应用时给出一些提示,说明我们在哪里添加了新功能,点击哪里可以看到这个新功能。

这时我们第一时间想到的可能是Toast,因为它用法简单,又不影响用户操作,但是它有个缺点,就是不能明确的指示是哪里添加了新功能,除非你用文字描述出来。

为此,我基于Toast编写了一个小组件FloatTextToast(下面遇到的这个名字代替我写的这个组件),他和Toast的用法一样简单,并且弥补了Toast的缺点,也更显得更好看。

效果图你可以学到1. Toast的基本用法2. Android的消息机制,如何创建自己的消息队列3. 怎样在Activity启动时获取一个View的width、height、top、left等属性基本思路1. 首先你要有一个处理好的9 PNG的图片,用于自适应文字显示,关于9 PNG处理可以参考Android Doc2. 要显示在哪个View的下面,就要知道这个目标View的位置3. 把要显示的文本放在一个TextView里,使用Toast的setView方法设置Toast要显示的View。

4. 根据得到的位置,最后就是使用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构造函数,可以对它有个大概的了解,防止后面的代码中出现的成员变量不认识。

[java]view plaincopyprint?1.private FloatTextToast(Context context,View targetView) {2.this.mTargetView = targetView;3.this.mContext= context;4.mToast=new Toast(mContext);5.mContentView=new TextView(mContext);6.mContentView.setBackgroundResource(R.drawable.float_text_toast_bg);7.mContentView.setTextColor(Color.BLACK);8.mContentView.setTextSize(PLEX_UNIT_DIP,16);9.mToast.setView(mContentView);10.11.//初始化一个Handler线程12.mHandlerThread=new HandlerThread("FloatTextToast");13.mHandlerThread.start();14.mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());15.}自定义自己的消息循环机制要想在一个自定义的组件中使用Message机制,一定要有自己的Looper机制,我们不能使用Activity的Looper,因为主Looper可能会有其他的Message需要处理,这就会导致我们的show方法会延迟调用,这样效果就不好了,所以要有一个专门的Looper来处理此Message。

要声明自己的Looper,就需要HandlerThread这个类的配合了,这可是个好东西,使用它你会很容易的创建一个自己的线程用于处理你Message。

使用方法很简单,如下代码:[java]view plaincopyprint?1.//初始化一个Handler线程2.mHandlerThread=new HandlerThread("FloatTextToast");3.mHandlerThread.start();4.mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());这样就声明了一个HandlerThread并且让它运行,运行之后我们就可以获取一个属于该Thread的Looper,然后把Message发送给这个Looper,那么这个线程就可以处理你发送的消息了。

看看我们的自定义Handler[java]view plaincopyprint?1.private class FloatTextToastHandler extends Handler{2.3.public FloatTextToastHandler(Looper looper) {4.super(looper);5.}6.7.@Override8.public void handleMessage(Message msg) {9.switch(msg.what){10.case WHAT_SHOW:11.showInHandler();12.}13.}14.15.16.}它需要传递一个Looper作为构造参数声明,意思就是使用这个Looper处理我发send的Message的意思。

上面的代码[java]view plaincopyprint?1.mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());正是我们使用自己开启的线程处理我们的Message的意思。

下面看下我们的showInHandler()方法是怎么处理的。

[java]view plaincopyprint?1./**在Handler调用的show方法,主要为了等待{@link #mTargetView}的位置*/2.private void showInHandler(){3.int[] targetPos=getTargetViewPos();4.if(targetPos[0]==0&&targetPos[1]==0){5.mHandler.sendEmptyMessageDelayed(WHAT_SHOW, 100);6.}else{7.final Rect contentPos=getContentViewPos(targetPos);8.mToast.setGravity(Gravity.LEFT|Gravity.TOP, contentPos.left,contentPos.top);9.mToast.show();10.}11.}该方法其实就是在获取targetVIew的位置,如果获取不到,则向自定义的Looper里发送一个Message重新调用该函数,如果得到了位置,那么就调用Toast的setGravity方法设置好要显示文本的位置,然后显示即可。

获取要显示文本的位置要获取显示的位置,就要知道targetVIew的位置以及它的宽、高,这样就能计算要显示文本的位置了。

View组件都有一个函数,可以把自己在Window里的坐标转换为一个数组。

[java]view plaincopyprint?1.private int[] getTargetViewPos(){2.final int[] targetPos=new int[2];3.mTargetView.getLocationInWindow(targetPos);4.return targetPos;5.}这样,就返回了targetView的xy坐标了。

光有targetView的坐标还不够,还要有contentView 最终要显示的位置。

[java]view plaincopyprint?1./**2.* 计算获取浮动文本显示的位置,把浮动文本放在targetView的中心处3.* @return 一个包含top和left的Rect4.*/5.private Rect getContentViewPos(int[] targetPos){6.final Rect windowVisibleRect=new Rect();7.final View targetView=mTargetView;8.final TextView contentView=mContentView;9.//状态栏高度10.targetView.getWindowVisibleDisplayFrame(windowVisibleRect);11.int statusBarHeight=windowVisibleRect.top;12.13.//背景图那个三角箭头的位置14.final TextPaint textPaint=contentView.getPaint();15.intcontentW=(int)textPaint.measureText((String)contentView.getText());16.int arrowPos=(int)(contentW*(30.0/160));17.18.final Rect rect = new Rect();19.rect.left = targetPos[0]+targetView.getWidth()/2-arrowPos;20.rect.top = targetPos[1]-statusBarHeight + targetView.getHeight();21.return rect;22.}这个函数的功能就是让文本显示在targetView的下方的横向中间的位置,也就是文本的背景尖角三角要指向targetView横向中间的位置,这样才好看些。