您的位置:首页 > 移动开发 > Android开发

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

2012-03-17 07:36 579 查看

前言

在开发Android应用时,加新功能是必不可少的,我们加入了新的功能,有的一看界面就可以看出来,但是有的新功能就比较隐蔽,也就是用户很难知道你添加了这个新功能,这个时候就需要用户在打开我们的应用时给出一些提示,说明我们在哪里添加了新功能,点击哪里可以看到这个新功能。这时我们第一时间想到的可能是Toast,因为它用法简单,又不影响用户操作,但是它有个缺点,就是不能明确的指示是哪里添加了新功能,除非你用文字描述出来。为此,我基于Toast编写了一个小组件FloatTextToast(下面遇到的这个名字代替我写的这个组件),他和Toast的用法一样简单,并且弥补了Toast的缺点,也更显得更好看。

效果图



你可以学到

Toast的基本用法

Android的消息机制,如何创建自己的消息队列

怎样在Activity启动时获取一个View的width、height、top、left等属性

基本思路

首先你要有一个处理好的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构造函数,可以对它有个大概的了解,防止后面的代码中出现的成员变量不认识。

[java] view plaincopyprint?

private FloatTextToast(Context context,View targetView) {

this.mTargetView = targetView;

this.mContext= context;

mToast=new Toast(mContext);

mContentView=new TextView(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=new HandlerThread("FloatTextToast");

mHandlerThread.start();

mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());

}

[java] view plaincopyprint?

//初始化一个Handler线程

mHandlerThread=new HandlerThread("FloatTextToast");

mHandlerThread.start();

mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());

//初始化一个Handler线程
mHandlerThread=new HandlerThread("FloatTextToast");
mHandlerThread.start();
mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());

这样就声明了一个HandlerThread并且让它运行,运行之后我们就可以获取一个属于该Thread的Looper,然后把Message发送给这个Looper,那么这个线程就可以处理你发送的消息了。。看看我们的自定义Handler

[java] view plaincopyprint?

private class FloatTextToastHandler extends Handler{

public FloatTextToastHandler(Looper looper) {

super(looper);

}

@Override

public void handleMessage(Message msg) {

switch(msg.what){

case WHAT_SHOW:

showInHandler();

}

}

}

[java] view plaincopyprint?

mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());

mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());

正是我们使用自己开启的线程处理我们的Message的意思。下面看下我们的showInHandler()方法是怎么处理的。

[java] view plaincopyprint?

/**在Handler调用的show方法,主要为了等待{@link #mTargetView}的位置*/

private void showInHandler(){

int[] targetPos=getTargetViewPos();

if(targetPos[0]==0&&targetPos[1]==0){

mHandler.sendEmptyMessageDelayed(WHAT_SHOW, 100);

}else{

final Rect contentPos=getContentViewPos(targetPos);

mToast.setGravity(Gravity.LEFT|Gravity.TOP, contentPos.left, contentPos.top);

mToast.show();

}

}

[java] view plaincopyprint?

private int[] getTargetViewPos(){

final int[] targetPos=new int[2];

mTargetView.getLocationInWindow(targetPos);

return targetPos;

}

private int[] getTargetViewPos(){
final int[] targetPos=new int[2];
mTargetView.getLocationInWindow(targetPos);
return targetPos;
}
这样,就返回了targetView的xy坐标了。光有targetView的坐标还不够,还要有contentView最终要显示的位置。

[java] view plaincopyprint?

/**

* 计算获取浮动文本显示的位置,把浮动文本放在targetView的中心处

* @return 一个包含top和left的Rect

*/

private Rect getContentViewPos(int[] targetPos){

final Rect windowVisibleRect=new Rect();

final View targetView=mTargetView;

final TextView contentView=mContentView;

//状态栏高度

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;

}

[java] view plaincopyprint?

/**

* 浮动的文本显示。根据一个提供的View,可以把文本显示到该View的下面.

* 可以设置显示的时间,多了该时间后自动消失。目前只支持纯文本{@link String}类型的显示

* 因为要计算显示文本的宽度。

* @author michael_li(飞雪无情)

* @since 2011-12-10 下午04:57:36

*/

public class FloatTextToast {

public static final int LENGTH_LONG=Toast.LENGTH_LONG;

public static final int LENGTH_SHORT=Toast.LENGTH_SHORT;

private static final int WHAT_SHOW=1;

private Context mContext;

private View mTargetView;

private Toast mToast;

private  TextView mContentView;

private HandlerThread mHandlerThread;

private FloatTextToastHandler mHandler;

private FloatTextToast(Context context,View targetView) {

this.mTargetView = targetView;

this.mContext= context;

mToast=new Toast(mContext);

mContentView=new TextView(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=new HandlerThread("FloatTextToast");

mHandlerThread.start();

mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());

}

/**

* 生成一个FloatTextToast

* @param context Activity 上下文

* @param targetView  目标View,浮动文本要显示在哪个View下面

* @param text 要显示的文本

* @param duration 浮动文本显示的时间 {@link #LENGTH_LONG} {@link #LENGTH_SHORT}

* @return 一个FloatTextToast,可以调用{@link #show()}显示

*/

public static FloatTextToast makeText(Context context,View targetView, String text, int duration) {

final FloatTextToast floatToast=new FloatTextToast(context,targetView);

final TextView contentView=floatToast.mContentView;

contentView.setText(text);

floatToast.mToast.setDuration(duration);

return floatToast;

}

/**

* 显示浮动文本

*/

public void show(){

mHandler.sendEmptyMessage(WHAT_SHOW);

}

/**在Handler调用的show方法,主要为了等待{@link #mTargetView}的位置*/

private void showInHandler(){

int[] targetPos=getTargetViewPos();

if(targetPos[0]==0&&targetPos[1]==0){

mHandler.sendEmptyMessageDelayed(WHAT_SHOW, 100);

}else{

final Rect contentPos=getContentViewPos(targetPos);

mToast.setGravity(Gravity.LEFT|Gravity.TOP, contentPos.left, contentPos.top);

mToast.show();

}

}

private int[] getTargetViewPos(){

final int[] targetPos=new int[2];

mTargetView.getLocationInWindow(targetPos);

return targetPos;

}

/**

* 计算获取浮动文本显示的位置,把浮动文本放在targetView的中心处

* @return 一个包含top和left的Rect

*/

private  Rect getContentViewPos(int[] targetPos){

final Rect windowVisibleRect=new Rect();

final View targetView=mTargetView;

final TextView contentView=mContentView;

//状态栏高度

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;

}

private class FloatTextToastHandler extends Handler{

public FloatTextToastHandler(Looper looper) {

super(looper);

}

@Override

public void handleMessage(Message msg) {

switch(msg.what){

case WHAT_SHOW:

showInHandler();

}

}

}

}

/**
* 浮动的文本显示。根据一个提供的View,可以把文本显示到该View的下面.
* 可以设置显示的时间,多了该时间后自动消失。目前只支持纯文本{@link String}类型的显示
* 因为要计算显示文本的宽度。
* @author michael_li(飞雪无情)
* @since 2011-12-10 下午04:57:36
*/
public class FloatTextToast {
public static final int LENGTH_LONG=Toast.LENGTH_LONG;
public static final int LENGTH_SHORT=Toast.LENGTH_SHORT;
private static final int WHAT_SHOW=1;

private Context mContext;
private View mTargetView;
private Toast mToast;
private  TextView mContentView;

private HandlerThread mHandlerThread;
private FloatTextToastHandler mHandler;
private FloatTextToast(Context context,View targetView) {
this.mTargetView = targetView;
this.mContext= context;
mToast=new Toast(mContext);
mContentView=new TextView(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=new HandlerThread("FloatTextToast");
mHandlerThread.start();
mHandler=new FloatTextToastHandler(mHandlerThread.getLooper());
}
/**
* 生成一个FloatTextToast
* @param context Activity 上下文
* @param targetView  目标View,浮动文本要显示在哪个View下面
* @param text 要显示的文本
* @param duration 浮动文本显示的时间 {@link #LENGTH_LONG} {@link #LENGTH_SHORT}
* @return 一个FloatTextToast,可以调用{@link #show()}显示
*/
public static FloatTextToast makeText(Context context,View targetView, String text, int duration) {
final FloatTextToast floatToast=new FloatTextToast(context,targetView);
final TextView contentView=floatToast.mContentView;
contentView.setText(text);
floatToast.mToast.setDuration(duration);
return floatToast;
}
/**
* 显示浮动文本
*/
public void show(){
mHandler.sendEmptyMessage(WHAT_SHOW);
}
/**在Handler调用的show方法,主要为了等待{@link #mTargetView}的位置*/
private void showInHandler(){
int[] targetPos=getTargetViewPos();
if(targetPos[0]==0&&targetPos[1]==0){
mHandler.sendEmptyMessageDelayed(WHAT_SHOW, 100);
}else{
final Rect contentPos=getContentViewPos(targetPos);
mToast.setGravity(Gravity.LEFT|Gravity.TOP, contentPos.left, contentPos.top);
mToast.show();
}
}
private int[] getTargetViewPos(){
final int[] targetPos=new int[2];
mTargetView.getLocationInWindow(targetPos);
return targetPos;
}
/**
* 计算获取浮动文本显示的位置,把浮动文本放在targetView的中心处
* @return 一个包含top和left的Rect
*/
private  Rect getContentViewPos(int[] targetPos){
final Rect windowVisibleRect=new Rect();
final View targetView=mTargetView;
final TextView contentView=mContentView;
//状态栏高度
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;
}
private class FloatTextToastHandler extends Handler{

public FloatTextToastHandler(Looper looper) {
super(looper);
}

@Override
public void handleMessage(Message msg) {
switch(msg.what){
case WHAT_SHOW:
showInHandler();
}
}

}
}

此组件和Toast的实现方法一样,所以上手不难,只需使用makeText静态方法生成一个即可

[java] view plaincopyprint?

FloatTextToast.makeText(Context context, View targetView, String text, int duration).show()

FloatTextToast.makeText(Context context, View targetView, String text, int duration).show()就这么简单,传进去几个参数,show出即可,和Toast一样好用。

小结

这里主要是通过类之间的组合编写一个一个FloatTextToast组件,便于在应用中提示一些信息,不光局限于新功能的提示,还有其他的点击查看个人信息等等,就如上面的效果图一样。这里主要的难点就在于Activity启动获取targetView的状态,这里采用了不受影响的自定义的消息机制,能及时的获取targetView的状态。这里也采用的Toast的队列机制,这样就能够更好的一个个的提示,让用户看完一个再显示另外一个,不至于一下子全显示出来,而用户没有时间看。这里还采用了Paint用于测量文本的真实宽度,所以也有了一些缺陷,如果哪位有更好的方法,也可以留言告知我,不胜感激。



附上组件源代码和效果图的Demo下载 http://download.csdn.net/detail/michael__li/3904636
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐