草庐IT

Android 11适配指南之Toast解析

yechaoa 2023-03-28 原文

起源

在​​targetSdkVersion为30​​​的情况下,在​​Android 11​​​的​​小米10​​​手机上运行,调用​​ToastUtil​​的时候闪退报错:

null cannot be cast to non-null type android.widget.LinearLayout
且看已知条件:

  • targetSdkVersion 30
  • Android 11
  • 小米10

文末附Android 11适配手册

定位问题

ok,遇到问题,迅速定位。
我在原有的​​​Toast​​​调用上重新封装了一下,即​ToastUtil​。

所以很快就定位到问题所在了

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()
}
没错,就是这句进行了转换:

val linearLayout = toast!!.view as LinearLayout
代码也比较简单,拿到view之后只是设置了一下字体大小。

为什么这么写呢,且看接下来源码分析(非常简单)。

源码解析

我们一般的调用是这么写的:

Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
一行代码,也很容易能找到重点——​​makeText​​,没错,接下来从这里开始分析

compileSdkVersion 30之前

以​​compileSdkVersion 28​​​为例,​​makeText​​源码:

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;
}
这几行的代码重点在哪呢,在这:

View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
引用了一个布局来显示信息

这个layout也非常的简单:

<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>
根布局​​LinearLayout​​​ 和​​TextView​​显示文本。

所以才有了前面报错的这行代码:

val linearLayout = toast!!.view as LinearLayout
现在看来其实是没有错的,事实上运行在​​Android11​​以下也确实没问题。

​setView​​​、​​getView​​也是没问题的

/**
* 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

compileSdkVersion 30之后

重点来了,在​​compileSdkVersion 30​​之后,源码是有改动的

还是直接看重点​​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;
}
}
嗯?view的获取方式变了,原来是​​inflate​​的方式,现在是

View v = ToastPresenter.getTextToastView(context, text);
ok,继续看​​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);
return view;
}
}
到这里是不是有点熟悉了,没错,跟​​compileSdkVersion 28​​​中的源码差不多,但是layout变成​​常量​​​了,且有​​@VisibleForTesting​​​注解,不过​​xml​​代码还是一样的。

而且​​setView​​​、​​getView​​也弃用的

/**
* 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;
}
直接来看​​注释​​的重点:

@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.

大意:
自定义toast view已经弃用,你可以创建一个​​​标准的toast​​​,或者用​​Snackbar​​​。
从Android​​​R​​开始,将不再显示自定位toast view。

Android R 也就是Android11,具体的版本对应关系查看

这里有同学可能会有一些想法,既然​​getView​​​弃用了,那我可不可以像系统一样通过ToastPresenter.getTextToastView来获取呢,很遗憾,是不行的,​​ToastPresenter​​​是​​@hide​​。。

适配方案

综上所诉,适配方案也了然于心了。

方案一

使用标准的toast

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

方案二

使用Snackbar

Snackbar的使用跟Toast差不多,更多查看这个。

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

方案三

不使用系统的toast,但可以借鉴来写一个自定义view

大致思路:

  • 初始化引用自定义布局
  • 编写一些公开的set、get属性
  • 加上进入进出动画
  • 开始/结束显示倒计时

等有空了再来补一下这个。。

Android 11开发手册

​《Android 11 开发者手册》​

最后

写作不易,如果对你有用,点个赞呗 ^ _ ^


有关Android 11适配指南之Toast解析的更多相关文章

  1. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 用逗号、双引号和编码解析 csv - 2

    我正在使用ruby​​1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\

  4. ruby-on-rails - 我更新了 ruby​​ gems,现在到处都收到解析树错误和弃用警告! - 2

    简而言之错误:NOTE:Gem::SourceIndex#add_specisdeprecated,useSpecification.add_spec.Itwillberemovedonorafter2011-11-01.Gem::SourceIndex#add_speccalledfrom/opt/local/lib/ruby/site_ruby/1.8/rubygems/source_index.rb:91./opt/local/lib/ruby/gems/1.8/gems/rails-2.3.8/lib/rails/gem_dependency.rb:275:in`==':und

  5. ruby - 安装libv8(3.11.8.13)出错,Bundler无法继续 - 2

    运行bundleinstall后出现此错误:Gem::Package::FormatError:nometadatafoundin/Users/jeanosorio/.rvm/gems/ruby-1.9.3-p286/cache/libv8-3.11.8.13-x86_64-darwin-12.gemAnerroroccurredwhileinstallinglibv8(3.11.8.13),andBundlercannotcontinue.Makesurethat`geminstalllibv8-v'3.11.8.13'`succeedsbeforebundling.我试试gemin

  6. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  7. ruby - 用 YAML.load 解析 json 安全吗? - 2

    我正在使用ruby2.1.0我有一个json文件。例如:test.json{"item":[{"apple":1},{"banana":2}]}用YAML.load加载这个文件安全吗?YAML.load(File.read('test.json'))我正在尝试加载一个json或yaml格式的文件。 最佳答案 YAML可以加载JSONYAML.load('{"something":"test","other":4}')=>{"something"=>"test","other"=>4}JSON将无法加载YAML。JSON.load("

  8. ruby - 如何使用 Nokogiri 解析纯 HTML 表格? - 2

    我想用Nokogiri解析HTML页面。页面的一部分有一个表,它没有使用任何特定的ID。是否可以提取如下内容:Today,3,455,34Today,1,1300,3664Today,10,100000,3444,Yesterday,3454,5656,3Yesterday,3545,1000,10Yesterday,3411,36223,15来自这个HTML:TodayYesterdayQntySizeLengthLengthSizeQnty345534345456563113003664354510001010100000344434113622315

  9. python - 帮我找到合适的 ruby​​/python 解析器生成器 - 2

    我使用的第一个解析器生成器是Parse::RecDescent,它的指南/教程很棒,但它最有用的功能是它的调试工具,特别是tracing功能(通过将$RD_TRACE设置为1来激活)。我正在寻找可以帮助您调试其规则的解析器生成器。问题是,它必须用python或ruby​​编写,并且具有详细模式/跟踪模式或非常有用的调试技术。有人知道这样的解析器生成器吗?编辑:当我说调试时,我并不是指调试python或ruby​​。我指的是调试解析器生成器,查看它在每一步都在做什么,查看它正在读取的每个字符,它试图匹配的规则。希望你明白这一点。赏金编辑:要赢得赏金,请展示一个解析器生成器框架,并说明它的

  10. ruby - ri 有空文件 – Ubuntu 11.10, Ruby 1.9 - 2

    我正在运行Ubuntu11.10并像这样安装Ruby1.9:$sudoapt-getinstallruby1.9rubygems一切都运行良好,但ri似乎有空文档。ri告诉我文档是空的,我必须安装它们。我执行此操作是因为我读到它会有所帮助:$rdoc--all--ri现在,当我尝试打开任何文档时:$riArrayNothingknownaboutArray我搜索的其他所有内容都是一样的。 最佳答案 这个呢?apt-getinstallri1.8编辑或者试试这个:(非rvm)geminstallrdocrdoc-datardoc-da

随机推荐