主要是Ptt一頁的文章最多列出20篇,若要搜尋到20篇以前的文章就需要換頁,幸運的是文章列表這邊換頁的指令蠻簡單的。
可以看到在這邊可以簡單的用"P"和"N"換頁。
實作上首先在SearchArticleResultAdapter新增callback來通知更新
//...
var needMoreArticleCallback: (() -> Unit)? = null
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindView(articleList[position])
if (position == articleList.size - 1 && articleList[position].number != "1") {
needMoreArticleCallback?.invoke()
}
}
//...
如果目前列表的最後一項編號不是1的話代表還有舊的內容,此時呼叫通知更新的方法。
//...
articleAdapter.needMoreArticleCallback = {
if (PttClient.getInstance().expect(inBoardPattern) == 2) {
(requireActivity() as MainActivity).showLoading("")
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
val ret = withContext(Dispatchers.IO) {
PttClient.getInstance().send("P")
delay(200L)
PttClient.getInstance().expect(inBoardPattern)
}
if (ret == 2) {
parseBoardArticle(PttClient.getInstance().getScreen())
}
(requireActivity() as MainActivity).dismissLoading()
}
}
}
//...
首先先用inBoardPattern確認目前畫面在看板內再送後續指令,如前面所述送出P後確認畫面依舊在看板內就送入parseBoardArticle方法內了。
為了保留現有內容並在後面新增文章,對此方法作了一點修改。
private val articleList = mutableListOf<Article>()
private fun parseBoardArticle(result: String) {
val articlePattern =
Pattern.compile("([●]|[ ])*(?<no>[0-9]+)[ ].([0-9 X]+|爆)(?<date>../..)[ ](?<author>.*?)([\\s□轉]|R:)+(?<title>.*)")
val rows = result.split("\n")
val currentArticleList = mutableListOf<Article>()
for (row in rows) {
val matcher = articlePattern.matcher(row)
if (matcher.find()) {
val article = Article(
matcher.group("no")?.trim(),
matcher.group("date")?.trim(),
matcher.group("author")?.trim(),
matcher.group("title")?.trim()
)
if (!articleList.any { it.number == article.number }) {
currentArticleList.add(0, article)
}
Log.d(mTag, "article=$article")
}
}
articleList.addAll(currentArticleList)
articleAdapter.setData(articleList)
}
首先將articleList提出成全域變數,在解析目前頁面時另外宣告currentArticleList來儲存,最後再加入到articleList中。過程中則是用any來找出目前解析出的文章編號是否已經被加入過了,這是因為若剩餘未顯示的文章篇數不到20篇的話,在切換頁面後會用已顯示過的文章來補足20篇的篇數,因此有重複的可能。
articleList除了這邊使用到以外,再先前的搜尋操作前要注意先將內容清除掉,以避免重複。
private suspend fun searchDetailProcess(command: String): Boolean {
articleList.clear()
//...
}
private fun refreshSearch() {
articleList.clear()
//...
}
最後稍微看一下設置及更新資料的部分。
fun setData(newList: List<Article>) {
val result = DiffUtil.calculateDiff(ArticleDiffUtilCallbackImpl(articleList, newList))
articleList.clear()
articleList.addAll(newList)
result.dispatchUpdatesTo(this)
}
主要就是使用Android提供的DiffUtil來做更新,並實作DiffUtil.Callback。
class ArticleDiffUtilCallbackImpl(
private val oldList: List<Article>,
private val newList: List<Article>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].number == newList[newItemPosition].number
}
}