第一行代码第三版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中添加绑定的方法,如果数据不为空则进行绑定。

···

不继续了

 

posted @ 2021-10-21 15:42  茄子鱼  阅读(520)  评论(2编辑  收藏  举报