The origin of
When ToastUtil is invoked on an Android 11 Mi 10 phone with targetSdkVersion of 30, an error appears:
null cannot be cast to non-null type android.widget.LinearLayout
Copy the code
Let’s look at the known conditions:
- targetSdkVersion 30
- Android 11
- Millet 10
The Android 11 adaptation manual is attached at the end of the document
Location problem
Ok, we have a problem. Locate it quickly. I repackaged the old Toast call, ToastUtil.
So the problem was quickly identified
private fun createToast(msg: String) {
if (toast == null) {
toast = Toast.makeText(YUtils.getApp().applicationContext, msg, Toast.LENGTH_SHORT)
} else{ toast!! .setText(msg) }vallinearLayout = toast!! .viewas LinearLayout
val messageTextView = linearLayout.getChildAt(0) as TextView
messageTextView.textSize = 15ftoast!! .show() }Copy the code
That’s right, that’s the translation:
vallinearLayout = toast!! .viewas LinearLayout
Copy the code
The code is pretty simple, you get the view and you just set the font size.
To see why, look at the next source code analysis (very simple).
The source code parsing
Our usual call would look something like this:
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
Copy the code
One line of code, and it’s easy to get to the point — makeText, yes, and that’s where you start
CompileSdkVersion 30 before
For example, compileSdkVersion 28, the makeText source 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;
}
Copy the code
What’s the point of these lines of code? Here it is:
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
Copy the code
A layout is referenced to display the information
The 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>
Copy the code
The root layout LinearLayout and TextView display text.
That’s where the previous error came in:
vallinearLayout = toast!! .viewas LinearLayout
Copy the code
Now it looks like there’s nothing wrong with it, and in fact it runs on Android11 or below.
SetView and getView are also fine
/**
* Set the view to show.
* @see #getView
*/
public void setView(View view) {
mNextView = view;
}
/**
* Return the view.
* @see #setView
*/
public View getView() {
return mNextView;
}
Copy the code
Author: yechaoa
After compileSdkVersion 30
The point is that after compileSdkVersion 30, the source code has changed
Let’s go straight to the point 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;
returnresult; }}Copy the code
Huh? View access has changed. View access was inflate, but now it is
View v = ToastPresenter.getTextToastView(context, text);
Copy the code
Ok, continue to see ToastPresenter 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);
returnview; }}Copy the code
This is a bit familiar. Yes, it’s the same source code as in compileSdkVersion 28, but the layout is constant and has the @visibleForTesting annotation, but the XML code is the same.
And setView and getView are 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;
}
Copy the code
Let’s get straight to the point:
@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.
Copy the code
Custom Toast view is deprecated, you can create a standard toast, or use Snackbar. Starting with Android R, self-positioned Toast Views will no longer be displayed.
Android R is also Android11, the specific version of the corresponding relationship to view
There are students might have some ideas, since getView abandoned, that I could like system through ToastPresenter getTextToastView to obtain, unfortunately, is not enough, ToastPresenter is @ hide..
Adaptation scheme
To sum up, the adaptation scheme is clear.
Plan a
Use the standard TOAST
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
Copy the code
Scheme 2
Use the Snackbar
Snackbar is used in much the same way as Toast, check this out more.
Snackbar.make(view, "Added to itinerary", Snackbar.LENGTH_SHORT).show()
Copy the code
Plan 3
Don’t use the system toast, but use it to write a custom view
General idea:
- Initialize a reference to a custom layout
- Write some public set and get properties
- Add in and out animation
- Start/end display countdown
I’ll fix this when I have time.
Android 11 development manual
Android 11 Developer Manual
The last
Writing is not easy, if it works for you, give it a like ^ _ ^