iT邦幫忙

2021 iThome 鐵人賽

DAY 24
0

今天要做的是從懸浮視窗還到預覽頁。

Day22中當我們啟動ObserveService時會把MainActivity給終止掉,因此今天要做的內容事實上就是重啟MainActivity後能直接回到PreviewFragment的過程。

nav_main.xml

啟動時會在WelcomeFragment,由於已經是在Ptt內的狀態,可以直接跳過LoginFragment到後面。另外為了達到PreviewFramgnet點擊返回後能順利回到SearchArticleFragment的路線,我新加的Navigation Action會是WelcomeFragmentSearchArticleFragment的路線,接著再另外回到PreviewFragment

在welcomeFragment下加入Action

<action
    android:id="@+id/action_welcomeFragment_to_searchArticleFragment"
    app:destination="@id/searchArticleFragment"
    app:popUpTo="@id/welcomeFragment"
    app:popUpToInclusive="true">
    <argument
        android:name="toPreview"
        android:defaultValue="true"
        app:argType="boolean"
        app:nullable="false" />
</action>

可以看到我加入的是一個帶有argument的Action,這部份請參考Pass data between destinations

在searchArticleFragment下加入Argument

<argument
    android:name="toPreview"
    android:defaultValue="false"
    app:argType="boolean"
    app:nullable="false" />

這是與welcomeFragment的argument對應的,但是在這邊的defaultValue設為false,這是為了避免從LoginFragment進來時被這個argument影響到。

searchArticleFragment_to_previewFragment加入argument

<argument
    android:name="isInArticle"
    android:defaultValue="false"
    app:argType="boolean"
    app:nullable="false" />

previewFragment中也加入一樣的Argument,這是因為從懸浮視窗回來時Ptt本來就已在文章裡面,不需要做原本PreviewFragmentDay16~Day17的內容中做的前置動作。

路線規劃好後就是處理程式碼的判斷了。

ObserveService

主要就是在點擊back時取消註冊updateRunnable並開啟新的MainActivity

binding.back.setOnClickListener {
    updateHandler.removeCallbacks(updateRunnable)
    val intent = Intent(this, MainActivity::class.java)
    intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
    startActivity(intent)
}

WelcomeFragment

首先需要判斷目前ObserviceService是否正在啟動中。
ObserviceService加入:

class ObserveService : Service() {

    companion object {
        public var isRunning = false
    }
    
    override fun onCreate() {
        super.onCreate()
        isRunning = true
        // ...
    }
    
    override fun onDestroy() {
        super.onDestroy()
        isRunning = false
        // ...
    }
}

接著就能直接判斷了:

class WelcomeFragment : Fragment() {
    //...
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        if (ObserveService.isRunning) {
            requireActivity().stopService(
                Intent(requireActivity(), ObserveService::class.java)
            )
            NavHostFragment.findNavController(this@WelcomeFragment)
                .navigate(R.id.action_welcomeFragment_to_searchArticleFragment)
            return
        }

        // ...
    }
}

ObserveService關閉並且使用剛剛新增的Action直接進到SearchArticleFragment,由於這個ActiontoPreview argument預設值為true,這邊就不需要做另外的操作了。

SearchArticleFragment

判斷若toPreviewtrue的話直接進入PreviewFragment。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    arguments?.run {
        if (SearchArticleFragmentArgs.fromBundle(this).toPreview) {
            this.clear()
            NavHostFragment.findNavController(this@SearchArticleFragment)
                .navigate(
                    SearchArticleFragmentDirections
                        .actionSearchArticleFragmentToPreviewFragment(
                            true
                        )
                )

            return@onViewCreated
        }
    }
    
    // ...
}

進入時記得要把isInArticle argument設為true

PreviewFragment

針對isInArticle做判斷即可。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // ...
    arguments?.run {
        if (PreviewFragmentArgs.fromBundle(this).isInArticle) {
            updateHandler.post(updateRunnable)
        } else {
            viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
                PttClient.getInstance().send("G")
                delay(100L)
                parseComments(PttClient.getInstance().getScreen())

                updateHandler.postDelayed(updateRunnable, updateInterval)
            }
        }
    }
}

PttClient

先講結論就是可能明後天會針對這個Class做修改了,具體要改成什麼樣子我還在思考中。這邊會提到是因為從PreviewFragment回到SearchArticleFragment時,我需要保留先前的搜尋狀態,否則當進入ObserveService、把MainActivity退出後,原本存在SearchArticleFragment內的searchTitleSetsearchAuthorSetcurrentBoard都會被清掉。為了保留這些狀態我是先把幾個變數放到PttClient這個singleton class中了。

    public var currentBoard = ""
    public val searchTitleSet = mutableSetOf<String>()
    public val searchAuthorSet = mutableSetOf<String>()

接著在SearchArticleFragment開啟時將值重新放入。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // ...
    binding.searchBoardInput.setText(PttClient.getInstance().currentBoard)
    // ...
    if ((PttClient.getInstance().searchAuthorSet.isNotEmpty()
                || PttClient.getInstance().searchTitleSet.isNotEmpty())
    ) {
        (requireActivity() as MainActivity).showLoading("")
        PttClient.getInstance().searchAuthorSet.forEach { addAuthorChip(it) }
        PttClient.getInstance().searchTitleSet.forEach { addTitleChip(it) }
        refreshSearch()
    }
}

最後就是如一開始所說的,PttClient的修改(or 重做)已經是勢在必行了,畢竟目前使用上也不少彆扭的地方。

目前畫面

https://imgur.com/BdCjWjS.gif

到目前為止基本上App的主要畫面和流程都已經出來了,接下來的幾天應該就是針對現有內容做修改/優化了。


上一篇
Day23 - 調整懸浮視窗大小
下一篇
Day25 - 加入簡單的動畫
系列文
花30天做個Android小專案30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言