Update source filter items

TristateItem now uses TriStateCheckBox to animate between states
SortItem now uses SortTextView
Both views now support textAppearance & drawablePadding via xml
This commit is contained in:
Jays2Kings 2021-08-10 19:29:36 -04:00
parent 8b511b5708
commit 1198c2a77e
9 changed files with 82 additions and 71 deletions

View File

@ -1,17 +1,14 @@
package eu.kanade.tachiyomi.ui.source.filter package eu.kanade.tachiyomi.ui.source.filter
import android.view.View import android.view.View
import android.widget.CheckedTextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractSectionableItem import eu.davidea.flexibleadapter.items.AbstractSectionableItem
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.widget.SortTextView
class SortItem(val name: String, val group: SortGroup) : AbstractSectionableItem<SortItem.Holder, SortGroup>(group) { class SortItem(val name: String, val group: SortGroup) : AbstractSectionableItem<SortItem.Holder, SortGroup>(group) {
@ -30,29 +27,17 @@ class SortItem(val name: String, val group: SortGroup) : AbstractSectionableItem
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: MutableList<Any?>?) { override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: MutableList<Any?>?) {
val view = holder.text val view = holder.text
view.text = name view.text = name
val filter = group.filter val filter = group.filter
val i = filter.values.indexOf(name) val i = filter.values.indexOf(name)
view.state = when (filter.state) {
fun getIcon() = when (filter.state) { Filter.Sort.Selection(i, false) -> SortTextView.State.DESCENDING
Filter.Sort.Selection(i, false) -> Filter.Sort.Selection(i, true) -> SortTextView.State.ASCENDING
VectorDrawableCompat.create(view.resources, R.drawable.ic_arrow_downward_32dp, null) else -> SortTextView.State.NONE
?.apply { setTint(view.context.getResourceColor(R.attr.colorAccent)) }
Filter.Sort.Selection(i, true) ->
VectorDrawableCompat.create(view.resources, R.drawable.ic_arrow_upward_32dp, null)
?.apply { setTint(view.context.getResourceColor(R.attr.colorAccent)) }
else -> ContextCompat.getDrawable(view.context, R.drawable.empty_drawable_32dp)
}
view.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null)
holder.itemView.setOnClickListener {
val pre = filter.state?.index ?: i
if (pre != i) {
filter.state = Filter.Sort.Selection(i, false)
} else {
filter.state = Filter.Sort.Selection(i, filter.state?.ascending == false)
} }
view.setOnSortChangeListener { _, state ->
filter.state = Filter.Sort.Selection(i, state == SortTextView.State.ASCENDING)
group.subItems.forEach { adapter.notifyItemChanged(adapter.getGlobalPositionOf(it)) } group.subItems.forEach { adapter.notifyItemChanged(adapter.getGlobalPositionOf(it)) }
} }
} }
@ -72,6 +57,6 @@ class SortItem(val name: String, val group: SortGroup) : AbstractSectionableItem
class Holder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>) : FlexibleViewHolder(view, adapter) { class Holder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>) : FlexibleViewHolder(view, adapter) {
val text: CheckedTextView = itemView.findViewById(R.id.nav_view_item) val text: SortTextView = itemView.findViewById(R.id.nav_view_item)
} }
} }

View File

@ -1,23 +1,19 @@
package eu.kanade.tachiyomi.ui.source.filter package eu.kanade.tachiyomi.ui.source.filter
import android.view.View import android.view.View
import android.widget.CheckedTextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.google.android.material.R
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.widget.TriStateCheckBox
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.R as TR import eu.kanade.tachiyomi.R as TR
open class TriStateItem(val filter: Filter.TriState) : AbstractFlexibleItem<TriStateItem.Holder>() { open class TriStateItem(val filter: Filter.TriState) : AbstractFlexibleItem<TriStateItem.Holder>() {
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return TR.layout.navigation_view_checkedtext return TR.layout.navigation_view_tristatebox
} }
override fun getItemViewType(): Int { override fun getItemViewType(): Int {
@ -31,30 +27,19 @@ open class TriStateItem(val filter: Filter.TriState) : AbstractFlexibleItem<TriS
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: MutableList<Any?>?) { override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: MutableList<Any?>?) {
val view = holder.text val view = holder.text
view.text = filter.name view.text = filter.name
view.state = when (filter.state) {
fun getIcon() = VectorDrawableCompat.create( Filter.TriState.STATE_IGNORE -> TriStateCheckBox.State.UNCHECKED
view.resources, Filter.TriState.STATE_INCLUDE -> TriStateCheckBox.State.CHECKED
when (filter.state) { Filter.TriState.STATE_EXCLUDE -> TriStateCheckBox.State.INVERSED
Filter.TriState.STATE_IGNORE -> TR.drawable.ic_check_box_outline_blank_24dp
Filter.TriState.STATE_INCLUDE -> TR.drawable.ic_check_box_24dp
Filter.TriState.STATE_EXCLUDE -> TR.drawable.ic_check_box_x_24dp
else -> throw Exception("Unknown state") else -> throw Exception("Unknown state")
},
null
)?.apply {
val color = if (filter.state == Filter.TriState.STATE_INCLUDE) {
R.attr.colorAccent
} else {
android.R.attr.textColorSecondary
} }
setTint(view.context.getResourceColor(color)) view.setOnCheckedChangeListener { _, state ->
filter.state = when (state) {
TriStateCheckBox.State.UNCHECKED -> Filter.TriState.STATE_IGNORE
TriStateCheckBox.State.CHECKED -> Filter.TriState.STATE_INCLUDE
TriStateCheckBox.State.INVERSED -> Filter.TriState.STATE_EXCLUDE
} }
view.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null)
holder.itemView.setOnClickListener {
filter.state = (filter.state + 1) % 3
view.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null)
} }
} }
@ -70,12 +55,6 @@ open class TriStateItem(val filter: Filter.TriState) : AbstractFlexibleItem<TriS
class Holder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>) : FlexibleViewHolder(view, adapter) { class Holder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>) : FlexibleViewHolder(view, adapter) {
val text: CheckedTextView = itemView.findViewById(TR.id.nav_view_item) val text: TriStateCheckBox = itemView.findViewById(TR.id.nav_view_item)
init {
// Align with native checkbox
text.setPadding(4.dpToPx, 0, 0, 0)
text.compoundDrawablePadding = 20.dpToPx
}
} }
} }

View File

@ -6,7 +6,6 @@ import android.util.AttributeSet
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.CheckBox import android.widget.CheckBox
import android.widget.CheckedTextView
import android.widget.EditText import android.widget.EditText
import android.widget.RadioButton import android.widget.RadioButton
import android.widget.TextView import android.widget.TextView
@ -147,7 +146,7 @@ open class SimpleNavigationView @JvmOverloads constructor(
class MultiStateHolder(parent: ViewGroup, listener: View.OnClickListener?) : class MultiStateHolder(parent: ViewGroup, listener: View.OnClickListener?) :
ClickableHolder(parent.inflate(TR.layout.navigation_view_checkedtext), listener) { ClickableHolder(parent.inflate(TR.layout.navigation_view_checkedtext), listener) {
val text: CheckedTextView = itemView.findViewById(TR.id.nav_view_item) val text: TriStateCheckBox = itemView.findViewById(TR.id.nav_view_item)
} }
class SpinnerHolder(parent: ViewGroup, listener: OnClickListener? = null) : class SpinnerHolder(parent: ViewGroup, listener: OnClickListener? = null) :

View File

@ -4,6 +4,7 @@ import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.core.view.updateLayoutParams
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.SortTextViewBinding import eu.kanade.tachiyomi.databinding.SortTextViewBinding
import eu.kanade.tachiyomi.util.view.setVectorCompat import eu.kanade.tachiyomi.util.view.setVectorCompat
@ -45,6 +46,18 @@ class SortTextView constructor(context: Context, attrs: AttributeSet?) :
val maxLines = a.getInt(R.styleable.SortTextView_android_maxLines, Int.MAX_VALUE) val maxLines = a.getInt(R.styleable.SortTextView_android_maxLines, Int.MAX_VALUE)
binding.textView.maxLines = maxLines binding.textView.maxLines = maxLines
val resourceId = a.getResourceId(R.styleable.SortTextView_android_textAppearance, 0)
if (resourceId != 0) {
binding.textView.setTextAppearance(resourceId)
}
val drawablePadding = a.getDimensionPixelSize(R.styleable.SortTextView_android_drawablePadding, 0)
if (drawablePadding != 0) {
binding.textView.updateLayoutParams<MarginLayoutParams> {
marginStart = drawablePadding
}
}
a.recycle() a.recycle()
setOnClickListener { setOnClickListener {

View File

@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.databinding.TriStateCheckBoxBinding
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.setAnimVectorCompat import eu.kanade.tachiyomi.util.view.setAnimVectorCompat
import eu.kanade.tachiyomi.util.view.setVectorCompat import eu.kanade.tachiyomi.util.view.setVectorCompat
import eu.kanade.tachiyomi.util.view.updateLayoutParams
class TriStateCheckBox constructor(context: Context, attrs: AttributeSet?) : class TriStateCheckBox constructor(context: Context, attrs: AttributeSet?) :
FrameLayout(context, attrs) { FrameLayout(context, attrs) {
@ -57,6 +58,18 @@ class TriStateCheckBox constructor(context: Context, attrs: AttributeSet?) :
val maxLines = a.getInt(R.styleable.TriStateCheckBox_android_maxLines, Int.MAX_VALUE) val maxLines = a.getInt(R.styleable.TriStateCheckBox_android_maxLines, Int.MAX_VALUE)
binding.textView.maxLines = maxLines binding.textView.maxLines = maxLines
val resourceId = a.getResourceId(R.styleable.TriStateCheckBox_android_textAppearance, 0)
if (resourceId != 0) {
binding.textView.setTextAppearance(resourceId)
}
val drawablePadding = a.getDimensionPixelSize(R.styleable.TriStateCheckBox_android_drawablePadding, 0)
if (drawablePadding != 0) {
binding.textView.updateLayoutParams<MarginLayoutParams> {
marginStart = drawablePadding
}
}
a.recycle() a.recycle()
setOnClickListener { setOnClickListener {

View File

@ -8,7 +8,7 @@
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:focusable="true"> android:focusable="true">
<CheckBox <com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/nav_view_item" android:id="@+id/nav_view_item"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -17,7 +17,6 @@
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:gravity="center_vertical|start" android:gravity="center_vertical|start"
android:maxLines="1" android:maxLines="1"
android:clickable="false" android:clickable="false" />
android:textAppearance="@style/TextAppearance.AppCompat.Body2" />
</LinearLayout> </LinearLayout>

View File

@ -1,21 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeightSmall" android:layout_height="?attr/listPreferredItemHeightSmall"
android:paddingStart="?attr/listPreferredItemPaddingStart" xmlns:tools="http://schemas.android.com/tools"
android:paddingEnd="?attr/listPreferredItemPaddingEnd"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:focusable="true"> android:focusable="true">
<CheckedTextView <eu.kanade.tachiyomi.widget.SortTextView
android:id="@+id/nav_view_item" android:id="@+id/nav_view_item"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
android:drawablePadding="@dimen/material_component_lists_icon_left_padding" android:drawablePadding="@dimen/material_component_lists_icon_left_padding"
tools:text="Sorting by"
android:gravity="center_vertical|start" android:gravity="center_vertical|start"
android:maxLines="1" android:maxLines="1" />
android:textAppearance="@style/TextAppearance.AppCompat.Body2" />
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeightSmall"
xmlns:tools="http://schemas.android.com/tools"
android:paddingStart="?attr/listPreferredItemPaddingStart"
android:paddingEnd="?attr/listPreferredItemPaddingEnd"
android:background="?attr/selectableItemBackground"
android:focusable="true">
<eu.kanade.tachiyomi.widget.TriStateCheckBox
android:id="@+id/nav_view_item"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:drawablePadding="@dimen/material_component_lists_icon_left_padding"
android:gravity="center_vertical|start"
android:maxLines="1"
tools:text="Item" />
</LinearLayout>

View File

@ -31,12 +31,16 @@
<declare-styleable name="TriStateCheckBox"> <declare-styleable name="TriStateCheckBox">
<attr name="android:text" format="reference|string"/> <attr name="android:text" format="reference|string"/>
<attr name="android:maxLines"/> <attr name="android:maxLines"/>
<attr name="android:textAppearance"/>
<attr name="android:drawablePadding"/>
</declare-styleable> </declare-styleable>
<declare-styleable name="SortTextView"> <declare-styleable name="SortTextView">
<attr name="android:text" format="reference|string"/> <attr name="android:text" format="reference|string"/>
<attr name="android:maxLines"/> <attr name="android:maxLines"/>
<attr name="android:textAppearance"/>
<attr name="android:drawablePadding"/>
</declare-styleable> </declare-styleable>
<declare-styleable name="MenuSheetItemView"> <declare-styleable name="MenuSheetItemView">