• 第一行代码第三版Kotlin Fragment 228页练习


      书225页——Fragment的最佳实战:一个简易版的新闻应用

    项目说明

      Fragment的产生是为了更好的适应平板界面。练习内容,主要是在手机和平板端分别展示不同的页面。以新闻为例,分为标题部分和内容两部分,在平板上MainActivity直接加载两个Fragment,在手机上需要两个Activity分别加载两个Fragment。为了让newsContentFragment达到复用的效果,分别在MainActivity和NewsContentActivity上加载。有关平板与手机加载页面的是通过新建 layout-sw600dp 实现的。

    实现效果

    项目结构

      

    遇到问题

    有关 NewsContentActivity 中的代码问题

    布局文件 activity_news_content.xml

    <LinearLayout 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"
        tools:context=".activity.NewsContentActivity">
        
        <fragment
            android:id="@+id/newsContentFrag"
            android:name="com.vertex.myapplication.fragment.NewsContentFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            >
        </fragment>
    
    </LinearLayout>

    Activity代码 NewsContentActivity.kt

    	val title = intent.getStringExtra("news_title")
            val content = intent.getStringExtra("news_content")
            if (title != null && content != null) {
                val fragment =  newsContentFrag as NewsContentFragment;
                fragment.refresh(title, content)
             }

     有关书上的写法是这样的,但由于无法使用 kotlin-android-extensions 所以我们需要自己去获取控件。插件内部的逻辑还是findViewById呀,于是···你人才般的延伸了以下代码:

            val title = intent.getStringExtra("news_title")
            val content = intent.getStringExtra("news_content")
            if (title != null && content != null) {
                val fragment = findViewById<View>(R.id.newsContentFrag) as NewsContentFragment;
                fragment.refresh(title, content)
            }

     直到项目运行起来报错:ClassCastException :xxx can not cast NewsContentFragment

    细细想来,findViewById 返回的是一个View啊,而NewsContentFragment不是一个View啊。那么Java里面是怎么写的,哦!Java里面就没有这种写法,是通过FragmentManager 去动态绑定的吧。简直学费了···

    于是,毫不犹豫的写下以下代码:

            val title = intent.getStringExtra("news_title")
            val content = intent.getStringExtra("news_content")
            if (title != null && content != null) {
                val beginTransaction = supportFragmentManager.beginTransaction()
                val newsContentFragment = NewsContentFragment();
                newsContentFragment.refresh(title, content)
                beginTransaction.replace(R.id.newsContentFrag, newsContentFragment)
                beginTransaction.commit()
            }

    重点来了,NewsContentFragment页面展示出来了,但是数据没绑定上。回头看一下 NewsContentFragment 中的代码

    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            Log.d("TAG", "onCreateView: --------")
            // Inflate the layout for this fragment
            val view = inflater.inflate(R.layout.fragment_news_content, container, false).apply {
                initView(
                    this
                )
            }
            return view
        }
        var newsTitle: TextView? = null
        var newsContent: TextView? = null
        var contentLayout :LinearLayout? = null
        private fun initView(view: View?) {
            newsTitle = view?.findViewById<TextView>(R.id.newsTitle)!!
            newsContent = view.findViewById<TextView>(R.id.newsContent)
            contentLayout = view.findViewById<LinearLayout>(R.id.contentLayout)
        }
    
        fun refresh(title: String, content: String) {
            contentLayout?.visibility = View.VISIBLE;
            newsTitle?.text = title
            newsContent?.text = content
        }

    也是因为 kotlin-android-extensions 插件不能用的原因,所以我把控件初始化放在 onCreateView 中,refresh 还是保留原来的功能方法。

    那么问题来了,onCreateView 属于Fragment的生命周期,不是构造函数,不是 new Fragment 就能执行的,那么直接new Fragment 之后立即调用了refresh 方法,此时控件并没有初始化啊!

    那么那么既然这样,把 newsContentFragment.refresh(title, content) 的调用放在 commit() 的后面,让 fragment 先绑定上页面再去调用refresh 绑定数据,岂不完美?

            val title = intent.getStringExtra("news_title")
            val content = intent.getStringExtra("news_content")
            if (title != null && content != null) {
                val beginTransaction = supportFragmentManager.beginTransaction()
                val newsContentFragment = NewsContentFragment();
                beginTransaction.replace(R.id.newsContentFrag, newsContentFragment)
                beginTransaction.commit()
                newsContentFragment.refresh(title, content)
            }

    但结果并未如常所愿,还是先执行了refresh 方法后执行了 initView 方法,我想···beginTransaction.commit() 是开了个线程去干活吧··· 异步了

    折磨着发现,还有一个 commitNow 方法,那 commitNow 会不会同步呢,我看了网上一位同学的源码分析,commitNow 应该是立即执行,不放在队列中,但也许···这并不代表着同步吧(我没再继续研究)

    突然觉得这段代码有点狗啊···那么如何处理呢

    思路1:做一个延迟,等待fragment执行onCreateView 再调用 refresh

    思路2fragment中通过refresh将title和content 用类变量存储起来,如果控件不为空则直接绑定。在initView中添加绑定的方法,如果数据不为空则进行绑定。

    ···

    不继续了

  • 相关阅读:
    进位的算法
    java操作mysql中的编码问题解决
    ajax
    java.lang.NoClassDefFoundError: org/objectweb/asm/Type
    java.lang.NoSuchFieldError: deferredExpression
    java.lang.NoClassDefFoundError: antlr/ANTLRException
    Missing message for key "err1" in bundle "(default bundle)" for locale zh_CN
    接口和抽象类
    overload和override
    public,protected,friendly,private的访问权限
  • 原文地址:https://www.cnblogs.com/xqz0618/p/15432431.html
Copyright © 2020-2023  润新知