Day08 - 寻找看板

今天来做搜寻看板的部分,首先Layout我先简单的放一个EditText以及Button,点击Button後将输入的内容送出做搜寻:

Layout

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".SearchArticleFragment">

    <EditText
        android:id="@+id/searchBoardInput"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/one_and_half_grid_unit"
        android:layout_marginTop="@dimen/one_and_half_grid_unit"
        android:layout_marginEnd="@dimen/half_grid_unit"
        android:background="@color/text_normal"
        android:padding="@dimen/one_grid_unit"
        android:textColor="@color/background"
        app:layout_constraintEnd_toStartOf="@id/searchBoard"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="Gossip" />

    <ImageView
        android:id="@+id/searchBoard"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/text_normal"
        android:padding="@dimen/half_grid_unit"
        android:layout_marginEnd="@dimen/one_and_half_grid_unit"
        android:src="@drawable/ic_baseline_location_searching"
        app:layout_constraintBottom_toBottomOf="@id/searchBoardInput"
        app:layout_constraintEnd_toEndOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

OnClick事件

searchBoard.setOnClickListener {
    val board = searchBoardInput.text.toString().trim()
    searchBoardInput.setText(board)
    if (board.isEmpty()) return@setOnClickListener
    PttClient.getInstance().send("s$board ")
    //...
}

搜寻的起始是小写的英文字母s,接着就是输入板名+空白键。以输入"a"为例,在Ptt按上述操作输入後的画面如下:
https://ithelp.ithome.com.tw/upload/images/20210922/20124602LARAvpZtP7.png

接着要做的事便是将回传画面下方的看板列表给解析出来。

Pattern - 所有搜寻结果页面

首先可以看到上图的最後一行显示"按空白键可列出更多项目",这代表以"a"搜寻还有更多的结果无法在一个画面内呈现。若持续点击空白键把结果走到最後的话,画面的呈现如下:
https://ithelp.ithome.com.tw/upload/images/20210922/20124602y4m7HnF60i.png
唯一的差别就只有最後一行消失了。

於是为了确保解析出所有搜寻结果页面,我先设定了以下Pattern:

private val searchBoardPattern = arrayOf(
    "按空白键可列出更多项目",
    "相关资讯一览表"
)

如果先匹配到"按空白键可列出更多项目",代表还有内容需要解析,反之代表最後一页。

viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
    val boardList = mutableListOf<String>()
    do {
        val ret = PttClient.getInstance().expect(searchBoardPattern)
        Log.d(mTag, "expect: ret=$ret")
        if (ret == 0 || ret == 1) {
            // ... -> 解析看板名称
        }
        if (ret == 0) {
            PttClient.getInstance().send(" ")
            delay(100L)
        }
    } while (ret == 0)
    // ... -> 回归主页面
}

Pattern - 所有看板

接下来要做的是上面程序码注解中的解析看板名称部分,解析出来的内容我会放到上方已宣告的boardList中。一样我们先看画面内容:
https://ithelp.ithome.com.tw/upload/images/20210922/20124602UQco1Rkw3H.png
基本上要做的事情就如上图的标记,拿到页面的字串後分割出第3~22行的内容,并且将每行内容以长度22做分割。

val searchResult = PttClient.getInstance().getScreen().split("\n")
for (i in 3..22) {
    val splitResult = arrayOf(
        searchResult[i].substring(0, 22).trim(),
        searchResult[i].substring(22, 44).trim(),
        searchResult[i].substring(44).trim()
    )
    splitResult.forEach {
        if (it.isNotEmpty()) {
            boardList.add(it)
        }
    }
}

trim完之後不为空的内容就加入到boardList之中,以前面的"a"来搜寻的话,下中断点能看到以下的结果:
https://ithelp.ithome.com.tw/upload/images/20210922/20124602g8FdDKCqVE.png

回归主页面

在搜寻完成後我预计要做的事是将结果呈现并让使用者做选择,在此之前我想要先确保我的Ptt页面是在主列表中,这样在後续的操作会比较方便。而要离开搜寻的话需要送出Enter,但有时候当我搜寻的内容本来就有能匹配到的看板的话,按下Enter可能会直接进入该看板中,如下:
https://ithelp.ithome.com.tw/upload/images/20210922/201246024MBE3pBEf9.png
此时点击Enter会直接进入该看板:
https://ithelp.ithome.com.tw/upload/images/20210922/201246027i2a7G6x3h.png
并且有些看板会有进版画面:
https://ithelp.ithome.com.tw/upload/images/20210922/20124602HFj9vRdDjP.png

因此在送出Enter後我会有以下Pattern需要判断:

private val cancelSearchBoardPattern = arrayOf(
    "【主功能表】",
    "请按任意键继续", // 进板画面
    "文章选读", // 看板内
)

最後就简单了。

PttClient.getInstance().send("\r\n")
var ret = PttClient.getInstance().expect(cancelSearchBoardPattern)
while (ret != 0 && ret != -1) {
    PttClient.getInstance().send("qq")
    delay(50L)
    ret = PttClient.getInstance().expect(cancelSearchBoardPattern)
}
// PttClient.getInstance().printScreen()

一次送两个q是为了能更快速的回到主列表。

补充

因为不想每次送出内容都还要另外加toByteArray(Charset)来转换成big5编码的byte array,所以我有复写了PttClient的send方法,之後直接送字串就好了。

override fun send(text: String?) {
    text?.run {
        send(toByteArray(BIG5))
    }
}

今天的内容就到这边了,明天是想把搜寻的结果呈现给User做点选,应该内容会比较少一点。


<<:  Day08 X 浏览器架构演进史 & 渲染机制

>>:  [Day18] - 在 React 中引用现成的 Web Component

30天程序语言研究

今天是30天程序语言研究的第二天,研究的语言一样是python,今天主要学习的部分是string 和...

JavaScript Document Object

Document Object 我们知道 DOM 是 Document Object Model(文...

爱用iPhone的设计师可以多恐怖?

(我想到的这个标题真的很误导人。因为「不是只有爱用iPhone的设计师会有这些毛病。」) 下面这是在...

DAY1 学习动机与选择原因

这是一个在疫情期间无法去学校上课且刚放暑假的我, 为了自己的目标和理想所以努力的学习, 在完成了前一...

Day 14 Detect objects in images with the Custom Vision service

Object detection - Recognize individual types of o...