??Android Toast 在項(xiàng)目中是普遍應(yīng)用的一個(gè)控件蜻展,應(yīng)該也是一個(gè)使用非常簡(jiǎn)單的控件魁亦。這里渔隶,有人會(huì)問(wèn),Toast 這么簡(jiǎn)單干嘛還要花時(shí)間去學(xué)習(xí)呢洁奈,項(xiàng)目里直接 Toast.makeText 就可以了啊间唉。我的回答是:“因?yàn)楹?jiǎn)單,所以需要去學(xué)習(xí)利术,去了解”呈野。
為什么我還要去做一個(gè)自定義Toast?
Android Toast 如果在某個(gè)場(chǎng)景交互中印叁,一不小心多次觸發(fā)了 Toast show 結(jié)果是不停地在你屏幕顯示同樣的 Toast 信息被冒。我是個(gè)有時(shí)間潔癖的人,不喜歡因?yàn)槟愣囡@示一次 Toast 信息浪費(fèi)我一秒鐘或兩秒鐘轮蜕,在碎片時(shí)間學(xué)習(xí)中昨悼,我更希望花一秒鐘看到我更喜歡看到的知識(shí),集中精力去得到我想要的東西跃洛。
廢話少扯率触,進(jìn)入主題。
先看看Android 系統(tǒng) Toast 的實(shí)現(xiàn)源碼:
Toast MakeText:
/**
* Make a standard toast that just contains a text view.
*
* @param context The context to use. Usually your {@link android.app.Application}
* or {@link android.app.Activity} object.
* @param text The 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();
}
??通過(guò)簡(jiǎn)單查看Toast 源碼得知一些信息
- Toast 實(shí)現(xiàn)沒(méi)有對(duì)同一條消息顯示進(jìn)行過(guò)濾汇竭,show 方法調(diào)用通知服務(wù) enqueueToast 顯示信息葱蝗。
- Toast 布局很簡(jiǎn)單,就一個(gè)LinearLayout 的圓角布局和顯示信息的陰影效果细燎。
android:background="?android:attr/toastFrameBackground"
android:shadowColor="#BB000000"
android:shadowRadius="2.75"
顯然两曼,系統(tǒng)的Toast 不滿(mǎn)足我的需求,我需要一個(gè)自定義一個(gè)能夠過(guò)濾相同信息的 Toast 玻驻。
自定義Toast 主要思想:
- 創(chuàng)建對(duì)應(yīng)構(gòu)造器悼凑。采用設(shè)計(jì)模式 Builder 模式構(gòu)造一個(gè)需要的Toast。主要針對(duì)有個(gè)性化 Toast 需求提供击狮,如自定義背景顏色佛析,自定義字體顏色,自定義 Toast 背景布局等彪蓬。
- 在Toast 上一次顯示后沒(méi)有 cancel 前,界面某個(gè)位置多次觸發(fā)捺萌,只顯示同樣的Toast 信息一次档冬。取顯示信息對(duì)應(yīng)的 hashCode作為key膘茎,緩存對(duì)應(yīng)的Toast 信息,如果我每次傳入的信息都是同樣的則不做顯示酷誓,Handler 控制對(duì)應(yīng)的顯示時(shí)間披坏,即使取消 Toast 顯示,清除上次Toast 顯示信息盐数。
- Toast 背景圓角問(wèn)題的一些思考棒拂。Android Toast 圓角布局背景調(diào)用的Android 系統(tǒng) xml 布局,如果動(dòng)態(tài)修改 View 布局顏色會(huì)丟失 圓角玫氢。當(dāng)然帚屉,采用簡(jiǎn)單的自定義一個(gè)圓角布局就可以解決問(wèn)題的,具體圓角布局代碼可參照代碼里 RoundLinearLayout.java漾峡,具體使用方式太簡(jiǎn)單了攻旦,這里不做細(xì)說(shuō)。
自定義相關(guān)代碼展示:
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. 最簡(jiǎn)單的使用
JToast.getInstance(this).show("show 一個(gè)牢屋!")
2. 構(gòu)造器的使用
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)
我自定義的代碼都太簡(jiǎn)單了,這里就不太啰嗦了槽袄。不懂的也隨時(shí)可以聯(lián)系我烙无。
??總結(jié): 我總感覺(jué)我的實(shí)現(xiàn)方式不太友好,并不完善遍尺,歡迎各位評(píng)論區(qū)提出你們寶貴的建議截酷,或直接GitHub 把你們的想法提交。非常感謝各位大神的批評(píng)指正狮鸭!