0%

Android开发 自定义Toast样式II 在AlertDialog基础扩展

前文,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();
    }


}