commit
87265c7818
@ -0,0 +1,235 @@ |
||||
package io.legado.app.ui.book.read |
||||
|
||||
import android.annotation.SuppressLint |
||||
import android.content.Context |
||||
import android.content.res.ColorStateList |
||||
import android.util.AttributeSet |
||||
import android.view.Gravity |
||||
import android.view.LayoutInflater |
||||
import android.view.animation.Animation |
||||
import android.widget.FrameLayout |
||||
import androidx.core.view.isVisible |
||||
import io.legado.app.R |
||||
import io.legado.app.constant.EventBus |
||||
import io.legado.app.databinding.ViewSearchMenuBinding |
||||
import io.legado.app.help.* |
||||
import io.legado.app.lib.theme.* |
||||
import io.legado.app.model.ReadBook |
||||
import io.legado.app.ui.book.searchContent.SearchResult |
||||
import io.legado.app.utils.* |
||||
import splitties.views.* |
||||
|
||||
/** |
||||
* 搜索界面菜单 |
||||
*/ |
||||
class SearchMenu @JvmOverloads constructor( |
||||
context: Context, attrs: AttributeSet? = null |
||||
) : FrameLayout(context, attrs) { |
||||
|
||||
private val callBack: CallBack get() = activity as CallBack |
||||
private val binding = ViewSearchMenuBinding.inflate(LayoutInflater.from(context), this, true) |
||||
|
||||
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 val searchResultList: MutableList<SearchResult> = mutableListOf() |
||||
private var currentSearchResultIndex: Int = 0 |
||||
private var lastSearchResultIndex: Int = 0 |
||||
private val hasSearchResult: Boolean |
||||
get() = searchResultList.isNotEmpty() |
||||
val selectedSearchResult: SearchResult? |
||||
get() = if (searchResultList.isNotEmpty()) searchResultList[currentSearchResultIndex] else null |
||||
val previousSearchResult: SearchResult |
||||
get() = searchResultList[lastSearchResultIndex] |
||||
|
||||
init { |
||||
initAnimation() |
||||
initView() |
||||
bindEvent() |
||||
updateSearchInfo() |
||||
observeSearchResultList() |
||||
} |
||||
|
||||
private fun observeSearchResultList() { |
||||
activity?.let { owner -> |
||||
eventObservable<List<SearchResult>>(EventBus.SEARCH_RESULT).observe(owner, { |
||||
searchResultList.clear() |
||||
searchResultList.addAll(it) |
||||
updateSearchInfo() |
||||
}) |
||||
} |
||||
} |
||||
|
||||
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) |
||||
ivSearchContentUp.setColorFilter(textColor) |
||||
ivSearchContentDown.setColorFilter(textColor) |
||||
tvCurrentSearchInfo.setTextColor(textColor) |
||||
} |
||||
|
||||
|
||||
fun runMenuIn() { |
||||
this.visible() |
||||
binding.llSearchBaseInfo.visible() |
||||
binding.llBottomBg.visible() |
||||
binding.vwMenuBg.visible() |
||||
binding.llSearchBaseInfo.startAnimation(menuBottomIn) |
||||
binding.llBottomBg.startAnimation(menuBottomIn) |
||||
} |
||||
|
||||
fun runMenuOut(onMenuOutEnd: (() -> Unit)? = null) { |
||||
this.onMenuOutEnd = onMenuOutEnd |
||||
if (this.isVisible) { |
||||
binding.llSearchBaseInfo.startAnimation(menuBottomOut) |
||||
binding.llBottomBg.startAnimation(menuBottomOut) |
||||
} |
||||
} |
||||
|
||||
fun updateSearchInfo() { |
||||
ReadBook.curTextChapter?.let { |
||||
binding.tvCurrentSearchInfo.text = context.getString(R.string.search_content_size) + ": ${searchResultList.size} / 当前章节: ${it.title}" |
||||
} |
||||
} |
||||
|
||||
fun updateSearchResultIndex(updateIndex: Int) { |
||||
lastSearchResultIndex = currentSearchResultIndex |
||||
currentSearchResultIndex = when { |
||||
updateIndex < 0 -> 0 |
||||
updateIndex >= searchResultList.size -> searchResultList.size - 1 |
||||
else -> updateIndex |
||||
} |
||||
} |
||||
|
||||
private fun bindEvent() = binding.run { |
||||
|
||||
llSearchResults.setOnClickListener { |
||||
runMenuOut { |
||||
callBack.openSearchActivity(selectedSearchResult?.query) |
||||
} |
||||
} |
||||
|
||||
//主菜单 |
||||
llMainMenu.setOnClickListener { |
||||
runMenuOut { |
||||
callBack.showMenuBar() |
||||
this@SearchMenu.invisible() |
||||
} |
||||
} |
||||
|
||||
//目录 |
||||
llSearchExit.setOnClickListener { |
||||
runMenuOut { |
||||
callBack.exitSearchMenu() |
||||
this@SearchMenu.invisible() |
||||
} |
||||
} |
||||
|
||||
//设置 |
||||
// llSetting.setOnClickListener { |
||||
// runMenuOut { |
||||
// callBack.showSearchSetting() |
||||
// } |
||||
// } |
||||
|
||||
fabLeft.setOnClickListener { |
||||
updateSearchResultIndex(currentSearchResultIndex - 1) |
||||
callBack.navigateToSearch(searchResultList[currentSearchResultIndex]) |
||||
} |
||||
|
||||
ivSearchContentUp.setOnClickListener { |
||||
updateSearchResultIndex(currentSearchResultIndex - 1) |
||||
callBack.navigateToSearch(searchResultList[currentSearchResultIndex]) |
||||
} |
||||
|
||||
ivSearchContentDown.setOnClickListener { |
||||
updateSearchResultIndex(currentSearchResultIndex + 1) |
||||
callBack.navigateToSearch(searchResultList[currentSearchResultIndex]) |
||||
} |
||||
|
||||
fabRight.setOnClickListener { |
||||
updateSearchResultIndex(currentSearchResultIndex + 1) |
||||
callBack.navigateToSearch(searchResultList[currentSearchResultIndex]) |
||||
} |
||||
} |
||||
|
||||
private fun initAnimation() { |
||||
//显示菜单 |
||||
menuBottomIn.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 |
||||
}) |
||||
|
||||
//隐藏菜单 |
||||
menuBottomOut.setAnimationListener(object : Animation.AnimationListener { |
||||
override fun onAnimationStart(animation: Animation) { |
||||
binding.vwMenuBg.setOnClickListener(null) |
||||
} |
||||
|
||||
override fun onAnimationEnd(animation: Animation) { |
||||
binding.llSearchBaseInfo.invisible() |
||||
binding.llBottomBg.invisible() |
||||
binding.vwMenuBg.invisible() |
||||
binding.vwMenuBg.setOnClickListener { runMenuOut() } |
||||
|
||||
onMenuOutEnd?.invoke() |
||||
callBack.upSystemUiVisibility() |
||||
} |
||||
|
||||
override fun onAnimationRepeat(animation: Animation) = Unit |
||||
}) |
||||
} |
||||
|
||||
interface CallBack { |
||||
var isShowingSearchResult: Boolean |
||||
fun openSearchActivity(searchWord: String?) |
||||
fun showSearchSetting() |
||||
fun upSystemUiVisibility() |
||||
fun exitSearchMenu() |
||||
fun showMenuBar() |
||||
fun navigateToSearch(searchResult: SearchResult) |
||||
} |
||||
|
||||
} |
@ -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" |
||||
tools:visibility="invisible"/> |
||||
|
||||
<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:rotation="180" |
||||
android:src="@drawable/ic_arrow_right" |
||||
android:tint="@color/primaryText" |
||||
android:tooltipText="@string/search_content" |
||||
app:backgroundTint="@color/background_menu" |
||||
app:elevation="2dp" |
||||
app:fabSize="mini" |
||||
app:layout_constraintBottom_toBottomOf="parent" |
||||
app:layout_constraintStart_toStartOf="parent" |
||||
app:layout_constraintTop_toTopOf="parent" |
||||
app:pressedTranslationZ="2dp" |
||||
tools:ignore="UnusedAttribute" /> |
||||
|
||||
<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_arrow_right" |
||||
android:tint="@color/primaryText" |
||||
android:tooltipText="@string/search_content" |
||||
app:backgroundTint="@color/background_menu" |
||||
app:elevation="2dp" |
||||
app:fabSize="mini" |
||||
app:layout_constraintBottom_toBottomOf="parent" |
||||
app:layout_constraintEnd_toEndOf="parent" |
||||
app:layout_constraintTop_toTopOf="parent" |
||||
app:pressedTranslationZ="2dp" |
||||
tools:ignore="UnusedAttribute" /> |
||||
|
||||
<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"> |
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView |
||||
android:id="@+id/iv_search_content_up" |
||||
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_down" |
||||
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" /> |
||||
|
||||
<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" /> |
||||
</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"> |
||||
|
||||
<View |
||||
android:layout_width="0dp" |
||||
android:layout_height="match_parent" |
||||
android:layout_weight="1" |
||||
android:importantForAccessibility="no" /> |
||||
|
||||
<!--结果按钮--> |
||||
<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:maxLines="1" |
||||
android:text="结果" |
||||
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:maxLines="1" |
||||
android:text="@string/main_menu" |
||||
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:maxLines="1" |
||||
android:text="退出" |
||||
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:maxLines="1"--> |
||||
<!-- android:text="@string/setting"--> |
||||
<!-- android:textColor="@color/primaryText"--> |
||||
<!-- android:textSize="12sp" />--> |
||||
<!-- </LinearLayout>--> |
||||
|
||||
<View |
||||
android:layout_width="0dp" |
||||
android:layout_height="match_parent" |
||||
android:layout_weight="1" |
||||
android:importantForAccessibility="no" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout> |
Loading…
Reference in new issue