搜索功能实现

pull/374/head
Jason Yao 4 years ago
parent ec9e554d0d
commit 56edd5a3a7
  1. 5
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchListActivity.kt
  2. 10
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchListAdapter.kt
  3. 73
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchListFragment.kt
  4. 1
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchListViewModel.kt
  5. 4
      app/src/main/res/layout/item_search_list.xml

@ -20,6 +20,7 @@ import kotlinx.android.synthetic.main.view_tab_layout.*
class SearchListActivity : VMBaseActivity<SearchListViewModel>(R.layout.activity_search_list) { class SearchListActivity : VMBaseActivity<SearchListViewModel>(R.layout.activity_search_list) {
// todo: 完善搜索界面UI
override val viewModel: SearchListViewModel override val viewModel: SearchListViewModel
get() = getViewModel(SearchListViewModel::class.java) get() = getViewModel(SearchListViewModel::class.java)
@ -51,7 +52,9 @@ class SearchListActivity : VMBaseActivity<SearchListViewModel>(R.layout.activity
searchView?.setOnSearchClickListener { tab_layout.gone() } searchView?.setOnSearchClickListener { tab_layout.gone() }
searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
viewModel.startContentSearch(query) if (viewModel.lastQuery != query){
viewModel.startContentSearch(query)
}
return false return false
} }

@ -3,6 +3,7 @@ package io.legado.app.ui.book.searchContent
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import android.text.Html import android.text.Html
import android.util.Log
import android.view.View import android.view.View
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
@ -22,15 +23,10 @@ class SearchListAdapter(context: Context, val callback: Callback) :
val cacheFileNames = hashSetOf<String>() val cacheFileNames = hashSetOf<String>()
@RequiresApi(Build.VERSION_CODES.N)
override fun convert(holder: ItemViewHolder, item: SearchResult, payloads: MutableList<Any>) { override fun convert(holder: ItemViewHolder, item: SearchResult, payloads: MutableList<Any>) {
with(holder.itemView) { with(holder.itemView) {
if (payloads.isEmpty()) { if (payloads.isEmpty()) {
// set search result color here
tv_search_result.text = HtmlCompat.fromHtml(item.presentText, HtmlCompat.FROM_HTML_MODE_LEGACY) tv_search_result.text = HtmlCompat.fromHtml(item.presentText, HtmlCompat.FROM_HTML_MODE_LEGACY)
} else {
//to do
} }
} }
} }
@ -43,10 +39,6 @@ class SearchListAdapter(context: Context, val callback: Callback) :
} }
} }
private fun upHasCache(itemView: View, isDur: Boolean, cached: Boolean) = itemView.apply {
tv_search_result.paint.isFakeBoldText = cached
}
interface Callback { interface Callback {
fun openSearchResult(searchResult: SearchResult) fun openSearchResult(searchResult: SearchResult)
} }

@ -81,16 +81,6 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
} }
} }
/*
private fun initDoc() {
tocLiveData?.removeObservers(this@SearchListFragment)
tocLiveData = App.db.bookChapterDao().observeByBook(viewModel.bookUrl)
tocLiveData?.observe(viewLifecycleOwner, {
adapter.setItems(it)
})
}
*/
private fun initCacheFileNames(book: Book) { private fun initCacheFileNames(book: Book) {
launch(IO) { launch(IO) {
adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book)) adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book))
@ -113,14 +103,21 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
override fun startContentSearch(newText: String?) { override fun startContentSearch(newText: String?) {
if (!newText.isNullOrBlank()) { if (!newText.isNullOrBlank()) {
adapter.clearItems()
viewModel.lastQuery = newText
App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map{ App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map{
launch(IO) { launch(IO) {
val beginTime = System.currentTimeMillis() val beginTime = System.currentTimeMillis()
if (isLocalBook || if (isLocalBook
adapter.cacheFileNames.contains(BookHelp.formatChapterName(it)) || adapter.cacheFileNames.contains(BookHelp.formatChapterName(it))
) { ) {
val value = searchChapter(newText, it) val searchResults = searchChapter(newText, it)
searchResultCounts += value if (searchResults.size > 0 ){
searchResultCounts += searchResults.size
withContext(Main){
adapter.addItems(searchResults)
}
}
} }
val finishedTime = System.currentTimeMillis() - beginTime val finishedTime = System.currentTimeMillis() - beginTime
Log.d("Jason", "Search finished, the total time cost is $finishedTime") Log.d("Jason", "Search finished, the total time cost is $finishedTime")
@ -131,15 +128,17 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
} }
private suspend fun searchChapter(query: String, chapter: BookChapter?): Int { private fun searchChapter(query: String, chapter: BookChapter?): MutableList<SearchResult> {
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
val searchResult: MutableList<SearchResult> = mutableListOf() val searchResults: MutableList<SearchResult> = mutableListOf()
var positions : List<Int>? = listOf() var positions : List<Int>? = listOf()
if (chapter != null){ if (chapter != null){
Log.d("Jason", "Search ${chapter.title}") Log.d("Jason", "Search ${chapter.title}")
viewModel.book?.let { bookSource -> viewModel.book?.let { bookSource ->
val bookContent = BookHelp.getContent(bookSource, chapter) val bookContent = BookHelp.getContent(bookSource, chapter)
if (bookContent != null){ if (bookContent != null){
//todo: 搜索替换后的正文
//todo: 计算搜索结果所在的pageIndex直接跳转
/* replace content, let's focus on original content first /* replace content, let's focus on original content first
chapter.title = when (AppConfig.chineseConverterType) { chapter.title = when (AppConfig.chineseConverterType) {
1 -> HanLP.convertToSimplifiedChinese(chapter.title) 1 -> HanLP.convertToSimplifiedChinese(chapter.title)
@ -168,30 +167,28 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
*/ */
positions = countMatches(bookContent, query) positions = countMatches(bookContent, query)
positions?.map{ positions?.map{
searchResult.add( val result = SearchResult(index = 0,
SearchResult(index = 0, text = constructText(bookContent, it),
text = constructText(bookContent, it), chapterTitle = chapter.title,
chapterTitle = chapter.title, query = query,
query = query, pageSize = 0, // to be finished
pageSize = 0, // to be finished chapterIndex = chapter.index, // to be finished
chapterIndex = chapter.index, // to be finished pageIndex = 0, // to be finished
pageIndex = 0, // to be finished
)
) )
searchResults.add(result)
Log.d("Jason", result.presentText)
} }
adapter.addItems(searchResult)
Log.d("Jason", "Search ${chapter.title} finished, the appeared count is ${positions!!.size}") Log.d("Jason", "Search ${chapter.title} finished, the appeared count is ${positions!!.size}")
} }
} }
val endTime = System.currentTimeMillis() - startTime val endTime = System.currentTimeMillis() - startTime
Log.d("Jason", "Search ${chapter.title} finished, the time cost is $endTime") Log.d("Jason", "Search ${chapter.title} finished, the time cost is $endTime")
} }
return positions!!.size return searchResults
} }
private fun countMatches(content: String, pattern: String): List<Int> { private fun countMatches(content: String, pattern: String): List<Int> {
val position : MutableList<Int> = mutableListOf() val position : MutableList<Int> = mutableListOf()
var count = 0
var index = content.indexOf(pattern) var index = content.indexOf(pattern)
while(index >= 0){ while(index >= 0){
position.add(index) position.add(index)
@ -201,8 +198,11 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
} }
private fun constructText(content: String, position: Int): String{ private fun constructText(content: String, position: Int): String{
// 构建关键词周边文字,在搜索结果里显示
val length = 10 // todo: 判断段落,只在关键词所在段落内分割
// todo: 利用标点符号分割完整的句
// todo: length和设置结合,自由调整周边文字长度
val length = 20
var po1 = position - length var po1 = position - length
var po2 = position + length var po2 = position + length
if (po1 <0) { if (po1 <0) {
@ -211,18 +211,7 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
if (po2 > content.length){ if (po2 > content.length){
po2 = content.length po2 = content.length
} }
return content.substring(po1, po2) return "..." + content.substring(po1, po2)
/*
if (position >= length && position <= content.length - length){
return content.substring(position - length, position + length)
}
else if (position <= length){
return content.substring(0, position + length)
}
else if (position >= content.length - length){
return content.substring(position - length, content.length)
}
*/
} }
val isLocalBook: Boolean val isLocalBook: Boolean

@ -10,6 +10,7 @@ class SearchListViewModel(application: Application) : BaseViewModel(application)
var bookUrl: String = "" var bookUrl: String = ""
var book: Book? = null var book: Book? = null
var searchCallBack: SearchListCallBack? = null var searchCallBack: SearchListCallBack? = null
var lastQuery: String = ""
fun initBook(bookUrl: String, success: () -> Unit) { fun initBook(bookUrl: String, success: () -> Unit) {
this.bookUrl = bookUrl this.bookUrl = bookUrl

@ -9,9 +9,9 @@
<TextView <TextView
android:id="@+id/tv_search_result" android:id="@+id/tv_search_result"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:singleLine="false"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="parent" /> app:layout_constraintRight_toLeftOf="parent" />

Loading…
Cancel
Save