Use SideSheet instead of BottomSheet on landscape
parent
3d05541f61
commit
0c132a521e
@ -0,0 +1,123 @@
|
|||||||
|
package org.koitharu.kotatsu.core.ui.sheet
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.view.View
|
||||||
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.google.android.material.sidesheet.SideSheetBehavior
|
||||||
|
import com.google.android.material.sidesheet.SideSheetCallback
|
||||||
|
import com.google.android.material.sidesheet.SideSheetDialog
|
||||||
|
import java.util.LinkedList
|
||||||
|
|
||||||
|
sealed class AdaptiveSheetBehavior {
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
protected val callbacks = LinkedList<AdaptiveSheetCallback>()
|
||||||
|
|
||||||
|
abstract var state: Int
|
||||||
|
|
||||||
|
abstract var isDraggable: Boolean
|
||||||
|
|
||||||
|
open val isHideable: Boolean = true
|
||||||
|
|
||||||
|
fun addCallback(callback: AdaptiveSheetCallback) {
|
||||||
|
callbacks.add(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeCallback(callback: AdaptiveSheetCallback) {
|
||||||
|
callbacks.remove(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bottom(
|
||||||
|
private val delegate: BottomSheetBehavior<*>,
|
||||||
|
) : AdaptiveSheetBehavior() {
|
||||||
|
|
||||||
|
override var state: Int
|
||||||
|
get() = delegate.state
|
||||||
|
set(value) {
|
||||||
|
delegate.state = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override var isDraggable: Boolean
|
||||||
|
get() = delegate.isDraggable
|
||||||
|
set(value) {
|
||||||
|
delegate.isDraggable = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override val isHideable: Boolean
|
||||||
|
get() = delegate.isHideable
|
||||||
|
|
||||||
|
var isFitToContents: Boolean
|
||||||
|
get() = delegate.isFitToContents
|
||||||
|
set(value) {
|
||||||
|
delegate.isFitToContents = value
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
delegate.addBottomSheetCallback(
|
||||||
|
object : BottomSheetCallback() {
|
||||||
|
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||||
|
callbacks.forEach { it.onStateChanged(bottomSheet, newState) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||||
|
callbacks.forEach { it.onSlide(bottomSheet, slideOffset) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Side(
|
||||||
|
private val delegate: SideSheetBehavior<*>,
|
||||||
|
) : AdaptiveSheetBehavior() {
|
||||||
|
|
||||||
|
override var state: Int
|
||||||
|
get() = delegate.state
|
||||||
|
set(value) {
|
||||||
|
delegate.state = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override var isDraggable: Boolean
|
||||||
|
get() = delegate.isDraggable
|
||||||
|
set(value) {
|
||||||
|
delegate.isDraggable = value
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
delegate.addCallback(
|
||||||
|
object : SideSheetCallback() {
|
||||||
|
override fun onStateChanged(sheet: View, newState: Int) {
|
||||||
|
callbacks.forEach { it.onStateChanged(sheet, newState) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSlide(sheet: View, slideOffset: Float) {
|
||||||
|
callbacks.forEach { it.onSlide(sheet, slideOffset) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val STATE_EXPANDED = SideSheetBehavior.STATE_EXPANDED
|
||||||
|
const val STATE_SETTLING = SideSheetBehavior.STATE_SETTLING
|
||||||
|
const val STATE_DRAGGING = SideSheetBehavior.STATE_DRAGGING
|
||||||
|
const val STATE_HIDDEN = SideSheetBehavior.STATE_HIDDEN
|
||||||
|
|
||||||
|
fun from(dialog: Dialog?): AdaptiveSheetBehavior? = when (dialog) {
|
||||||
|
is BottomSheetDialog -> Bottom(dialog.behavior)
|
||||||
|
is SideSheetDialog -> Side(dialog.behavior)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun from(lp: CoordinatorLayout.LayoutParams): AdaptiveSheetBehavior? = when (val behavior = lp.behavior) {
|
||||||
|
is BottomSheetBehavior<*> -> Bottom(behavior)
|
||||||
|
is SideSheetBehavior<*> -> Side(behavior)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package org.koitharu.kotatsu.core.ui.sheet
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
interface AdaptiveSheetCallback {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the sheet changes its state.
|
||||||
|
*
|
||||||
|
* @param sheet The sheet view.
|
||||||
|
* @param newState The new state.
|
||||||
|
*/
|
||||||
|
fun onStateChanged(sheet: View, newState: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the sheet is being dragged.
|
||||||
|
*
|
||||||
|
* @param sheet The sheet view.
|
||||||
|
* @param slideOffset The new offset of this sheet.
|
||||||
|
*/
|
||||||
|
fun onSlide(sheet: View, slideOffset: Float) = Unit
|
||||||
|
}
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
package org.koitharu.kotatsu.core.ui.sheet
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowInsets
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.annotation.AttrRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
import androidx.core.content.withStyledAttributes
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.view.isGone
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.parents
|
||||||
|
import org.koitharu.kotatsu.databinding.LayoutSheetHeaderAdaptiveBinding
|
||||||
|
|
||||||
|
class AdaptiveSheetHeaderBar @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
@AttrRes defStyleAttr: Int = 0,
|
||||||
|
) : LinearLayout(context, attrs, defStyleAttr), AdaptiveSheetCallback {
|
||||||
|
|
||||||
|
private val binding = LayoutSheetHeaderAdaptiveBinding.inflate(LayoutInflater.from(context), this)
|
||||||
|
private var sheetBehavior: AdaptiveSheetBehavior? = null
|
||||||
|
|
||||||
|
var title: CharSequence?
|
||||||
|
get() = binding.textViewTitle.text
|
||||||
|
set(value) {
|
||||||
|
binding.textViewTitle.text = value
|
||||||
|
}
|
||||||
|
|
||||||
|
val isExpanded: Boolean
|
||||||
|
get() = binding.dragHandle.isGone
|
||||||
|
|
||||||
|
init {
|
||||||
|
orientation = VERTICAL
|
||||||
|
binding.buttonClose.setOnClickListener { dismissSheet() }
|
||||||
|
context.withStyledAttributes(
|
||||||
|
attrs,
|
||||||
|
R.styleable.AdaptiveSheetHeaderBar, defStyleAttr,
|
||||||
|
) {
|
||||||
|
title = getText(R.styleable.AdaptiveSheetHeaderBar_title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow()
|
||||||
|
dispatchInsets(ViewCompat.getRootWindowInsets(this))
|
||||||
|
setBottomSheetBehavior(findParentSheetBehavior())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromWindow() {
|
||||||
|
setBottomSheetBehavior(null)
|
||||||
|
super.onDetachedFromWindow()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets {
|
||||||
|
dispatchInsets(if (insets != null) WindowInsetsCompat.toWindowInsetsCompat(insets) else null)
|
||||||
|
return super.onApplyWindowInsets(insets)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStateChanged(sheet: View, newState: Int) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTitle(@StringRes resId: Int) {
|
||||||
|
binding.textViewTitle.setText(resId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dispatchInsets(insets: WindowInsetsCompat?) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setBottomSheetBehavior(behavior: AdaptiveSheetBehavior?) {
|
||||||
|
binding.dragHandle.isVisible = behavior is AdaptiveSheetBehavior.Bottom
|
||||||
|
binding.layoutSidesheet.isVisible = behavior is AdaptiveSheetBehavior.Side
|
||||||
|
sheetBehavior?.removeCallback(this)
|
||||||
|
sheetBehavior = behavior
|
||||||
|
behavior?.addCallback(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dismissSheet() {
|
||||||
|
sheetBehavior?.state = AdaptiveSheetBehavior.STATE_HIDDEN
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findParentSheetBehavior(): AdaptiveSheetBehavior? {
|
||||||
|
for (p in parents) {
|
||||||
|
val layoutParams = (p as? View)?.layoutParams
|
||||||
|
if (layoutParams is CoordinatorLayout.LayoutParams) {
|
||||||
|
AdaptiveSheetBehavior.from(layoutParams)?.let {
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,161 @@
|
|||||||
|
package org.koitharu.kotatsu.core.ui.sheet
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.ViewGroup.LayoutParams
|
||||||
|
import androidx.activity.ComponentDialog
|
||||||
|
import androidx.activity.OnBackPressedDispatcher
|
||||||
|
import androidx.appcompat.app.AppCompatDialogFragment
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.google.android.material.sidesheet.SideSheetDialog
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import com.google.android.material.R as materialR
|
||||||
|
|
||||||
|
abstract class BaseAdaptiveSheet<B : ViewBinding> : AppCompatDialogFragment() {
|
||||||
|
|
||||||
|
private var waitingForDismissAllowingStateLoss = false
|
||||||
|
|
||||||
|
var viewBinding: B? = null
|
||||||
|
private set
|
||||||
|
|
||||||
|
@Deprecated("", ReplaceWith("requireViewBinding()"))
|
||||||
|
protected val binding: B
|
||||||
|
get() = requireViewBinding()
|
||||||
|
|
||||||
|
protected val behavior: AdaptiveSheetBehavior?
|
||||||
|
get() = AdaptiveSheetBehavior.from(dialog)
|
||||||
|
|
||||||
|
val isExpanded: Boolean
|
||||||
|
get() = behavior?.state == AdaptiveSheetBehavior.STATE_EXPANDED
|
||||||
|
|
||||||
|
val onBackPressedDispatcher: OnBackPressedDispatcher
|
||||||
|
get() = (requireDialog() as ComponentDialog).onBackPressedDispatcher
|
||||||
|
|
||||||
|
final override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?,
|
||||||
|
): View {
|
||||||
|
val binding = onCreateViewBinding(inflater, container)
|
||||||
|
viewBinding = binding
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
val binding = requireViewBinding()
|
||||||
|
onViewBindingCreated(binding, savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
viewBinding = null
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
val context = requireContext()
|
||||||
|
return if (context.resources.getBoolean(R.bool.is_tablet)) {
|
||||||
|
SideSheetDialog(context, theme)
|
||||||
|
} else {
|
||||||
|
BottomSheetDialog(context, theme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addSheetCallback(callback: AdaptiveSheetCallback) {
|
||||||
|
val b = behavior ?: return
|
||||||
|
b.addCallback(callback)
|
||||||
|
val rootView = dialog?.findViewById<View>(materialR.id.design_bottom_sheet)
|
||||||
|
?: dialog?.findViewById(materialR.id.coordinator)
|
||||||
|
if (rootView != null) {
|
||||||
|
callback.onStateChanged(rootView, b.state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): B
|
||||||
|
|
||||||
|
protected open fun onViewBindingCreated(binding: B, savedInstanceState: Bundle?) = Unit
|
||||||
|
|
||||||
|
protected fun setExpanded(isExpanded: Boolean, isLocked: Boolean) {
|
||||||
|
val b = behavior ?: return
|
||||||
|
if (isExpanded) {
|
||||||
|
b.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
|
if (b is AdaptiveSheetBehavior.Bottom) {
|
||||||
|
b.isFitToContents = !isExpanded
|
||||||
|
val rootView = dialog?.findViewById<View>(materialR.id.design_bottom_sheet)
|
||||||
|
rootView?.updateLayoutParams {
|
||||||
|
height = if (isExpanded) LayoutParams.MATCH_PARENT else LayoutParams.WRAP_CONTENT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.isDraggable = !isLocked
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requireViewBinding(): B = checkNotNull(viewBinding) {
|
||||||
|
"Fragment $this did not return a ViewBinding from onCreateView() or this was called before onCreateView()."
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dismiss() {
|
||||||
|
if (!tryDismissWithAnimation(false)) {
|
||||||
|
super.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun dismissAllowingStateLoss() {
|
||||||
|
if (!tryDismissWithAnimation(true)) {
|
||||||
|
super.dismissAllowingStateLoss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to dismiss the dialog fragment with the bottom sheet animation. Returns true if possible,
|
||||||
|
* false otherwise.
|
||||||
|
*/
|
||||||
|
private fun tryDismissWithAnimation(allowingStateLoss: Boolean): Boolean {
|
||||||
|
val shouldDismissWithAnimation = when (val dialog = dialog) {
|
||||||
|
is BottomSheetDialog -> dialog.dismissWithAnimation
|
||||||
|
is SideSheetDialog -> dialog.isDismissWithSheetAnimationEnabled
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
val behavior = behavior ?: return false
|
||||||
|
return if (shouldDismissWithAnimation && behavior.isHideable) {
|
||||||
|
dismissWithAnimation(behavior, allowingStateLoss)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dismissWithAnimation(behavior: AdaptiveSheetBehavior, allowingStateLoss: Boolean) {
|
||||||
|
waitingForDismissAllowingStateLoss = allowingStateLoss
|
||||||
|
if (behavior.state == AdaptiveSheetBehavior.STATE_HIDDEN) {
|
||||||
|
dismissAfterAnimation()
|
||||||
|
} else {
|
||||||
|
behavior.addCallback(SheetDismissCallback())
|
||||||
|
behavior.state = AdaptiveSheetBehavior.STATE_HIDDEN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dismissAfterAnimation() {
|
||||||
|
if (waitingForDismissAllowingStateLoss) {
|
||||||
|
super.dismissAllowingStateLoss()
|
||||||
|
} else {
|
||||||
|
super.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class SheetDismissCallback : AdaptiveSheetCallback {
|
||||||
|
override fun onStateChanged(sheet: View, newState: Int) {
|
||||||
|
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
||||||
|
dismissAfterAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSlide(sheet: View, slideOffset: Float) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package org.koitharu.kotatsu.favourites.ui.categories.select.adapter
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.databinding.ItemCategoriesHeaderBinding
|
||||||
|
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity
|
||||||
|
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity
|
||||||
|
import org.koitharu.kotatsu.favourites.ui.categories.select.model.CategoriesHeaderItem
|
||||||
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
|
|
||||||
|
fun categoriesHeaderAD() = adapterDelegateViewBinding<CategoriesHeaderItem, ListModel, ItemCategoriesHeaderBinding>(
|
||||||
|
{ inflater, parent -> ItemCategoriesHeaderBinding.inflate(inflater, parent, false) },
|
||||||
|
) {
|
||||||
|
|
||||||
|
val onClickListener = View.OnClickListener { v ->
|
||||||
|
val intent = when (v.id) {
|
||||||
|
R.id.button_create -> FavouritesCategoryEditActivity.newIntent(v.context)
|
||||||
|
R.id.button_manage -> FavouriteCategoriesActivity.newIntent(v.context)
|
||||||
|
else -> return@OnClickListener
|
||||||
|
}
|
||||||
|
v.context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.buttonCreate.setOnClickListener(onClickListener)
|
||||||
|
binding.buttonManage.setOnClickListener(onClickListener)
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package org.koitharu.kotatsu.favourites.ui.categories.select.model
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
|
|
||||||
|
class CategoriesHeaderItem : ListModel {
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean = other?.javaClass == CategoriesHeaderItem::class.java
|
||||||
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
package org.koitharu.kotatsu.favourites.ui.categories.select.model
|
package org.koitharu.kotatsu.favourites.ui.categories.select.model
|
||||||
|
|
||||||
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
|
|
||||||
data class MangaCategoryItem(
|
data class MangaCategoryItem(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
val name: String,
|
val name: String,
|
||||||
val isChecked: Boolean
|
val isChecked: Boolean,
|
||||||
)
|
) : ListModel
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="12dp"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_create"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/create_category"
|
||||||
|
app:icon="@drawable/ic_add" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_manage"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:text="@string/manage"
|
||||||
|
app:icon="@drawable/ic_edit" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge
|
||||||
|
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="wrap_content"
|
||||||
|
tools:orientation="vertical"
|
||||||
|
tools:parentTag="android.widget.LinearLayout">
|
||||||
|
|
||||||
|
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
|
||||||
|
android:id="@+id/dragHandle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_sidesheet"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:baselineAligned="false"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="?textAppearanceBodyLarge"
|
||||||
|
tools:text="@string/filter" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/button_close"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:padding="16dp"
|
||||||
|
app:srcCompat="?actionModeCloseDrawable"
|
||||||
|
app:tint="?colorControlActivated" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</merge>
|
||||||
Loading…
Reference in New Issue