第一行代碼第三版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中添加綁定的方法,如果數據不爲空則進行綁定。

···

不繼續了

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章