• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

不保留后台情况下fragment设置Drawable文件xml形式 AndroidKotlin

武飞扬头像
盲目丶
帮助1

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

如果想要看如何添加drawable文件请直接跳转后边


前言

在开发过程中,当用户在开发者模式中打开了不保留后台的选项后,我们所写出来的app经常会出现闪退的情况【有用到fragment的情况】,原因就在于没有按照规范去书写当前的app


一、先说整个问题的解决方式

  1. 不保留后台的解决------》需要给使用到的fragment添加一个无参构造方法
  2. 添加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
系列文章
更多 icon
同类精品
更多 icon
继续加载