搜索功能实现

pull/374/head
Jason Yao 4 years ago
parent ec9e554d0d
commit 56edd5a3a7
  1. 3
      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. 61
      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) {
// todo: 完善搜索界面UI
override val viewModel: SearchListViewModel
get() = getViewModel(SearchListViewModel::class.java)
@ -51,7 +52,9 @@ class SearchListActivity : VMBaseActivity<SearchListViewModel>(R.layout.activity
searchView?.setOnSearchClickListener { tab_layout.gone() }
searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
if (viewModel.lastQuery != query){
viewModel.startContentSearch(query)
}
return false
}

@ -3,6 +3,7 @@ package io.legado.app.ui.book.searchContent
import android.content.Context
import android.os.Build
import android.text.Html
import android.util.Log
import android.view.View
import androidx.annotation.RequiresApi
import androidx.core.text.HtmlCompat
@ -22,15 +23,10 @@ class SearchListAdapter(context: Context, val callback: Callback) :
val cacheFileNames = hashSetOf<String>()
@RequiresApi(Build.VERSION_CODES.N)
override fun convert(holder: ItemViewHolder, item: SearchResult, payloads: MutableList<Any>) {
with(holder.itemView) {
if (payloads.isEmpty()) {
// set search result color here
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 {
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) {
launch(IO) {
adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book))
@ -113,14 +103,21 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
override fun startContentSearch(newText: String?) {
if (!newText.isNullOrBlank()) {
adapter.clearItems()
viewModel.lastQuery = newText
App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map{
launch(IO) {
val beginTime = System.currentTimeMillis()
if (isLocalBook ||
adapter.cacheFileNames.contains(BookHelp.formatChapterName(it))
if (isLocalBook
|| adapter.cacheFileNames.contains(BookHelp.formatChapterName(it))
) {
val value = searchChapter(newText, it)
searchResultCounts += value
val searchResults = searchChapter(newText, it)
if (searchResults.size > 0 ){
searchResultCounts += searchResults.size
withContext(Main){
adapter.addItems(searchResults)
}
}
}
val finishedTime = System.currentTimeMillis() - beginTime
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 searchResult: MutableList<SearchResult> = mutableListOf()
val searchResults: MutableList<SearchResult> = mutableListOf()
var positions : List<Int>? = listOf()
if (chapter != null){
Log.d("Jason", "Search ${chapter.title}")
viewModel.book?.let { bookSource ->
val bookContent = BookHelp.getContent(bookSource, chapter)
if (bookContent != null){
//todo: 搜索替换后的正文
//todo: 计算搜索结果所在的pageIndex直接跳转
/* replace content, let's focus on original content first
chapter.title = when (AppConfig.chineseConverterType) {
1 -> HanLP.convertToSimplifiedChinese(chapter.title)
@ -168,8 +167,7 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
*/
positions = countMatches(bookContent, query)
positions?.map{
searchResult.add(
SearchResult(index = 0,
val result = SearchResult(index = 0,
text = constructText(bookContent, it),
chapterTitle = chapter.title,
query = query,
@ -177,21 +175,20 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
chapterIndex = chapter.index, // 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}")
}
}
val endTime = System.currentTimeMillis() - startTime
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> {
val position : MutableList<Int> = mutableListOf()
var count = 0
var index = content.indexOf(pattern)
while(index >= 0){
position.add(index)
@ -201,8 +198,11 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
}
private fun constructText(content: String, position: Int): String{
val length = 10
// 构建关键词周边文字,在搜索结果里显示
// todo: 判断段落,只在关键词所在段落内分割
// todo: 利用标点符号分割完整的句
// todo: length和设置结合,自由调整周边文字长度
val length = 20
var po1 = position - length
var po2 = position + length
if (po1 <0) {
@ -211,18 +211,7 @@ class SearchListFragment : VMBaseFragment<SearchListViewModel>(R.layout.fragment
if (po2 > content.length){
po2 = content.length
}
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)
}
*/
return "..." + content.substring(po1, po2)
}
val isLocalBook: Boolean

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

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

Loading…
Cancel
Save