origin
In targetSdkVersion为30
the case of running Android 11
on 小米10
the mobile phone of, ToastUtil
it 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 Pro
it 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 Toast
repackaged 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- makeText
yes, let’s start the analysis from here
compileSdkVersion 30 before
Take for compileSdkVersion 28
example, 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;
}
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 LinearLayout
and TextView
display 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 Android11
is really no problem to run the following.
setView
, getView
Also 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 30
after 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 inflate
way 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 28
to the source code in, but the layout has changed 常量
, and there are @VisibleForTesting
annotations, but the xml
code is still the same.
Moreover setView
, getView
also 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 标准的toast
or 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 getView
deprecated, can I get it through ToastPresenter.getTextToastView like the system does? Unfortunately, it doesn't work, ToastPresenter
yes @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 ^ _ ^