前文,Android开发 自定义Toast样式,基本实现了自定义的Toast,但没想到客户的需求有点变态。要求提示的时候背景变灰,用户不可操作,而且提示的时间还要根据字符串长度计算,原想在Toast的基础上扩展,修改WindowManager.LayoutParams.flag,但发现Toast源代码调用了隐藏的API,我们是没法实现了。客户的要求其实就是一个AlertDialog,那就写个自定义的AlertDialog吧。因为项目中大量使用了Toast,调用方法为:
MyToast.makeText(context, "密码错误,请进SOS查看!!", Toast.LENGTH_LONG).show();
为了减少代码修改,必须把我们自定义的Toast也加上一个静态的makeText方法,和一个show()方法,以达到不用修改调用方法的目的。
package com.jtang.android.components;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
import com.jtang.android.R;
public class MyToast{
static AlertDialog ad;
Context context;
long delayMills=0;
View view;
Handler handler;
public MyToast(Context context) {
ad = new AlertDialog.Builder(context).create();
this.context=context;
handler=new Handler()
{
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch(msg.what)
{
case 0:
ad.dismiss();
break;
}
}
};
}
public static MyToast makeText(Context context, CharSequence text, int duration) {
// AlertDialog ad=new AlertDialog.Builder(context).create();
MyToast myToast = new MyToast(context);
myToast.setMessage(text);
return myToast;
}
public static MyToast makeText(Context context, int resId, int duration) {
return makeText(context,context.getResources().getString(resId),duration);
}
public void setMessage(CharSequence message) {
LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflate.inflate(R.layout.my_toast, null);
TextView tv = (TextView) view.findViewById(R.id.message);
tv.setText(message);
//根据文本长度设置显示的时间
delayMills=143*message.length();
}
public void show() {
ad.show();
Window window = ad.getWindow();
window.setContentView(view);
new Thread()
{
public void run()
{
try {
sleep(delayMills);
handler.sendEmptyMessage(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
}
以上的代码还有问题,当连续调用两次时,显示的效果不是像Toast一样按顺序显示信息,而是直接再开一个对话框,并且,在第二个对话框关闭后,第一个对话框就一直留着。 下面的代码在以上基础上做了修改,增加了单例模式,完全实现Toast的效果。
package com.jtang.android.components;
import java.util.LinkedList;
import java.util.Queue;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
import com.jtang.android.R;
public class MyToast {
AlertDialog ad;
Context context;
long delayMills = 0;
View view;
Handler handler;
//单例,是因为有时候会在一次提示没结束时弹出第二次提示,要实现像Toast一样,顺序提示,只能单例了
static MyToast instance;
//存储信息内容的列队
Queue messages;
TextView tv;
CharSequence message;
//当前是否正在显示
boolean isShowing=false;
public MyToast(Context context) {
messages = new LinkedList();
ad = new AlertDialog.Builder(context).create();
this.context = context;
LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflate.inflate(R.layout.my_toast, null);
tv = (TextView) view.findViewById(R.id.message);
final Window window = ad.getWindow();
//setContentView之前如果不调show方法会异常
ad.show();
window.setContentView(view);
ad.hide();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
switch (msg.what) {
case 0:
setMessage(message);
ad.show();
break;
case 1:
ad.hide();
break;
case 2:
ad.dismiss();
break;
}
}
};
}
public static MyToast makeText(Context context, CharSequence text, int duration) {
//构建新实例
if (instance==null) {
instance = new MyToast(context);
}
//把消息添加进列队
instance.addMessage(text);
return instance;
}
public static MyToast makeText(Context context, int resId, int duration) {
return makeText(context, context.getResources().getString(resId), duration);
}
public void setMessage(CharSequence message) {
tv.setText(message);
}
public void addMessage(CharSequence message)
{
messages.offer(message);
}
public void show() {
//如果在上一条没显示完的时候,再次调用show()方法,直接返回
if(isShowing)
{
return;
}
isShowing=true;
new Thread() {
public void run() {
//取出信息,循环显示
while((message=messages.poll()) != null)
{
handler.sendEmptyMessage(0);
//根据信息长度计算显示时间
delayMills=143*message.length();
try {
sleep(delayMills);
//隐藏
handler.sendEmptyMessage(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
handler.sendEmptyMessage(1);
}
}
//全部显示完,dismiss对话框
handler.sendEmptyMessage(2);
//把当前实例设为null,下次再调用时重新构建,之所以这么干,是因为窗口会按建构的顺序显示
//如果不重新构建,在之后创建的Dialog中如果要调用这个show方法,就会显示在之后创建的Dialog后面,谁最后创建,谁在最前。
instance=null;
}
}.start();
}
}