简单粗暴,先看效果:
首先贴上工具类的代码:
/** * 通用(主、子线程)的自定义Toast */ public class ToastUtil { private static final String TAG = "ToastUtil"; private static Toast toast; //如果只想在主线程中弹出自定义toast,则直接调用此方法即可 public static void showToast(Context context, String titles, String messages) { toastProcess(context, titles, messages); } //如果想在子线程中和子线程中都能使用,则调用此方法即可(前提是在Activity中,因为runOnUiThread属于Activity中的方法) public static void showToast1(final Activity context, final String titles, final String messages) { if ("main".equals(Thread.currentThread().getName())) { toastProcess(context, titles, messages); } else { context.runOnUiThread(new Runnable() { @Override public void run() { toastProcess(context, titles, messages); } }); } } /** * 自定义toast * * @param context 上下文对象 * @param titles toast 标题 * @param messages toast内容 */ private static void toastProcess(Context context, String titles, String messages) { LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); assert layoutInflater != null; View view = layoutInflater.inflate(R.layout.custom_toast, null); ImageView bg = view.findViewById(R.id.toast_bg); TextView title = view.findViewById(R.id.toast_title); TextView text = view.findViewById(R.id.toast_content); bg.setImageResource(R.mipmap.toast_bg);//toast背景 title.setText(titles); //toast的标题 text.setText(messages); //toast内容 if (toast == null) { toast = new Toast(context.getApplicationContext()); } toast.setGravity(Gravity.CENTER, 12, 20);//setGravity用来设置Toast显示的位置,相当于xml中的android:gravity或android:layout_gravity toast.setDuration(Toast.LENGTH_LONG);//setDuration方法:设置持续时间,以毫秒为单位。该方法是设置补间动画时间长度的主要方法 toast.setView(view); //添加视图文件 toast.show(); } }
上述代码中封装了2个toast方法,注释上已经很明白了。
关注2个重点:
(1).自定义toast
(2).可在子线程中使用
(1).自定义toast
上述代码展示了自定义toast的代码逻辑(做了封装toastProcess(....)),下面讲toast的自定义布局也贴出来,custom_toast.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/toast_root" android:layout_width="wrap_content" android:layout_height="wrap_content"> <TextView android:id="@+id/toast_title" android:layout_width="100dp" android:layout_height="wrap_content" android:background="@android:color/darker_gray" android:text="这是一个toast" android:textSize="12sp" /> <RelativeLayout android:layout_width="100dp" android:layout_height="100dp" android:layout_below="@+id/toast_title"> <ImageView android:id="@+id/toast_bg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@mipmap/toast_bg" /> <TextView android:id="@+id/toast_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:text="我就是那个toast" android:textColor="@android:color/background_dark" android:textSize="12sp" /> </RelativeLayout> </RelativeLayout>
(2).可在子线程中使用
这个也没啥好讲的,代码已经体现出来了。首先 通过Thread.currentThread().getName()方法获取当前线程的线程名称,如果等于"main"的话,则说明在主线程,否则在子线程。这里需要注意的是,所谓的子线程通用,其实也是有限制条件的,那就是传入的上下文对象必须是Activity,因为context.runOnUiThread()方法的context只能在其是Activity中才能使用。这里暂时不考虑service中弹出toast的情况,毕竟toast也是属于用户交互的一种,service主要用于不直接与用户交互的后台,使用toast的情况并不常见。。。
除此之外,我想说的是,如果不作为toast通用工具类来讲的话,完全可以只在ToastUtil中实现自定义toast的部分,而在主程序中通过handler来实现线程间通信,最后只要保证在ui线程中调用ToastUtil.showToast(...)方法即可。
贴出最初的MainActivity.java代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "MainActivity"; private Button customToast; private Button threadCustomToast; private static MyHandler myHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); customToast = findViewById(R.id.custom_toast); threadCustomToast = findViewById(R.id.thread_custom_toast); customToast.setOnClickListener(this); threadCustomToast.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.custom_toast: ToastUtil.showToast(MainActivity.this, "这是一个toast", "我就是那个toast"); break; case R.id.thread_custom_toast: myHandler = new MyHandler(this); myHandler.post(myRunnable); break; default: break; } } @Override protected void onDestroy() { super.onDestroy(); myHandler.removeCallbacks(myRunnable); myHandler = null; } //****************************************************** /** * 创建静态内部类 */ private static class MyHandler extends Handler { //持有弱引用HandlerActivity,GC回收时会被回收掉. private final WeakReference<MainActivity> mActivty; MyHandler(MainActivity activity) { mActivty = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 0x01: MainActivity activity = mActivty.get(); if (activity != null) { //执行业务逻辑 String text = (String) msg.obj; ToastUtil.showToast(activity, "这是一个toast", text); } break; default: break; } } } private Runnable myRunnable = new Runnable() { @Override public void run() { //线程中无法使用Toast,需要将Toast发送至主线程中才能使用 Message msg = new Message(); msg.what = 0x01; msg.obj = "这是线程的toast"; myHandler.sendMessage(msg); } }; }
上述代码,仅供读者参考吧,毕竟如果存在多个不同的子线程的话,如此操作还是比较繁琐的,不如工具类来的方便啊。
好啦,到此结束,如果有什么疑问,或者意见或建议,请留言探索。