今天在此分享一个之前自己封装的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;
}
}
网友评论