【Toast】關(guān)于Android Toast 的一丁點認識

圖片來自百度圖片,如有侵權(quán),請聯(lián)系作者刪除

??Android Toast 在項目中是普遍應用的一個控件,應該也是一個使用非常簡單的控件。這里,有人會問,Toast 這么簡單干嘛還要花時間去學習呢,項目里直接 Toast.makeText 就可以了啊。我的回答是:“因為簡單,所以需要去學習,去了解”。

為什么我還要去做一個自定義Toast?

Android Toast 如果在某個場景交互中,一不小心多次觸發(fā)了 Toast show 結(jié)果是不停地在你屏幕顯示同樣的 Toast 信息。我是個有時間潔癖的人,不喜歡因為你多顯示一次 Toast 信息浪費我一秒鐘或兩秒鐘,在碎片時間學習中,我更希望花一秒鐘看到我更喜歡看到的知識,集中精力去得到我想要的東西。
廢話少扯,進入主題。
先看看Android 系統(tǒng) Toast 的實現(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();
    }

??通過簡單查看Toast 源碼得知一些信息

  1. Toast 實現(xiàn)沒有對同一條消息顯示進行過濾,show 方法調(diào)用通知服務 enqueueToast 顯示信息。
  2. Toast 布局很簡單,就一個LinearLayout 的圓角布局和顯示信息的陰影效果。
android:background="?android:attr/toastFrameBackground"
 android:shadowColor="#BB000000"
 android:shadowRadius="2.75"

顯然,系統(tǒng)的Toast 不滿足我的需求,我需要一個自定義一個能夠過濾相同信息的 Toast 。

自定義Toast 主要思想:

  1. 創(chuàng)建對應構(gòu)造器。采用設計模式 Builder 模式構(gòu)造一個需要的Toast。主要針對有個性化 Toast 需求提供,如自定義背景顏色,自定義字體顏色,自定義 Toast 背景布局等。
  2. 在Toast 上一次顯示后沒有 cancel 前,界面某個位置多次觸發(fā),只顯示同樣的Toast 信息一次。取顯示信息對應的 hashCode作為key,緩存對應的Toast 信息,如果我每次傳入的信息都是同樣的則不做顯示,Handler 控制對應的顯示時間,即使取消 Toast 顯示,清除上次Toast 顯示信息。
  3. Toast 背景圓角問題的一些思考。Android Toast 圓角布局背景調(diào)用的Android 系統(tǒng) xml 布局,如果動態(tài)修改 View 布局顏色會丟失 圓角。當然,采用簡單的自定義一個圓角布局就可以解決問題的,具體圓角布局代碼可參照代碼里 RoundLinearLayout.java,具體使用方式太簡單了,這里不做細說。

自定義相關(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. 最簡單的使用

JToast.getInstance(this).show("show 一個!") 

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)

我自定義的代碼都太簡單了,這里就不太啰嗦了。不懂的也隨時可以聯(lián)系我。

??總結(jié): 我總感覺我的實現(xiàn)方式不太友好,并不完善,歡迎各位評論區(qū)提出你們寶貴的建議,或直接GitHub 把你們的想法提交。非常感謝各位大神的批評指正!

最后給出 GitHub 鏈接: JToast

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容