在前兩天分別做了以文章標題和文章作者來搜尋文章的部分,我打算使用Chip和ChipGroup來顯示搜尋的項目。
<!-- ... -->
<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,用來存放Chip。Chip則在程式碼內動態新增。
首先為了在確認有搜尋結果的狀態下再行紀錄,需要稍微改寫一下前兩天的搜尋方法。
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.
}
}
}
這部分的差異也只在填入的字串與目標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方法內重新整理目前狀態。
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差不多,接著就是把searchTitleSet和searchAuthorSet剩下的內容重新搜尋一遍就是。
需要注意的是這邊目前有一個狀況,就是在ptt搜尋時,搜尋失敗與相同的搜尋結果回傳的畫面是一致的,但是相同的搜尋結果會扣掉一次搜尋次數。這部分我還沒有處理的想法,後面再看看怎麼處理吧。