Merge pull request #1395 from h11128/search-arrow

搜索菜单
This commit is contained in:
kunfei 2021-10-22 10:53:54 +08:00 committed by GitHub
commit 20a516196a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 614 additions and 126 deletions

View File

@ -65,6 +65,7 @@ class ReadBookActivity : BaseReadBookActivity(),
TextActionMenu.CallBack,
ContentTextView.CallBack,
ReadMenu.CallBack,
SearchMenu.CallBack,
ReadAloudDialog.CallBack,
ChangeSourceDialog.CallBack,
ReadBook.CallBack,
@ -117,6 +118,7 @@ class ReadBookActivity : BaseReadBookActivity(),
private var backupJob: Job? = null
override var autoPageProgress = 0
override var isAutoPage = false
override var isShowingSearchResult = true
private var screenTimeOut: Long = 0
private var timeBatteryReceiver: TimeBatteryReceiver? = null
private var loadStates: Boolean = false
@ -635,6 +637,7 @@ class ReadBookActivity : BaseReadBookActivity(),
when {
BaseReadAloudService.isRun -> showReadAloudDialog()
isAutoPage -> showDialogFragment<AutoReadDialog>()
isShowingSearchResult -> binding.searchMenu.runMenuIn()
else -> binding.readMenu.runMenuIn()
}
}
@ -766,6 +769,14 @@ class ReadBookActivity : BaseReadBookActivity(),
showDialogFragment<MoreConfigDialog>()
}
override fun returnSearchActivity() {
TODO("Not yet implemented")
}
override fun showSearchSetting() {
TODO("Not yet implemented")
}
/**
* 更新状态栏,导航栏
*/
@ -774,6 +785,13 @@ class ReadBookActivity : BaseReadBookActivity(),
upNavigationBarColor()
}
override fun searchExit() {
if(isShowingSearchResult){
isShowingSearchResult = false
binding.searchMenu.invalidate()
}
}
override fun showLogin() {
ReadBook.bookSource?.let {
startActivity<SourceLoginActivity> {

View File

@ -0,0 +1,204 @@
package io.legado.app.ui.book.read
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.drawable.GradientDrawable
import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View.OnClickListener
import android.view.View.OnLongClickListener
import android.view.WindowManager
import android.view.animation.Animation
import android.widget.FrameLayout
import android.widget.SeekBar
import androidx.activity.viewModels
import androidx.appcompat.widget.SearchView
import androidx.core.view.isVisible
import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.databinding.ViewSearchMenuBinding
import io.legado.app.help.*
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.*
import io.legado.app.model.ReadBook
import io.legado.app.ui.book.info.BookInfoActivity
import io.legado.app.ui.book.searchContent.SearchContentViewModel
import io.legado.app.ui.browser.WebViewActivity
import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
import io.legado.app.utils.*
import splitties.views.*
/**
* 阅读界面菜单
*/
class SearchMenu @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {
private val searchView: SearchView by lazy {
binding.titleBar.findViewById(R.id.search_view)
}
val viewModel by viewModels<SearchContentViewModel>()
private val callBack: CallBack get() = activity as CallBack
private val binding = ViewSearchMenuBinding.inflate(LayoutInflater.from(context), this, true)
private val menuTopIn: Animation = AnimationUtilsSupport.loadAnimation(context, R.anim.anim_readbook_top_in)
private val menuTopOut: Animation = AnimationUtilsSupport.loadAnimation(context, R.anim.anim_readbook_top_out)
private val menuBottomIn: Animation = AnimationUtilsSupport.loadAnimation(context, R.anim.anim_readbook_bottom_in)
private val menuBottomOut: Animation = AnimationUtilsSupport.loadAnimation(context, R.anim.anim_readbook_bottom_out)
private val bgColor: Int = context.bottomBackground
private val textColor: Int = context.getPrimaryTextColor(ColorUtils.isColorLight(bgColor))
private val bottomBackgroundList: ColorStateList =
Selector.colorBuild().setDefaultColor(bgColor).setPressedColor(ColorUtils.darkenColor(bgColor)).create()
private var onMenuOutEnd: (() -> Unit)? = null
private var hasSearchResult: Boolean = true
init {
initAnimation()
initView()
bindEvent()
}
private fun initView() = binding.run {
llSearchBaseInfo.setBackgroundColor(bgColor)
tvCurrentSearchInfo.setTextColor(bottomBackgroundList)
llBottomBg.setBackgroundColor(bgColor)
fabLeft.backgroundTintList = bottomBackgroundList
fabLeft.setColorFilter(textColor)
fabRight.backgroundTintList = bottomBackgroundList
fabRight.setColorFilter(textColor)
tvMainMenu.setTextColor(textColor)
tvSearchResults.setTextColor(textColor)
tvSearchExit.setTextColor(textColor)
tvSetting.setTextColor(textColor)
ivMainMenu.setColorFilter(textColor)
ivSearchResults.setColorFilter(textColor)
ivSearchExit.setColorFilter(textColor)
ivSetting.setColorFilter(textColor)
ivSearchContentBottom.setColorFilter(textColor)
ivSearchContentTop.setColorFilter(textColor)
}
fun runMenuIn() {
this.visible()
binding.titleBar.visible()
binding.llSearchBaseInfo.visible()
binding.llBottomBg.visible()
binding.titleBar.startAnimation(menuTopIn)
binding.llSearchBaseInfo.startAnimation(menuBottomIn)
binding.llBottomBg.startAnimation(menuBottomIn)
}
fun runMenuOut(onMenuOutEnd: (() -> Unit)? = null) {
this.onMenuOutEnd = onMenuOutEnd
if (this.isVisible) {
binding.titleBar.startAnimation(menuTopOut)
binding.llSearchBaseInfo.startAnimation(menuBottomOut)
binding.llBottomBg.startAnimation(menuBottomOut)
}
}
private fun bindEvent() = binding.run {
titleBar.toolbar.setOnClickListener {
ReadBook.book?.let {
context.startActivity<BookInfoActivity> {
putExtra("name", it.name)
putExtra("author", it.author)
}
}
}
llSearchResults.setOnClickListener {
runMenuOut {
callBack.returnSearchActivity()
}
}
//主菜单
llMainMenu.setOnClickListener {
runMenuOut {
callBack.showMenuBar()
}
}
//目录
llSearchExit.setOnClickListener {
runMenuOut {
callBack.searchExit()
}
}
//设置
llSetting.setOnClickListener {
runMenuOut {
callBack.showSearchSetting()
}
}
}
private fun initAnimation() {
//显示菜单
menuTopIn.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {
callBack.upSystemUiVisibility()
binding.fabLeft.visible(hasSearchResult)
binding.fabRight.visible(hasSearchResult)
}
@SuppressLint("RtlHardcoded")
override fun onAnimationEnd(animation: Animation) {
val navigationBarHeight = if (ReadBookConfig.hideNavigationBar) {
activity?.navigationBarHeight ?: 0
} else {
0
}
binding.run {
vwMenuBg.setOnClickListener { runMenuOut() }
root.padding = 0
when (activity?.navigationBarGravity) {
Gravity.BOTTOM -> root.bottomPadding = navigationBarHeight
Gravity.LEFT -> root.leftPadding = navigationBarHeight
Gravity.RIGHT -> root.rightPadding = navigationBarHeight
}
}
callBack.upSystemUiVisibility()
}
override fun onAnimationRepeat(animation: Animation) = Unit
})
//隐藏菜单
menuTopOut.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {
binding.vwMenuBg.setOnClickListener(null)
}
override fun onAnimationEnd(animation: Animation) {
this@SearchMenu.invisible()
binding.titleBar.invisible()
binding.llSearchBaseInfo.invisible()
binding.llBottomBg.invisible()
binding.fabRight.invisible()
binding.fabLeft.invisible()
onMenuOutEnd?.invoke()
callBack.upSystemUiVisibility()
}
override fun onAnimationRepeat(animation: Animation) = Unit
})
}
interface CallBack {
var isShowingSearchResult: Boolean
fun returnSearchActivity()
fun showSearchSetting()
fun upSystemUiVisibility()
fun searchExit()
fun showMenuBar()
}
}

View File

@ -5,7 +5,6 @@ import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.widget.SearchView
import com.github.liuyueyi.quick.transfer.ChineseUtils
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.EventBus
@ -13,7 +12,6 @@ import io.legado.app.data.appDb
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.databinding.ActivitySearchContentBinding
import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.getPrimaryTextColor
@ -40,9 +38,7 @@ class SearchContentActivity :
private val searchView: SearchView by lazy {
binding.titleBar.findViewById(R.id.search_view)
}
private var searchResultCounts = 0
private var durChapterIndex = 0
private var searchResultList: MutableList<SearchResult> = mutableListOf()
override fun onActivityCreated(savedInstanceState: Bundle?) {
val bbg = bottomBackground
@ -103,7 +99,7 @@ class SearchContentActivity :
@SuppressLint("SetTextI18n")
private fun initBook() {
binding.tvCurrentSearchInfo.text = "搜索结果:$searchResultCounts"
binding.tvCurrentSearchInfo.text = "搜索结果:${viewModel.searchResultCounts}"
viewModel.book?.let {
initCacheFileNames(it)
durChapterIndex = it.durChapterIndex
@ -115,7 +111,7 @@ class SearchContentActivity :
private fun initCacheFileNames(book: Book) {
launch(Dispatchers.IO) {
adapter.cacheFileNames.addAll(BookHelp.getChapterFiles(book))
viewModel.cacheChapterNames.addAll(BookHelp.getChapterFiles(book))
withContext(Dispatchers.Main) {
adapter.notifyItemRangeChanged(0, adapter.itemCount, true)
}
@ -126,7 +122,7 @@ class SearchContentActivity :
observeEvent<BookChapter>(EventBus.SAVE_CONTENT) { chapter ->
viewModel.book?.bookUrl?.let { bookUrl ->
if (chapter.bookUrl == bookUrl) {
adapter.cacheFileNames.add(chapter.getFileName())
viewModel.cacheChapterNames.add(chapter.getFileName())
adapter.notifyItemChanged(chapter.index, true)
}
}
@ -134,28 +130,26 @@ class SearchContentActivity :
}
@SuppressLint("SetTextI18n")
fun startContentSearch(newText: String) {
fun startContentSearch(query: String) {
// 按章节搜索内容
if (newText.isNotBlank()) {
if (query.isNotBlank()) {
adapter.clearItems()
searchResultList.clear()
binding.refreshProgressBar.isAutoLoading = true
searchResultCounts = 0
viewModel.lastQuery = newText
viewModel.searchResultList.clear()
viewModel.searchResultCounts = 0
viewModel.lastQuery = query
var searchResults = listOf<SearchResult>()
launch(Dispatchers.Main) {
appDb.bookChapterDao.getChapterList(viewModel.bookUrl).map { chapter ->
appDb.bookChapterDao.getChapterList(viewModel.bookUrl).map { bookChapter ->
binding.refreshProgressBar.isAutoLoading = true
withContext(Dispatchers.IO) {
if (isLocalBook
|| adapter.cacheFileNames.contains(chapter.getFileName())
) {
searchResults = searchChapter(newText, chapter)
if (isLocalBook || viewModel.cacheChapterNames.contains(bookChapter.getFileName())) {
searchResults = viewModel.searchChapter(query, bookChapter)
}
}
if (searchResults.isNotEmpty()) {
searchResultList.addAll(searchResults)
viewModel.searchResultList.addAll(searchResults)
binding.refreshProgressBar.isAutoLoading = false
binding.tvCurrentSearchInfo.text = "搜索结果:$searchResultCounts"
binding.tvCurrentSearchInfo.text = "搜索结果:${viewModel.searchResultCounts}"
adapter.addItems(searchResults)
searchResults = listOf()
}
@ -164,85 +158,6 @@ class SearchContentActivity :
}
}
private suspend fun searchChapter(query: String, chapter: BookChapter?): List<SearchResult> {
val searchResults: MutableList<SearchResult> = mutableListOf()
var positions: List<Int>
var replaceContents: List<String>?
var totalContents: String
if (chapter != null) {
viewModel.book?.let { book ->
val bookContent = BookHelp.getContent(book, chapter)
if (bookContent != null) {
//搜索替换后的正文
withContext(Dispatchers.IO) {
chapter.title = when (AppConfig.chineseConverterType) {
1 -> ChineseUtils.t2s(chapter.title)
2 -> ChineseUtils.s2t(chapter.title)
else -> chapter.title
}
replaceContents =
viewModel.contentProcessor!!.getContent(
book,
chapter,
bookContent,
chineseConvert = false,
reSegment = false
)
}
totalContents = replaceContents?.joinToString("") ?: bookContent
positions = searchPosition(totalContents, query)
var count = 1
positions.map {
val construct = constructText(totalContents, it, query)
val result = SearchResult(
index = searchResultCounts,
indexWithinChapter = count,
text = construct[1] as String,
chapterTitle = chapter.title,
query = query,
chapterIndex = chapter.index,
newPosition = construct[0] as Int,
contentPosition = it
)
count += 1
searchResultCounts += 1
searchResults.add(result)
}
}
}
}
return searchResults
}
private fun searchPosition(content: String, pattern: String): List<Int> {
val position: MutableList<Int> = mutableListOf()
var index = content.indexOf(pattern)
while (index >= 0) {
position.add(index)
index = content.indexOf(pattern, index + 1)
}
return position
}
private fun constructText(content: String, position: Int, query: String): Array<Any> {
// 构建关键词周边文字,在搜索结果里显示
// todo: 判断段落,只在关键词所在段落内分割
// todo: 利用标点符号分割完整的句
// todo: length和设置结合自由调整周边文字长度
val length = 20
var po1 = position - length
var po2 = position + query.length + length
if (po1 < 0) {
po1 = 0
}
if (po2 > content.length) {
po2 = content.length
}
val newPosition = position - po1
val newText = content.substring(po1, po2)
return arrayOf(newPosition, newText)
}
val isLocalBook: Boolean
get() = viewModel.book?.isLocalBook() == true
@ -251,7 +166,7 @@ class SearchContentActivity :
searchData.putExtra("index", searchResult.chapterIndex)
searchData.putExtra("contentPosition", searchResult.contentPosition)
searchData.putExtra("query", searchResult.query)
searchData.putExtra("indexWithinChapter", searchResult.indexWithinChapter)
searchData.putExtra("indexWithinChapter", searchResult.resultCountWithinChapter)
setResult(RESULT_OK, searchData)
finish()
}

View File

@ -14,7 +14,6 @@ import io.legado.app.utils.hexString
class SearchContentAdapter(context: Context, val callback: Callback) :
RecyclerAdapter<SearchResult, ItemSearchListBinding>(context) {
val cacheFileNames = hashSetOf<String>()
val textColor = context.getCompatColor(R.color.primaryText).hexString.substring(2)
val accentColor = context.accentColor.hexString.substring(2)

View File

@ -2,16 +2,25 @@ package io.legado.app.ui.book.searchContent
import android.app.Application
import com.github.liuyueyi.quick.transfer.ChineseUtils
import io.legado.app.base.BaseViewModel
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.help.ContentProcessor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class SearchContentViewModel(application: Application) : BaseViewModel(application) {
var bookUrl: String = ""
var book: Book? = null
var contentProcessor: ContentProcessor? = null
private var contentProcessor: ContentProcessor? = null
var lastQuery: String = ""
var searchResultCounts = 0
val cacheChapterNames = hashSetOf<String>()
val searchResultList: MutableList<SearchResult> = mutableListOf()
fun initBook(bookUrl: String, success: () -> Unit) {
this.bookUrl = bookUrl
@ -25,4 +34,72 @@ class SearchContentViewModel(application: Application) : BaseViewModel(applicati
}
}
suspend fun searchChapter(query: String, chapter: BookChapter?): List<SearchResult> {
val searchResultsWithinChapter: MutableList<SearchResult> = mutableListOf()
if (chapter != null) {
book?.let { book ->
val chapterContent = BookHelp.getContent(book, chapter)
if (chapterContent != null) {
//搜索替换后的正文
val replaceContent: String
withContext(Dispatchers.IO) {
chapter.title = when (AppConfig.chineseConverterType) {
1 -> ChineseUtils.t2s(chapter.title)
2 -> ChineseUtils.s2t(chapter.title)
else -> chapter.title
}
replaceContent = contentProcessor!!.getContent(
book, chapter, chapterContent, chineseConvert = false, reSegment = false
).joinToString("")
}
val positions = searchPosition(replaceContent, query)
positions.forEachIndexed { index, position ->
val construct = getResultAndQueryIndex(replaceContent, position, query)
val result = SearchResult(
resultCountWithinChapter = index,
resultText = construct.second,
chapterTitle = chapter.title,
query = query,
chapterIndex = chapter.index,
queryIndexInResult = construct.first,
contentPosition = position
)
searchResultsWithinChapter.add(result)
}
searchResultCounts += searchResultsWithinChapter.size
}
}
}
return searchResultsWithinChapter
}
private fun searchPosition(content: String, pattern: String): List<Int> {
val position: MutableList<Int> = mutableListOf()
var index = content.indexOf(pattern)
while (index >= 0) {
position.add(index)
index = content.indexOf(pattern, index + 1)
}
return position
}
private fun getResultAndQueryIndex(content: String, queryIndexInContent: Int, query: String): Pair<Int, String> {
// 左右移动20个字符构建关键词周边文字在搜索结果里显示
// todo: 判断段落,只在关键词所在段落内分割
// todo: 利用标点符号分割完整的句
// todo: length和设置结合自由调整周边文字长度
val length = 20
var po1 = queryIndexInContent - length
var po2 = queryIndexInContent + query.length + length
if (po1 < 0) {
po1 = 0
}
if (po2 > content.length) {
po2 = content.length
}
val queryIndexInResult = queryIndexInContent - po1
val newText = content.substring(po1, po2)
return queryIndexInResult to newText
}
}

View File

@ -4,36 +4,29 @@ import android.text.Spanned
import androidx.core.text.HtmlCompat
data class SearchResult(
var index: Int = 0,
var indexWithinChapter: Int = 0,
var text: String = "",
var chapterTitle: String = "",
val resultCount: Int = 0,
val resultCountWithinChapter: Int = 0,
val resultText: String = "",
val chapterTitle: String = "",
val query: String,
var pageSize: Int = 0,
var chapterIndex: Int = 0,
var pageIndex: Int = 0,
var newPosition: Int = 0,
var contentPosition: Int = 0
val pageSize: Int = 0,
val chapterIndex: Int = 0,
val pageIndex: Int = 0,
val queryIndexInResult: Int = 0,
val contentPosition: Int = 0
) {
fun getHtmlCompat(textColor: String, accentColor: String): Spanned {
val html = colorPresentText(newPosition, query, text, textColor, accentColor) +
"<font color=#${accentColor}>($chapterTitle)</font>"
val queryIndexInSurrounding = resultText.indexOf(query)
val leftString = resultText.substring(0, queryIndexInSurrounding)
val rightString = resultText.substring(queryIndexInSurrounding + query.length, resultText.length)
val html = leftString.colorTextForHtml(textColor) +
query.colorTextForHtml(accentColor) +
rightString.colorTextForHtml(textColor) +
chapterTitle.colorTextForHtml(accentColor)
return HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
}
private fun colorPresentText(
position: Int,
center: String,
targetText: String,
textColor: String,
accentColor: String
): String {
val sub1 = text.substring(0, position)
val sub2 = text.substring(position + center.length, targetText.length)
return "<font color=#${textColor}>$sub1</font>" +
"<font color=#${accentColor}>$center</font>" +
"<font color=#${textColor}>$sub2</font>"
}
private fun String.colorTextForHtml(textColor: String) = "<font color=#${textColor}>$this</font>"
}

View File

@ -37,6 +37,12 @@
android:layout_height="match_parent"
android:visibility="gone" />
<io.legado.app.ui.book.read.SearchMenu
android:id="@+id/search_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<View
android:id="@+id/navigation_bar"
android:layout_width="match_parent"

View File

@ -0,0 +1,276 @@
<?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"
android:orientation="vertical">
<View
android:id="@+id/vw_menu_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/content"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabLeft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="上个结果"
android:src="@drawable/ic_search"
android:tint="@color/primaryText"
android:tooltipText="@string/search_content"
app:backgroundTint="@color/background_menu"
app:elevation="2dp"
app:fabSize="mini"
app:pressedTranslationZ="2dp"
tools:ignore="UnusedAttribute"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabRight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="下个结果"
android:src="@drawable/ic_search"
android:tint="@color/primaryText"
android:tooltipText="@string/search_content"
app:backgroundTint="@color/background_menu"
app:elevation="2dp"
app:fabSize="mini"
app:pressedTranslationZ="2dp"
tools:ignore="UnusedAttribute"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<io.legado.app.ui.widget.TitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentLayout="@layout/view_search"
app:contentInsetRight="24dp"
app:layout_constraintTop_toTopOf="parent" />
<io.legado.app.ui.widget.anima.RefreshProgressBar
android:id="@+id/refresh_progress_bar"
android:layout_width="match_parent"
android:layout_height="2dp"
app:layout_constraintTop_toBottomOf="@id/title_bar"/>
<LinearLayout
android:id="@+id/ll_search_base_info"
android:layout_width="match_parent"
android:layout_height="36dp"
android:background="@color/background_menu"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingRight="10dp"
app:layout_constraintBottom_toTopOf="@id/ll_bottom_bg">
<TextView
android:id="@+id/tv_current_search_info"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:ellipsize="middle"
android:gravity="center_vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:singleLine="true"
android:textColor="@color/primaryText"
android:textSize="12sp" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_search_content_top"
android:layout_width="36dp"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/go_to_top"
android:src="@drawable/ic_arrow_drop_up"
android:tooltipText="@string/go_to_top"
app:tint="@color/primaryText"
tools:ignore="UnusedAttribute" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_search_content_bottom"
android:layout_width="36dp"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/go_to_bottom"
android:src="@drawable/ic_arrow_drop_down"
android:tooltipText="@string/go_to_bottom"
app:tint="@color/primaryText"
tools:ignore="UnusedAttribute" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_bottom_bg"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:baselineAligned="false"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent">
<!--结果按钮-->
<LinearLayout
android:id="@+id/ll_search_results"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="结果"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="7dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_search_results"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:contentDescription="结果"
android:src="@drawable/ic_toc"
app:tint="@color/primaryText"
tools:ignore="NestedWeights" />
<TextView
android:id="@+id/tv_search_results"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:text="结果"
android:maxLines="1"
android:textColor="@color/primaryText"
android:textSize="12sp" />
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
<!--调节按钮-->
<LinearLayout
android:id="@+id/ll_main_menu"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/read_aloud"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="7dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_main_menu"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:contentDescription="@string/main_menu"
android:src="@drawable/ic_menu"
app:tint="@color/primaryText"
tools:ignore="NestedWeights" />
<TextView
android:id="@+id/tv_main_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:text="@string/main_menu"
android:maxLines="1"
android:textColor="@color/primaryText"
android:textSize="12sp" />
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
<!--界面按钮-->
<LinearLayout
android:id="@+id/ll_search_exit"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="退出"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="7dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_search_exit"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:contentDescription="退出"
android:src="@drawable/ic_auto_page_stop"
app:tint="@color/primaryText"
tools:ignore="NestedWeights" />
<TextView
android:id="@+id/tv_search_exit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:text="退出"
android:maxLines="1"
android:textColor="@color/primaryText"
android:textSize="12sp" />
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
<!--设置按钮-->
<LinearLayout
android:id="@+id/ll_setting"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/setting"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="7dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_setting"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:contentDescription="@string/aloud_config"
android:src="@drawable/ic_settings"
app:tint="@color/primaryText"
tools:ignore="NestedWeights" />
<TextView
android:id="@+id/tv_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:text="@string/setting"
android:maxLines="1"
android:textColor="@color/primaryText"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>