【Toast】關於Android Toast 的一丁點認識

圖片來自百度圖片,如有侵權,請聯絡作者刪除
Android Toast 在專案中是普遍應用的一個控制元件,應該也是一個使用非常簡單的控制元件。這裡,有人會問,Toast 這麼簡單幹嘛還要花時間去學習呢,專案裡直接 Toast.makeText 就可以了啊。我的回答是:“因為簡單,所以需要去學習,去了解”。
為什麼我還要去做一個自定義Toast?
Android Toast 如果在某個場景互動中,一不小心多次觸發了 Toast show 結果是不停地在你螢幕顯示同樣的 Toast 資訊。我是個有時間潔癖的人,不喜歡因為你多顯示一次 Toast 資訊浪費我一秒鐘或兩秒鐘,在碎片時間學習中,我更希望花一秒鐘看到我更喜歡看到的知識,集中精力去得到我想要的東西。
廢話少扯,進入主題。
先看看Android 系統 Toast 的實現原始碼:
Toast MakeText:
/** * Make a standard toast that just contains a text view. * * @param contextThe context to use.Usually your {@link android.app.Application} *or {@link android.app.Activity} object. * @param textThe text to show.Can be formatted text. * @param duration How long to display the message.Either {@link #LENGTH_SHORT} or *{@link #LENGTH_LONG} * */ public static Toast makeText(Context context, CharSequence text, @Duration int duration) { return makeText(context, null, text, duration); }
/** * Make a standard toast to display using the specified looper. * If looper is null, Looper.myLooper() is used. * @hide */ public static Toast makeText(@NonNull Context context, @Nullable Looper looper, @NonNull CharSequence text, @Duration int duration) { Toast result = new Toast(context, looper); LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null); TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message); tv.setText(text); result.mNextView = v; result.mDuration = duration; return result; }
再看看Toast 佈局:
<?xml version="1.0" encoding="utf-8"?> <!-- /* //device/apps/common/res/layout/transient_notification.xml ** ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** **http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="?android:attr/toastFrameBackground"> <TextView android:id="@android:id/message" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_horizontal" android:textAppearance="@style/TextAppearance.Toast" android:textColor="@color/bright_foreground_dark" android:shadowColor="#BB000000" android:shadowRadius="2.75" /> </LinearLayout>
Toast show 和 cancel 方法:
/** * Show the view for the specified duration. */ public void show() { if (mNextView == null) { throw new RuntimeException("setView must have been called"); } INotificationManager service = getService(); String pkg = mContext.getOpPackageName(); TN tn = mTN; tn.mNextView = mNextView; try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty } }
/** * Close the view if it's showing, or don't show it if it isn't showing yet. * You do not normally have to call this.Normally view will disappear on its own * after the appropriate duration. */ public void cancel() { mTN.cancel(); }
通過簡單檢視Toast 原始碼得知一些資訊
- Toast 實現沒有對同一條訊息顯示進行過濾,show 方法呼叫通知服務 enqueueToast 顯示資訊。
- Toast 佈局很簡單,就一個LinearLayout 的圓角佈局和顯示資訊的陰影效果。
android:background="?android:attr/toastFrameBackground"
android:shadowColor="#BB000000" android:shadowRadius="2.75"
顯然,系統的Toast 不滿足我的需求,我需要一個自定義一個能夠過濾相同資訊的 Toast 。
自定義Toast 主要思想:
- 建立對應構造器。採用設計模式 Builder 模式構造一個需要的Toast。主要針對有個性化 Toast 需求提供,如自定義背景顏色,自定義字型顏色,自定義 Toast 背景佈局等。
- 在Toast 上一次顯示後沒有 cancel 前,介面某個位置多次觸發,只顯示同樣的Toast 資訊一次。取顯示資訊對應的 hashCode作為key,快取對應的Toast 資訊,如果我每次傳入的資訊都是同樣的則不做顯示,Handler 控制對應的顯示時間,即使取消 Toast 顯示,清除上次Toast 顯示資訊。
- Toast 背景圓角問題的一些思考。Android Toast 圓角佈局背景呼叫的Android 系統 xml 佈局,如果動態修改 View 佈局顏色會丟失 圓角。當然,採用簡單的自定義一個圓角佈局就可以解決問題的,具體圓角佈局程式碼可參照程式碼裡 ofollow,noindex">RoundLinearLayout.java ,具體使用方式太簡單了,這裡不做細說。
自定義相關程式碼展示:
private void initCustomToast(CustomToastBuilder builder) { runnable = RUNNABLES.get(text.hashCode()); if (isNull(runnable)) { customToast = new CustomToast(mContext); runnable = new ToastRunnable(customToast); RUNNABLES.put(text.hashCode(), runnable); long showTime = builder.showTime; if (showTime > DEFAULT_SHOW_TIME){ mHander.postDelayed(runnable, showTime); }else { mHander.postDelayed(runnable, DEFAULT_SHOW_TIME); } customToast.show(builder.customToastCreator,builder.text,builder.textResId); } }
public void show(CharSequence text) { if (!text.equals(TEXTS.get(text.hashCode()))) { TEXTS.put(text.hashCode(), text); systemToast = new SystemToast(mContext); runnable = new ToastRunnable(systemToast); mHander.postDelayed(runnable, DEFAULT_SHOW_TIME); systemToast.show(text); } }
private class ToastRunnable implements Runnable { private BaseToast toast; public ToastRunnable(BaseToast toast) { this.toast = toast; } @Override public void run() { toast.cancel(); TEXTS.remove(toast.getText().hashCode()); RUNNABLES.remove(toast.getText().hashCode()); toast = null; runnable = null; } }
圓角佈局:
@SuppressLint("WrongConstant") private void drawRoundDrawable() { Log.d(TAG, "drawRoundDrawable: "+cornesRadius); if (null == gradientDrawable) { return; } if (cornesRadius != 0) { gradientDrawable.setCornerRadius(cornesRadius); gradientDrawable.setGradientType(GradientDrawable.RECTANGLE); } else if (null != radii) { gradientDrawable.setCornerRadii(radii); gradientDrawable.setGradientType(GradientDrawable.RECTANGLE); } setBackgroundDrawable(); }
自定義 Toast 如何使用,程式碼(Kotlin)如下:
1. 最簡單的使用
JToast.getInstance(this).show("show 一個!")
2. 構造器的使用
var creator = SystemToastCreator.build() .shadowColor(Color.parseColor("#2F4F4F")) .shadowRadius(15f) .setTextColor(Color.parseColor("#ffffff")) .creator() JToast.build() .systemToastBuilder() .setToastCreator(creator) .setShowTime(2000) .setText("show creator!") .show(this)
var customCreator = CustomToastCreator.build() .setCustomView(R.layout.layout_toast) .setTextColor(Color.WHITE) .setBackgroundRound(true) .creator() JToast.build() .customToastBuilder() .setToastCreator(customCreator) .setShowTime(2000) .setText("show customCreator!",R.id.message) .show(this)
我自定義的程式碼都太簡單了,這裡就不太囉嗦了。不懂的也隨時可以聯絡我。
總結: 我總感覺我的實現方式不太友好,並不完善,歡迎各位評論區提出你們寶貴的建議,或直接GitHub 把你們的想法提交。非常感謝各位大神的批評指正!