Toast analysis of Android 11 adaptation guide

origin

In targetSdkVersion为30the case of running Android 11on 小米10the mobile phone of, ToastUtilit flashed back and reported an error when calling :

null cannot be cast to non-null type android.widget.LinearLayout

Why is it so detailed, because these conditions are necessary:

  • targetSdkVersion 30
  • Android 11
  • Mi 10

Similarly targetSdkVersion, 华为P30 Proit is indeed normal to run on Android 11. Why? According to the manufacturers' continuous improvement over the years 通知渠道, not only we need 适配new Android versions, but manufacturers also need to adapt, so it can only be summarized as the manufacturers. Some processing.

Android 11 adaptation manual attached at the end of the article

Positioning problem

ok, if you encounter a problem, locate it quickly.
I Toastrepackaged the original call, namely ToastUtil .

So the problem is quickly located

    private fun createToast(msg: String) {
    
    
        if (toast == null) {
    
    
            toast = Toast.makeText(YUtils.getApp().applicationContext, msg, Toast.LENGTH_SHORT)
        } else {
    
    
            toast!!.setText(msg)
        }
        val linearLayout = toast!!.view as LinearLayout
        val messageTextView = linearLayout.getChildAt(0) as TextView
        messageTextView.textSize = 15f
        toast!!.show()
    }

That's right, this sentence is converted:

val linearLayout = toast!!.view as LinearLayout

The code is also relatively simple, just set the font size after getting the view.

Why do you write this? Let's look at the next source code analysis (very simple).

Source code analysis

Our general call is written like this:

Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()

One line of code, it’s easy to find the key point- makeTextyes, let’s start the analysis from here

compileSdkVersion 30 before

Take for compileSdkVersion 28example, the makeTextsource code:

    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;
    }

What is the key point of these lines of code, here:

View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);

Reference to a layout to display information

This layout is also very simple:

<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_marginHorizontal="24dp"
        android:layout_marginVertical="15dp"
        android:layout_gravity="center_horizontal"
        android:textAppearance="@style/TextAppearance.Toast"
        android:textColor="@color/primary_text_default_materiaal_light"/>

</LinearLayout>

Root layout LinearLayoutand TextViewdisplay text.

That's why there is this line of code that was reported earlier:

val linearLayout = toast!!.view as LinearLayout

Now it seems that there is nothing wrong, in fact, it Android11is really no problem to run the following.

setView, getViewAlso no problem

    /**
     * Set the view to show.
     * @see #getView
     */
    public void setView(View view) {
    
    
        mNextView = view;
    }

    /**
     * Return the view.
     * @see #setView
     */
    public View getView() {
    
    
        return mNextView;
    }

author:yechaoa

After compileSdkVersion 30

Here comes the point, compileSdkVersion 30after that, the source code has been changed

Or look at the key points directly makeText:

    public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
            @NonNull CharSequence text, @Duration int duration) {
    
    
        if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
    
    
            Toast result = new Toast(context, looper);
            result.mText = text;
            result.mDuration = duration;
            return result;
        } else {
    
    
            Toast result = new Toast(context, looper);
            View v = ToastPresenter.getTextToastView(context, text);
            result.mNextView = v;
            result.mDuration = duration;

            return result;
        }
    }

Ok? The way to get the view has changed, the original inflateway is, now it is

View v = ToastPresenter.getTextToastView(context, text);

ok, keep watchingToastPresenter.getTextToastView

public class ToastPresenter {
    
    
    ...

    @VisibleForTesting
    public static final int TEXT_TOAST_LAYOUT = R.layout.transient_notification;

    /**
     * Returns the default text toast view for message {@code text}.
     */
    public static View getTextToastView(Context context, CharSequence text) {
    
    
        View view = LayoutInflater.from(context).inflate(TEXT_TOAST_LAYOUT, null);
        TextView textView = view.findViewById(com.android.internal.R.id.message);
        textView.setText(text);
        return view;
    }
 }

Isn't it a bit familiar here? Yes, it is similar compileSdkVersion 28to the source code in, but the layout has changed 常量, and there are @VisibleForTestingannotations, but the xmlcode is still the same.

Moreover setView, getViewalso deprecated

    /**
     * Set the view to show.
     *
     * @see #getView
     * @deprecated Custom toast views are deprecated. Apps can create a standard text toast with the
     *      {@link #makeText(Context, CharSequence, int)} method, or use a
     *      <a href="{@docRoot}reference/com/google/android/material/snackbar/Snackbar">Snackbar</a>
     *      when in the foreground. Starting from Android {@link Build.VERSION_CODES#R}, apps
     *      targeting API level {@link Build.VERSION_CODES#R} or higher that are in the background
     *      will not have custom toast views displayed.
     */
    @Deprecated
    public void setView(View view) {
    
    
        mNextView = view;
    }

    /**
     * Return the view.
     *
     * <p>Toasts constructed with {@link #Toast(Context)} that haven't called {@link #setView(View)}
     * with a non-{@code null} view will return {@code null} here.
     *
     * <p>Starting from Android {@link Build.VERSION_CODES#R}, in apps targeting API level {@link
     * Build.VERSION_CODES#R} or higher, toasts constructed with {@link #makeText(Context,
     * CharSequence, int)} or its variants will also return {@code null} here unless they had called
     * {@link #setView(View)} with a non-{@code null} view. If you want to be notified when the
     * toast is shown or hidden, use {@link #addCallback(Callback)}.
     *
     * @see #setView
     * @deprecated Custom toast views are deprecated. Apps can create a standard text toast with the
     *      {@link #makeText(Context, CharSequence, int)} method, or use a
     *      <a href="{@docRoot}reference/com/google/android/material/snackbar/Snackbar">Snackbar</a>
     *      when in the foreground. Starting from Android {@link Build.VERSION_CODES#R}, apps
     *      targeting API level {@link Build.VERSION_CODES#R} or higher that are in the background
     *      will not have custom toast views displayed.
     */
    @Deprecated
    @Nullable public View getView() {
    
    
        return mNextView;
    }

注释Points to look directly at:

@deprecated Custom toast views are deprecated. Apps can create a standard text toast with the
{@link #makeText(Context, CharSequence, int)} method, or use a Snackbar
when in the foreground. Starting from Android {@link Build.VERSION_CODES#R}, apps
targeting API level {@link Build.VERSION_CODES#R} or higher that are in the background
will not have custom toast views displayed.

Main idea:
The custom toast view is deprecated, you can create one 标准的toastor use it Snackbar.
Starting from Android R, the self-positioning toast view will no longer be displayed.

Android R is Android11, check the specific version correspondence

Some students here may have some ideas. Since it is getViewdeprecated, can I get it through ToastPresenter.getTextToastView like the system does? Unfortunately, it doesn't work, ToastPresenteryes @hide. .

Adaptation scheme

In summary, the adaptation plan is also clear.

Option One

Use standard toast

Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()

Option II

Use Snackbar

The use of Snackbar is similar to Toast, check this out more .

Snackbar.make(view, "已加入行程", Snackbar.LENGTH_SHORT).show()

third solution

Do not use the system toast, but you can learn to write a custom view

General idea:

  • Initialize reference custom layout
  • Write some public set and get attributes
  • Plus enter and exit animation
  • Start/end display countdown

I'll make up this when I have time. .

Android 11 Development Manual

"Android 11 Developer's Manual"

At last

Writing is not easy, if it is useful to you, please give me a thumbs up ^ _ ^

Guess you like

Origin blog.csdn.net/yechaoa/article/details/113186782