不保留后台情况下fragment设置Drawable文件xml形式 AndroidKotlin
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
如果想要看如何添加drawable文件请直接跳转后边
前言
在开发过程中,当用户在开发者模式中打开了不保留后台的选项后,我们所写出来的app经常会出现闪退的情况【有用到fragment的情况】,原因就在于没有按照规范去书写当前的app
一、先说整个问题的解决方式
- 不保留后台的解决------》需要给使用到的fragment添加一个无参构造方法
- 添加Drawable文件------》通过getResourceId获取资源Id后再通过对应的方法去加载当前的资源(前提是资源能够对应上)
二、为什么不保留后台会出问题?
对于不保留后台,其实他会将整个APP杀死,等到再次回到当前的APP后,他会进行一次重建,而当我们所写的fragment不够规范时【没有注意到这种情况】,就会出现崩溃问题
那么问题的分析与解决方式请看下方
三、不保留后台问题及解决
1.这是重建时报的错误
以下的错误信息是在开启了不保留后台情况下APP重建时调用的:
Caused by: android.view.InflateException: Binary XML file line #9 in com.xxx.test:layout/activity_main: Binary XML file line #9 in com.xxx.test:layout/activity_main: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #9 in com.xxx.test:layout/activity_main: Error inflating class fragment
Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment com.xxx.XXXFragment: could not find Fragment constructor
2.错误情况定位分析
通过错误信息,我们可以很快定位到当前的错误位置在哪里,以下的代码是APP重建时将会调用的代码(出错代码段):
@Deprecated
@NonNull
public static Fragment instantiate(@NonNull Context context, @NonNull String fname,
@Nullable Bundle args) {
try {
Class<? extends Fragment> clazz = FragmentFactory.loadFragmentClass(
context.getClassLoader(), fname);
Fragment f = clazz.getConstructor().newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.setArguments(args);
}
return f;
} catch (java.lang.InstantiationException e) {
throw new InstantiationException("Unable to instantiate fragment " fname
": make sure class name exists, is public, and has an"
" empty constructor that is public", e);
} catch (IllegalAccessException e) {
throw new InstantiationException("Unable to instantiate fragment " fname
": make sure class name exists, is public, and has an"
" empty constructor that is public", e);
} catch (NoSuchMethodException e) {
throw new InstantiationException("Unable to instantiate fragment " fname
": could not find Fragment constructor", e);
} catch (InvocationTargetException e) {
throw new InstantiationException("Unable to instantiate fragment " fname
": calling Fragment constructor caused an exception", e);
}
}
查找NoSuchMethodException 的出现位置后,不难得到,问题就出在 clazz.getConstructor().newInstance() 这里,如果有一定了解的话(不了解现在也可以知道了),这样的调用形式就是标准的反射获取构造方法并且创建一个实例
public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
return getConstructor0(parameterTypes, Member.PUBLIC);
}
他会去调用getConstructor0方法,要注意parameterTypes为null,因为上层调用的是空,并且要注意,这里的类型为PUBLIC,后续创建时要记得constructor的限制符。分析到此处,了解到会调用Public的空构造得到一个实例就可以了,后面部分才疏学浅,暂时打住。
这里分析完毕后,也就是我们的构造方法会出现问题,没有空构造。
3.解决方法
解决方式很简单,只需要创建fragment的空构造就可以了,但是这就带来一个传值问题,如果我还有数据该怎么办呢,比如我想设置某些属性值,那该怎么构建我的实例呢:
注:这种情况其实是自己这边的需求,用viewPager实现了对应的自定义控件,里面用了同一类型的fragment,保证fragment的重建,需要为空参构造,但是又有需要设置一些自定义的样式,所以会出现传值问题。
3.1 转换思路,拿drawable文件的资源id
在整个加载过程中,只有最后传输到需要设置的地方,我们才需要找到一个真正的 drawable 文件,其他的时候只需要一个代指就可以了。最好的标识符就是资源文件的对应资源ID。
当然,Android 中已经有对应的方法去拿到这个值。
就是其中的 getResourceId(),它可以拿到当前资源的资源 ID,这个是和最终生成的 R.txt 中相对应的,目前测试过 xml 背景,color 颜色,都可以获取到目标设定值:具体用法如下
当然,还有一个方法 getSourceResourceId(),它可以拿到当前设定资源的主体 xml 文件值(即,在 A.xml 中为 app:test 属性设定 B.xml,调用该方法获取 test 属性的资源Id值,拿到的结果是 A.xml 的资源 Id,用法和 getResourceId() 基本相同)
1. attrs.xml中定义属性
<declare-styleable name="TestView">
<attr name="test" format="inference"/>
</declare-styleable>
2. 在xml中使用
A.xml中有如下
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TestView
android:layout_width="match_parent"
android:layout_height="40dp"
app:test="@drawable/test_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
3. 在定义的TestView中获取整个值
val testBackground : Int = a.getResourceId(R.styleable.TestView_test, 0)
4. 拿到的这个值,其实就是资源ID了,后续的操作就比较简单了,可以根据自己的最终效果去操作即可;本例中就是在这里将这个值传递给fragment用于后续的创建,此做法及完成了Drawable资源的转移。
3.2 构建fragment
采取的方案就是将当前的fragment采用另一种形式构建
class XXXFragment : Fragment() {
private var argument: String = ""
private var backgroundStyle: Int? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val a = arguments?.getParcelable<SelectTimeArguments>("ddd")
a?.let {
argument= it.argument
backgroundStyle= it.backgroundStyle
}
// 设置资源
context?.let{
xxx.backgroud = it.resources.getDrawable(backgroundStyle)
}
}
companion object {
/**
* 实例化
* @param userTimeZone 用户在App中设置的时区
* @param defaultTime 设置的默认时间,不设置默认为当前时间(格式 <时>:<分>,如 "10:40")
*/
@JvmStatic
fun newInstance(
argument: String,
backgroundStyle: Int? = null,
): XXXFragment {
val fragment = XXXFragment()
val bundle = Bundle()
bundle.putParcelable(
"test",
Arguments(
argument,
backgroundStyle
)
)
fragment.arguments = bundle
return fragment
}
}
}
@Parcelize
data class Arguments(
var argument: String,
var backgroundStyle: Int? = null,
) : Parcelable
后续我们只需要调用这个newInstance方法就可以获取到对应的fragment了,并且解决了drawable文件的设置问题。
总结
整篇文章整理了 fragment的标准构建方法,以及 getResourceId 的用法。
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgbjkej
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13