Day13 - 使用Chip和ChipGroup显示搜寻项目

原本今天是想写解析文章列表的,不过思考了一下,为了让脉络顺一点,决定把今天的内容放到解析文章列表之前。

在前两天分别做了以文章标题和文章作者来搜寻文章的部分,我打算使用ChipChipGroup来显示搜寻的项目。

Layout

<!-- ... -->
<com.google.android.material.chip.ChipGroup
    android:id="@+id/titleChipGroup"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintEnd_toEndOf="@id/searchTitleInput"
    app:layout_constraintStart_toStartOf="@id/searchTitleInput"
    app:layout_constraintTop_toBottomOf="@id/searchTitleInput" />
<!-- ... -->
<com.google.android.material.chip.ChipGroup
    android:id="@+id/authorChipGroup"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintEnd_toEndOf="@id/searchAuthorInput"
    app:layout_constraintStart_toStartOf="@id/searchAuthorInput"
    app:layout_constraintTop_toBottomOf="@id/searchAuthorInput" />

在两个EditText底下分别插入一个ChipGroup,用来存放ChipChip则在程序码内动态新增。

Code

首先为了在确认有搜寻结果的状态下再行纪录,需要稍微改写一下前两天的搜寻方法。

前置修改

private suspend fun searchDetail(command: String): Boolean {
    // ...
    when (PttClient.getInstance().expect(searchTitleOrAuthorPattern)) {
        0 -> {
            // ...
            return false
        }
        1 -> {
            parseBoardArticle(PttClient.getInstance().getScreen())
            return true
        }
        else -> {
            // ...
            return false
        }
    }
}

private suspend fun searchDetailProcess(command: String): Boolean {
    when (PttClient.getInstance().expect(inBoardPattern)) {
        0 -> {
            // ...
            when (ret) {
                // ...
                2 -> { // "文章选读" -> 在看板内
                    return searchDetail(command)
                }
            }
            return false
        }
        2 -> {
            return searchDetail(command)
        }
        else -> {
            // ...
            return false
        }
    }
}

主要是多回传了布林值,并且把searchDetailProcess也改为suspend method,launch coroutine的部分提到click事件中处理。其余的内容与前两天差不多,就省略掉了。
以searchTitle的onClick事件为例

searchTitle.setOnClickListener {
    val word = searchTitleInput.text.toString()
    if (word.isEmpty()) return@setOnClickListener
    searchTitleInput.setText("")
    hideImm()

    viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
        val ret =
            withContext(Dispatchers.IO) { searchDetailProcess("/$word\r\n") }

        if (ret) {
            // Add chip.
        }
    }
}

Add chip

这部分的差异也只在填入的字串与目标ChipGroup,这边就也只放searchTitle的部分了

val chip = Chip(it.context)
chip.isCheckable = false
chip.text = word
chip.textSize = 16f
chip.typeface = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD)
chip.closeIcon =
    ResourcesCompat.getDrawable(
        resources,
        R.drawable.ic_baseline_clear_24,
        null
    )
chip.isCloseIconVisible = true
chip.setOnCloseIconClickListener {
    val text = (it as Chip).text
    searchTitleSet.remove(text)
    titleChipGroup.removeView(it)

    (requireActivity() as MainActivity).showLoading("")
    refreshSearch()
}
titleChipGroup.addView(chip)
searchTitleSet.add(word)

建立Chip其实也没有什麽特殊的地方,顶多就是多放一个clear的icon并设close事件。并且加搜寻的项目记在searchTitleSet中,在refreshSearch方法内重新整理目前状态。

refreshSearch

private val searchTitleSet = mutableSetOf<String>()
private val searchAuthorSet = mutableSetOf<String>()

private fun refreshSearch() {
    viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
        withContext(Dispatchers.IO) {
            PttClient.getInstance().send("qqqqqqqqqqqqq")
            delay(200L)
            if (searchTitleSet.isEmpty() && searchAuthorSet.isEmpty()) {
                PttClient.getInstance().printScreen()
                return@withContext
            }

            val board = searchBoardInput.text.toString().trim()
            if (board.isEmpty()) return@withContext
            PttClient.getInstance().send("s${board}\r\n")
            delay(200L)
            var ret = PttClient.getInstance().expect(inBoardPattern)
            while (ret == 1) {
                if (ret == 1) {
                    PttClient.getInstance().send("q")
                    delay(200L)
                    ret = PttClient.getInstance().expect(inBoardPattern)
                }
            }
            when (ret) {
                0 -> { // "【主功能表】" -> 在看板外,无法搜寻文章
                    Log.e(mTag, "Out of board.\n${PttClient.getInstance().getScreen()}")
                }
                2 -> { // "文章选读" -> 在看板内
                    searchTitleSet.forEach {
                        searchDetail("/$it\r\n")
                    }
                    searchAuthorSet.forEach {
                        searchDetail("a$it\r\n")
                    }
                }
            }
        }

        (requireActivity() as MainActivity).dismissLoading()
    }

}

首先先送一串q将画面重置到主功能表,接着是进入目前的看板,这段流程与searchDetailProcess差不多,接着就是把searchTitleSetsearchAuthorSet剩下的内容重新搜寻一遍就是。

Note

需要注意的是这边目前有一个状况,就是在ptt搜寻时,搜寻失败相同的搜寻结果回传的画面是一致的,但是相同的搜寻结果会扣掉一次搜寻次数。这部分我还没有处理的想法,後面再看看怎麽处理吧。

目前画面

https://imgur.com/axuraEU.gif


<<:  JavaScript Day 19. by value ( 传值 ) 与 by reference ( 传址 )

>>:  Day28 - 铁人付外挂部署与发行(一)- 建立测试机

[Day5] Holt's Model 介绍

第四篇我们介绍了时间序列经典的统计预测方法 ARIMA,包含公式内的两大模型 AR model、MA...

前端工程学习日记第8天

有些学生提到还要多一个 clear div 来清除会把 HTML 弄脏, 这里老师也分享一个是使用 ...

[Day15]ISO 27001 标准:内稽管审

是菜稽还是老稽,大部份可以看他们稽核的顺序来辨别, 有一阵子观察五年以上的资深前辈,会从内稽、管审开...

[Day13]空值转换函数

前几篇文章提及过,当资料表数值为空值时有三种情况,分别为: 目前不知道其值 未指派 没有值 但不管是...

如何在 WordPress 放上 Google AdSense 广告 - 为网站增加被动收入

当我们经营 WordPress 一段时间之後,许多朋友透过 Google 搜寻或是 FB 或其他社群...