Android 简单封装Toast工具类

今天在此分享一个之前自己封装的Toast工具类。作为一个提示类的组件,系统有些方面做得并不是特别好,比如多次点击会像冒泡一样显示,很多时候基本都是想让它显示一次而已。还有由于国内手机厂商对Rom层做的修改,出现很多不同形式的Toast,因此希望Toast在不同手机显示的形式一致, 并且不出现重复现象(例如:今日头条的Toast),所以有必要做一些简单的封装。还有一点市面上有关于修改Toast显示时长的,个人并不建议通过反射去修改toast的显示时长,既然Google只设置两种显示时长(Toast.LENGTH_SHORT和Toast.LENGTH_LONG),必定有合理性,Toast只是作为提醒功能使用,源码里面去除touch事件,如果项目中Toast满足不了,完全可以使用其他组件代替(Dialog等)。本工具类使用建造者模式构造,链式调用方便使用。

简单使用

Toastkeeper.getInstance()
   .createBuilder(this)
   .setMessage(message)
   .show();

构造

构造方法采用静态内部类方式

    //私有构造
    private Toastkeeper() {
    }

    private static class SingleTonHoler {
        private static Toastkeeper INSTANCE = new Toastkeeper();
    }

    public static Toastkeeper getInstance() {
        return SingleTonHoler.INSTANCE;
    }

属性介绍

        //文本
        private CharSequence mMessage;
        //其他属性参数
        private int mDuration = DURATION_LONG;
        private int mGravity = GRAVITY_CENTER;
        private int mOffsetX = 0;
        private int mOffsetY = 0;
        private boolean mUseSystem = false;
        private boolean mCancleSame = true;
        private View mToastView = null;
        private @LayoutRes
        int mlayoutId = 0;
  • mMessage: 设置提醒的内容
  • mGravity、mOffsetX mOffsetY 用于定位
  • mUseSystem :是否使用系统的toast
  • mCancleSame :取消同一时间相同内容的toast
  • mToastView 和mlayoutId :用于自定义toast的view

子线程处理:

//构造
        final Context appContext = context.getApplicationContext();
        //兼容在子线程中显示
        if (isMainLooper()) {
            showToastSafety(appContext, builder);
        } else {
            sMainHandler.post(new Runnable() {
                @Override
                public void run() {
                    showToastSafety(appContext, builder);
                }
            });
        }

说明:
isMainLooper()用于判断当前线程是否是子线程

去除相同的消息

/**
     * 处理相同消息的tag
     * 如果使用系统toast的话,只比较Message,
     * 如果使用自定义的,则比较bean
     * 再次就是需要比较是否主动取消相同的消息
     *
     * @param builder
     * @return
     */
    private boolean handleSameToast(Builder builder) {
        boolean sameBuilder = false;
        boolean mCancleSame = builder.mCancleSame;
        boolean mUseSystem = builder.mUseSystem;

        if (mUseSystem) {
            sameBuilder = lastBuilder.mMessage.equals(builder.mMessage);
        } else {
            String lastBuilderS = lastBuilder.toString();
            String currentBuilderS = builder.toString();
            sameBuilder = lastBuilderS.equals(currentBuilderS);
        }

        return mCancleSame && sameBuilder;
    }

后记

由于只是做简单的封装,只需要一个单独的类,注释很明确,如遇到使用问题或者显示出错,记得及时反馈。所有代码直接贴在下面:

/**
 * @author gexinyu
 */
public class Toastkeeper  {

    //默认位置5种位置(注解方式限定设置的位置)
    public static final int GRAVITY_CENTER = Gravity.CENTER;
    public static final int GRAVITY_TOP = Gravity.TOP;
    public static final int GRAVITY_BOTTOM = Gravity.BOTTOM;
    public static final int GRAVITY_LEFT = Gravity.LEFT;
    public static final int GRAVITY_RIGHT = Gravity.RIGHT;
    //默认的两种toast显示时长
    public static final int DURATION_SHORT = Toast.LENGTH_SHORT;
    public static final int DURATION_LONG = Toast.LENGTH_LONG;

    @IntDef({DURATION_SHORT, DURATION_LONG})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Duration {
    }

    @IntDef({GRAVITY_CENTER, GRAVITY_TOP, GRAVITY_BOTTOM, GRAVITY_LEFT, GRAVITY_RIGHT})
    @Retention(RetentionPolicy.SOURCE)
    public @interface IGravity {
    }

    //只有一个toast
    private Toast mToast;
    private boolean isShowing = false;
    //默认的view
    private TextView normalTextView = null;
    private Builder lastBuilder = null;
    //主线程的handle
    private Handler sMainHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            isShowing = false;
        }
    };

    //私有构造
    private Toastkeeper() {
    }

    private static class SingleTonHoler {
        private static Toastkeeper INSTANCE = new Toastkeeper();
    }

    public static Toastkeeper getInstance() {
        return SingleTonHoler.INSTANCE;
    }

    /**
     * 是否是主线程
     *
     * @return
     */
    private boolean isMainLooper() {
        return Looper.getMainLooper() == Looper.myLooper();
    }

    /**
     * 显示toast
     *
     * @param context
     * @param builder
     * @return
     */
    private void showToast(Context context, final Builder builder) {
        if (null == context) {
            throw new NullPointerException("you set context is null!");
        }

        if (null == builder) {
            throw new NullPointerException("you set builder is null!");
        }
        final Context appContext = context.getApplicationContext();
        //兼容在子线程中显示
        if (isMainLooper()) {
            showToastSafety(appContext, builder);
        } else {
            sMainHandler.post(new Runnable() {
                @Override
                public void run() {
                    showToastSafety(appContext, builder);
                }
            });
        }
    }


    /**
     * 取消toast
     */
    private void dimissToast() {
        if (mToast != null) {
            mToast.cancel();
            isShowing = false;
        }
    }

    /**
     * 创建构造
     *
     * @param context
     * @return
     */
    public Builder createBuilder(Context context) {
        Builder builder = new Builder(context);
        return builder;
    }

    //*****************************构造builder***********************

    public final class Builder {

        private Context mContext;
        //文本
        private CharSequence mMessage;
        //其他属性参数
        private int mDuration = DURATION_LONG;
        private int mGravity = GRAVITY_CENTER;
        private int mOffsetX = 0;
        private int mOffsetY = 0;
        private boolean mUseSystem = false;
        private boolean mCancleSame = true;
        private View mToastView = null;
        private @LayoutRes
        int mlayoutId = 0;

        Builder(Context context) {
            mContext = context;
        }

        /**
         * 设置信息
         *
         * @param text
         * @return
         */
        public Builder setMessage(String text) {
            mMessage = text;
            return this;
        }

        /**
         * 设置时长
         *
         * @param duration
         * @return
         */
        public Builder setDuration(@Duration int duration) {
            mDuration = duration;
            return this;
        }

        /**
         * 设置位置
         *
         * @param gravity
         * @return
         */
        public Builder setGravity(@IGravity int gravity) {
            mGravity = gravity;
            return this;
        }

        /**
         * 设置X偏移
         *
         * @param offsetX
         * @return
         */
        public Builder setOffsetX(int offsetX) {
            mOffsetX = offsetX;
            return this;
        }

        /**
         * 设置Y偏移
         *
         * @param offsetY
         * @return
         */
        public Builder setOffsetY(int offsetY) {
            mOffsetY = offsetY;
            return this;
        }

        /**
         * 设置使用系统的toast
         *
         * @param useSystem
         * @return
         */
        public Builder setUseSystem(boolean useSystem) {
            mUseSystem = useSystem;
            return this;
        }

        /**
         * 是否取消相同的(相同的不会重新创建,消失之后才能创建新的)
         *
         * @param cancleSame
         * @return
         */
        public Builder setCancleTheSame(boolean cancleSame) {
            mCancleSame = cancleSame;
            return this;
        }

        /**
         * 设置自定义view
         *
         * @param layoutId
         * @return
         */
        public Builder setView(@LayoutRes int layoutId) {
            if (layoutId == 0) {
                throw new NullPointerException("your set layout is null");
            }
            if (null == mContext) {
                throw new NullPointerException("context  is null");
            }

            mlayoutId = layoutId;
            setView(LayoutInflater.from(mContext).inflate(layoutId, null));
            return this;
        }

        /**
         * 设置自定义view
         *
         * @param view
         * @return
         */
        public Builder setView(View view) {
            mToastView = view;
            return this;
        }

        /**
         * 此方法用于添加toast
         */
        public Builder show() {
            Toastkeeper.this.showToast(mContext, this);
            return this;
        }

        /**
         * 此方法用于生命周期结束取消toast
         */
        public void dimiss() {
            Toastkeeper.this.dimissToast();
        }

        @Override
        public String toString() {
            String builderString = "";
            if (mlayoutId != 0) {
                builderString = "Builder{" +
                        "mContext=" + mContext +
                        ", mMessage=" + mMessage +
                        ", mDuration=" + mDuration +
                        ", mGravity=" + mGravity +
                        ", mOffsetX=" + mOffsetX +
                        ", mOffsetY=" + mOffsetY +
                        ", mlayoutId=" + mlayoutId +
                        '}';
            } else {
                builderString = "Builder{" +
                        "mContext=" + mContext +
                        ", mMessage=" + mMessage +
                        ", mDuration=" + mDuration +
                        ", mGravity=" + mGravity +
                        ", mOffsetX=" + mOffsetX +
                        ", mOffsetY=" + mOffsetY +
                        ", mToastView=" + mToastView +
                        '}';
            }
            return builderString;
        }
    }


    /**
     * 创建toast
     *
     * @param context
     * @param builder
     */
    private void showToastSafety(Context context, Builder builder) {

        if (lastBuilder == null) {
            createDiffToast(context, builder);
        } else {
            boolean sameTag = handleSameToast(builder);
            if (!sameTag) {
                if (mToast != null) {
                    mToast.cancel();
                }
                createDiffToast(context, builder);
            } else {
                //相同的toast时候
                if (!isShowing) {
                    createDiffToast(context, builder);
                }
            }
        }
    }

    /**
     * 创建不同的toast根据类型
     * <p>
     * 消息有三种情况
     * 第一系统
     * 第二种默认的
     * 第三中可以自定义布局
     *
     * @param context
     * @param builder
     * @param类型1是相同的toast/类型2是创建新toast
     */
    private void createDiffToast(Context context, Builder builder) {
        if (builder.mUseSystem) {
            if (builder.mMessage == null) {
                throw new NullPointerException("The message must not be null");
            } else {
                mToast = Toast.makeText(context, builder.mMessage, Toast.LENGTH_SHORT);
            }
        } else {
            mToast = new Toast(context);
            mToast.setGravity(builder.mGravity, builder.mOffsetX, builder.mOffsetY);
            mToast.setDuration(builder.mDuration);
            View toastView = getToastView(context, builder.mToastView, builder.mMessage);
            mToast.setView(toastView);
        }


        mToast.show();
        isShowing = true;
        lastBuilder = builder;

        sMainHandler.removeMessages(1);
        int mDuration = builder.mDuration == DURATION_LONG ? 3500 : 2000;
        sMainHandler.sendEmptyMessageDelayed(1, mDuration);

    }


    /**
     * 处理相同消息的tag
     * 如果使用系统toast的话,只比较Message,
     * 如果使用自定义的,则比较bean
     * 再次就是需要比较是否主动取消相同的消息
     *
     * @param builder
     * @return
     */
    private boolean handleSameToast(Builder builder) {
        boolean sameBuilder = false;
        boolean mCancleSame = builder.mCancleSame;
        boolean mUseSystem = builder.mUseSystem;

        if (mUseSystem) {
            sameBuilder = lastBuilder.mMessage.equals(builder.mMessage);
        } else {
            String lastBuilderS = lastBuilder.toString();
            String currentBuilderS = builder.toString();
            sameBuilder = lastBuilderS.equals(currentBuilderS);
        }

        return mCancleSame && sameBuilder;
    }

    /**
     * 创建toast的view
     *
     * @param context
     * @param toastView
     * @param mMessage
     * @return
     */
    private View getToastView(Context context, View toastView, CharSequence mMessage) {
        View mToastView = null;
        if (null == toastView) {
            if (mMessage == null) {
                throw new NullPointerException("The message must not be null");
            } else {
                if (normalTextView == null) {
                    normalTextView = new TextView(context);
                }
                normalTextView.setGravity(Gravity.CENTER);
                normalTextView.setBackgroundColor(0xEE333333);
                normalTextView.setTextColor(Color.WHITE);
                normalTextView.setPadding(50, 35, 50, 35);
                normalTextView.setText(mMessage);
                mToastView = normalTextView;
            }
        } else {
            mToastView = toastView;
        }

        return mToastView;
    }
}

推荐阅读更多精彩内容