mirror of
https://github.com/Aviortheking/Neo-Store.git
synced 2025-06-16 04:19:19 +00:00
Reformated all the code
This commit is contained in:
@ -8,41 +8,60 @@ import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.widget.TextView
|
||||
|
||||
object ClickableMovementMethod: MovementMethod {
|
||||
override fun initialize(widget: TextView, text: Spannable) {
|
||||
Selection.removeSelection(text)
|
||||
}
|
||||
|
||||
override fun onTouchEvent(widget: TextView, text: Spannable, event: MotionEvent): Boolean {
|
||||
val action = event.action
|
||||
val down = action == MotionEvent.ACTION_DOWN
|
||||
val up = action == MotionEvent.ACTION_UP
|
||||
return (down || up) && run {
|
||||
val x = event.x.toInt() - widget.totalPaddingLeft + widget.scrollX
|
||||
val y = event.y.toInt() - widget.totalPaddingTop + widget.scrollY
|
||||
val layout = widget.layout
|
||||
val line = layout.getLineForVertical(y)
|
||||
val offset = layout.getOffsetForHorizontal(line, x.toFloat())
|
||||
val span = text.getSpans(offset, offset, ClickableSpan::class.java)?.firstOrNull()
|
||||
if (span != null) {
|
||||
if (down) {
|
||||
Selection.setSelection(text, text.getSpanStart(span), text.getSpanEnd(span))
|
||||
} else {
|
||||
span.onClick(widget)
|
||||
}
|
||||
true
|
||||
} else {
|
||||
object ClickableMovementMethod : MovementMethod {
|
||||
override fun initialize(widget: TextView, text: Spannable) {
|
||||
Selection.removeSelection(text)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyDown(widget: TextView, text: Spannable, keyCode: Int, event: KeyEvent): Boolean = false
|
||||
override fun onKeyUp(widget: TextView, text: Spannable, keyCode: Int, event: KeyEvent): Boolean = false
|
||||
override fun onKeyOther(view: TextView, text: Spannable, event: KeyEvent): Boolean = false
|
||||
override fun onTakeFocus(widget: TextView, text: Spannable, direction: Int) = Unit
|
||||
override fun onTrackballEvent(widget: TextView, text: Spannable, event: MotionEvent): Boolean = false
|
||||
override fun onGenericMotionEvent(widget: TextView, text: Spannable, event: MotionEvent): Boolean = false
|
||||
override fun canSelectArbitrarily(): Boolean = false
|
||||
override fun onTouchEvent(widget: TextView, text: Spannable, event: MotionEvent): Boolean {
|
||||
val action = event.action
|
||||
val down = action == MotionEvent.ACTION_DOWN
|
||||
val up = action == MotionEvent.ACTION_UP
|
||||
return (down || up) && run {
|
||||
val x = event.x.toInt() - widget.totalPaddingLeft + widget.scrollX
|
||||
val y = event.y.toInt() - widget.totalPaddingTop + widget.scrollY
|
||||
val layout = widget.layout
|
||||
val line = layout.getLineForVertical(y)
|
||||
val offset = layout.getOffsetForHorizontal(line, x.toFloat())
|
||||
val span = text.getSpans(offset, offset, ClickableSpan::class.java)?.firstOrNull()
|
||||
if (span != null) {
|
||||
if (down) {
|
||||
Selection.setSelection(text, text.getSpanStart(span), text.getSpanEnd(span))
|
||||
} else {
|
||||
span.onClick(widget)
|
||||
}
|
||||
true
|
||||
} else {
|
||||
Selection.removeSelection(text)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyDown(
|
||||
widget: TextView,
|
||||
text: Spannable,
|
||||
keyCode: Int,
|
||||
event: KeyEvent
|
||||
): Boolean = false
|
||||
|
||||
override fun onKeyUp(
|
||||
widget: TextView,
|
||||
text: Spannable,
|
||||
keyCode: Int,
|
||||
event: KeyEvent
|
||||
): Boolean = false
|
||||
|
||||
override fun onKeyOther(view: TextView, text: Spannable, event: KeyEvent): Boolean = false
|
||||
override fun onTakeFocus(widget: TextView, text: Spannable, direction: Int) = Unit
|
||||
override fun onTrackballEvent(widget: TextView, text: Spannable, event: MotionEvent): Boolean =
|
||||
false
|
||||
|
||||
override fun onGenericMotionEvent(
|
||||
widget: TextView,
|
||||
text: Spannable,
|
||||
event: MotionEvent
|
||||
): Boolean = false
|
||||
|
||||
override fun canSelectArbitrarily(): Boolean = false
|
||||
}
|
||||
|
@ -3,33 +3,34 @@ package com.looker.droidify.widget
|
||||
import android.database.Cursor
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
abstract class CursorRecyclerAdapter<VT: Enum<VT>, VH: RecyclerView.ViewHolder>: EnumRecyclerAdapter<VT, VH>() {
|
||||
init {
|
||||
super.setHasStableIds(true)
|
||||
}
|
||||
|
||||
private var rowIdIndex = 0
|
||||
|
||||
var cursor: Cursor? = null
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field?.close()
|
||||
field = value
|
||||
rowIdIndex = value?.getColumnIndexOrThrow("_id") ?: 0
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
abstract class CursorRecyclerAdapter<VT : Enum<VT>, VH : RecyclerView.ViewHolder> :
|
||||
EnumRecyclerAdapter<VT, VH>() {
|
||||
init {
|
||||
super.setHasStableIds(true)
|
||||
}
|
||||
|
||||
final override fun setHasStableIds(hasStableIds: Boolean) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
private var rowIdIndex = 0
|
||||
|
||||
override fun getItemCount(): Int = cursor?.count ?: 0
|
||||
override fun getItemId(position: Int): Long = moveTo(position).getLong(rowIdIndex)
|
||||
var cursor: Cursor? = null
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field?.close()
|
||||
field = value
|
||||
rowIdIndex = value?.getColumnIndexOrThrow("_id") ?: 0
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
fun moveTo(position: Int): Cursor {
|
||||
val cursor = cursor!!
|
||||
cursor.moveToPosition(position)
|
||||
return cursor
|
||||
}
|
||||
final override fun setHasStableIds(hasStableIds: Boolean) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = cursor?.count ?: 0
|
||||
override fun getItemId(position: Int): Long = moveTo(position).getLong(rowIdIndex)
|
||||
|
||||
fun moveTo(position: Int): Cursor {
|
||||
val cursor = cursor!!
|
||||
cursor.moveToPosition(position)
|
||||
return cursor
|
||||
}
|
||||
}
|
||||
|
@ -6,83 +6,113 @@ import android.graphics.Rect
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.looker.droidify.R
|
||||
import com.looker.droidify.utility.extension.resources.*
|
||||
import kotlin.math.*
|
||||
import com.looker.droidify.utility.extension.resources.getDrawableFromAttr
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class DividerItemDecoration(context: Context, private val configure: (context: Context,
|
||||
position: Int, configuration: Configuration) -> Unit): RecyclerView.ItemDecoration() {
|
||||
interface Configuration {
|
||||
fun set(needDivider: Boolean, toTop: Boolean, paddingStart: Int, paddingEnd: Int)
|
||||
}
|
||||
|
||||
private class ConfigurationHolder: Configuration {
|
||||
var needDivider = false
|
||||
var toTop = false
|
||||
var paddingStart = 0
|
||||
var paddingEnd = 0
|
||||
|
||||
override fun set(needDivider: Boolean, toTop: Boolean, paddingStart: Int, paddingEnd: Int) {
|
||||
this.needDivider = needDivider
|
||||
this.toTop = toTop
|
||||
this.paddingStart = paddingStart
|
||||
this.paddingEnd = paddingEnd
|
||||
}
|
||||
}
|
||||
|
||||
private val View.configuration: ConfigurationHolder
|
||||
get() = getTag(R.id.divider_configuration) as? ConfigurationHolder ?: run {
|
||||
val configuration = ConfigurationHolder()
|
||||
setTag(R.id.divider_configuration, configuration)
|
||||
configuration
|
||||
class DividerItemDecoration(
|
||||
context: Context, private val configure: (
|
||||
context: Context,
|
||||
position: Int, configuration: Configuration
|
||||
) -> Unit
|
||||
) : RecyclerView.ItemDecoration() {
|
||||
interface Configuration {
|
||||
fun set(needDivider: Boolean, toTop: Boolean, paddingStart: Int, paddingEnd: Int)
|
||||
}
|
||||
|
||||
private val divider = context.getDrawableFromAttr(android.R.attr.listDivider)
|
||||
private val bounds = Rect()
|
||||
private class ConfigurationHolder : Configuration {
|
||||
var needDivider = false
|
||||
var toTop = false
|
||||
var paddingStart = 0
|
||||
var paddingEnd = 0
|
||||
|
||||
private fun draw(c: Canvas, configuration: ConfigurationHolder, view: View, top: Int, width: Int, rtl: Boolean) {
|
||||
val divider = divider
|
||||
val left = if (rtl) configuration.paddingEnd else configuration.paddingStart
|
||||
val right = width - (if (rtl) configuration.paddingStart else configuration.paddingEnd)
|
||||
val translatedTop = top + view.translationY.roundToInt()
|
||||
divider.alpha = (view.alpha * 0xff).toInt()
|
||||
divider.setBounds(left, translatedTop, right, translatedTop + divider.intrinsicHeight)
|
||||
divider.draw(c)
|
||||
}
|
||||
|
||||
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
val divider = divider
|
||||
val bounds = bounds
|
||||
val rtl = parent.layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||
for (i in 0 until parent.childCount) {
|
||||
val view = parent.getChildAt(i)
|
||||
val configuration = view.configuration
|
||||
if (configuration.needDivider) {
|
||||
val position = parent.getChildAdapterPosition(view)
|
||||
if (position == parent.adapter!!.itemCount - 1) {
|
||||
parent.getDecoratedBoundsWithMargins(view, bounds)
|
||||
draw(c, configuration, view, bounds.bottom, parent.width, rtl)
|
||||
} else {
|
||||
val toTopView = if (configuration.toTop && position >= 0)
|
||||
parent.findViewHolderForAdapterPosition(position + 1)?.itemView else null
|
||||
if (toTopView != null) {
|
||||
parent.getDecoratedBoundsWithMargins(toTopView, bounds)
|
||||
draw(c, configuration, toTopView, bounds.top - divider.intrinsicHeight, parent.width, rtl)
|
||||
} else {
|
||||
parent.getDecoratedBoundsWithMargins(view, bounds)
|
||||
draw(c, configuration, view, bounds.bottom - divider.intrinsicHeight, parent.width, rtl)
|
||||
}
|
||||
override fun set(needDivider: Boolean, toTop: Boolean, paddingStart: Int, paddingEnd: Int) {
|
||||
this.needDivider = needDivider
|
||||
this.toTop = toTop
|
||||
this.paddingStart = paddingStart
|
||||
this.paddingEnd = paddingEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
val configuration = view.configuration
|
||||
val position = parent.getChildAdapterPosition(view)
|
||||
if (position >= 0) {
|
||||
configure(view.context, position, configuration)
|
||||
private val View.configuration: ConfigurationHolder
|
||||
get() = getTag(R.id.divider_configuration) as? ConfigurationHolder ?: run {
|
||||
val configuration = ConfigurationHolder()
|
||||
setTag(R.id.divider_configuration, configuration)
|
||||
configuration
|
||||
}
|
||||
|
||||
private val divider = context.getDrawableFromAttr(android.R.attr.listDivider)
|
||||
private val bounds = Rect()
|
||||
|
||||
private fun draw(
|
||||
c: Canvas,
|
||||
configuration: ConfigurationHolder,
|
||||
view: View,
|
||||
top: Int,
|
||||
width: Int,
|
||||
rtl: Boolean
|
||||
) {
|
||||
val divider = divider
|
||||
val left = if (rtl) configuration.paddingEnd else configuration.paddingStart
|
||||
val right = width - (if (rtl) configuration.paddingStart else configuration.paddingEnd)
|
||||
val translatedTop = top + view.translationY.roundToInt()
|
||||
divider.alpha = (view.alpha * 0xff).toInt()
|
||||
divider.setBounds(left, translatedTop, right, translatedTop + divider.intrinsicHeight)
|
||||
divider.draw(c)
|
||||
}
|
||||
|
||||
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||
val divider = divider
|
||||
val bounds = bounds
|
||||
val rtl = parent.layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||
for (i in 0 until parent.childCount) {
|
||||
val view = parent.getChildAt(i)
|
||||
val configuration = view.configuration
|
||||
if (configuration.needDivider) {
|
||||
val position = parent.getChildAdapterPosition(view)
|
||||
if (position == parent.adapter!!.itemCount - 1) {
|
||||
parent.getDecoratedBoundsWithMargins(view, bounds)
|
||||
draw(c, configuration, view, bounds.bottom, parent.width, rtl)
|
||||
} else {
|
||||
val toTopView = if (configuration.toTop && position >= 0)
|
||||
parent.findViewHolderForAdapterPosition(position + 1)?.itemView else null
|
||||
if (toTopView != null) {
|
||||
parent.getDecoratedBoundsWithMargins(toTopView, bounds)
|
||||
draw(
|
||||
c,
|
||||
configuration,
|
||||
toTopView,
|
||||
bounds.top - divider.intrinsicHeight,
|
||||
parent.width,
|
||||
rtl
|
||||
)
|
||||
} else {
|
||||
parent.getDecoratedBoundsWithMargins(view, bounds)
|
||||
draw(
|
||||
c,
|
||||
configuration,
|
||||
view,
|
||||
bounds.bottom - divider.intrinsicHeight,
|
||||
parent.width,
|
||||
rtl
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
val configuration = view.configuration
|
||||
val position = parent.getChildAdapterPosition(view)
|
||||
if (position >= 0) {
|
||||
configure(view.context, position, configuration)
|
||||
}
|
||||
val needDivider = position < parent.adapter!!.itemCount - 1 && configuration.needDivider
|
||||
outRect.set(0, 0, 0, if (needDivider) divider.intrinsicHeight else 0)
|
||||
}
|
||||
val needDivider = position < parent.adapter!!.itemCount - 1 && configuration.needDivider
|
||||
outRect.set(0, 0, 0, if (needDivider) divider.intrinsicHeight else 0)
|
||||
}
|
||||
}
|
||||
|
@ -4,25 +4,26 @@ import android.util.SparseArray
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
abstract class EnumRecyclerAdapter<VT: Enum<VT>, VH: RecyclerView.ViewHolder>: RecyclerView.Adapter<VH>() {
|
||||
abstract val viewTypeClass: Class<VT>
|
||||
abstract class EnumRecyclerAdapter<VT : Enum<VT>, VH : RecyclerView.ViewHolder> :
|
||||
RecyclerView.Adapter<VH>() {
|
||||
abstract val viewTypeClass: Class<VT>
|
||||
|
||||
private val names = SparseArray<String>()
|
||||
private val names = SparseArray<String>()
|
||||
|
||||
private fun getViewType(viewType: Int): VT {
|
||||
return java.lang.Enum.valueOf(viewTypeClass, names.get(viewType))
|
||||
}
|
||||
private fun getViewType(viewType: Int): VT {
|
||||
return java.lang.Enum.valueOf(viewTypeClass, names.get(viewType))
|
||||
}
|
||||
|
||||
final override fun getItemViewType(position: Int): Int {
|
||||
val enum = getItemEnumViewType(position)
|
||||
names.put(enum.ordinal, enum.name)
|
||||
return enum.ordinal
|
||||
}
|
||||
final override fun getItemViewType(position: Int): Int {
|
||||
val enum = getItemEnumViewType(position)
|
||||
names.put(enum.ordinal, enum.name)
|
||||
return enum.ordinal
|
||||
}
|
||||
|
||||
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
||||
return onCreateViewHolder(parent, getViewType(viewType))
|
||||
}
|
||||
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
||||
return onCreateViewHolder(parent, getViewType(viewType))
|
||||
}
|
||||
|
||||
abstract fun getItemEnumViewType(position: Int): VT
|
||||
abstract fun onCreateViewHolder(parent: ViewGroup, viewType: VT): VH
|
||||
abstract fun getItemEnumViewType(position: Int): VT
|
||||
abstract fun onCreateViewHolder(parent: ViewGroup, viewType: VT): VH
|
||||
}
|
||||
|
@ -5,31 +5,35 @@ import android.util.AttributeSet
|
||||
import android.view.KeyEvent
|
||||
import android.widget.SearchView
|
||||
|
||||
class FocusSearchView: SearchView {
|
||||
constructor(context: Context): super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?): super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr)
|
||||
class FocusSearchView : SearchView {
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
)
|
||||
|
||||
var allowFocus = true
|
||||
var allowFocus = true
|
||||
|
||||
override fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
|
||||
// Always clear focus on back press
|
||||
return if (hasFocus() && event.keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (event.action == KeyEvent.ACTION_UP) {
|
||||
clearFocus()
|
||||
}
|
||||
true
|
||||
} else {
|
||||
super.dispatchKeyEventPreIme(event)
|
||||
override fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
|
||||
// Always clear focus on back press
|
||||
return if (hasFocus() && event.keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (event.action == KeyEvent.ACTION_UP) {
|
||||
clearFocus()
|
||||
}
|
||||
true
|
||||
} else {
|
||||
super.dispatchKeyEventPreIme(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setIconified(iconify: Boolean) {
|
||||
super.setIconified(iconify)
|
||||
override fun setIconified(iconify: Boolean) {
|
||||
super.setIconified(iconify)
|
||||
|
||||
// Don't focus view and raise keyboard unless allowed
|
||||
if (!iconify && !allowFocus) {
|
||||
clearFocus()
|
||||
// Don't focus view and raise keyboard unless allowed
|
||||
if (!iconify && !allowFocus) {
|
||||
clearFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,19 +4,23 @@ import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.LinearLayout
|
||||
|
||||
class FragmentLinearLayout: LinearLayout {
|
||||
constructor(context: Context): super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?): super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr)
|
||||
class FragmentLinearLayout : LinearLayout {
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
)
|
||||
|
||||
init {
|
||||
fitsSystemWindows = true
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
var percentTranslationY: Float
|
||||
get() = height.let { if (it > 0) translationY / it else 0f }
|
||||
set(value) {
|
||||
translationY = value * height
|
||||
init {
|
||||
fitsSystemWindows = true
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
var percentTranslationY: Float
|
||||
get() = height.let { if (it > 0) translationY / it else 0f }
|
||||
set(value) {
|
||||
translationY = value * height
|
||||
}
|
||||
}
|
||||
|
@ -8,240 +8,280 @@ import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.looker.droidify.utility.extension.resources.*
|
||||
import kotlin.math.*
|
||||
import com.looker.droidify.utility.extension.resources.getDrawableFromAttr
|
||||
import com.looker.droidify.utility.extension.resources.sizeScaled
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
class RecyclerFastScroller(private val recyclerView: RecyclerView) {
|
||||
companion object {
|
||||
private const val TRANSITION_IN = 100L
|
||||
private const val TRANSITION_OUT = 200L
|
||||
private const val TRANSITION_OUT_DELAY = 1000L
|
||||
companion object {
|
||||
private const val TRANSITION_IN = 100L
|
||||
private const val TRANSITION_OUT = 200L
|
||||
private const val TRANSITION_OUT_DELAY = 1000L
|
||||
|
||||
private val stateNormal = intArrayOf()
|
||||
private val statePressed = intArrayOf(android.R.attr.state_pressed)
|
||||
}
|
||||
|
||||
private val thumbDrawable = recyclerView.context.getDrawableFromAttr(android.R.attr.fastScrollThumbDrawable)
|
||||
private val trackDrawable = recyclerView.context.getDrawableFromAttr(android.R.attr.fastScrollTrackDrawable)
|
||||
private val minTrackSize = recyclerView.resources.sizeScaled(16)
|
||||
|
||||
private data class FastScrolling(val startAtThumbOffset: Float?, val startY: Float, val currentY: Float)
|
||||
|
||||
private var scrolling = false
|
||||
private var fastScrolling: FastScrolling? = null
|
||||
private var display = Pair(0L, false)
|
||||
|
||||
private val invalidateTransition = Runnable(recyclerView::invalidate)
|
||||
|
||||
private fun updateState(scrolling: Boolean, fastScrolling: FastScrolling?) {
|
||||
val oldDisplay = this.scrolling || this.fastScrolling != null
|
||||
val newDisplay = scrolling || fastScrolling != null
|
||||
this.scrolling = scrolling
|
||||
this.fastScrolling = fastScrolling
|
||||
if (oldDisplay != newDisplay) {
|
||||
recyclerView.removeCallbacks(invalidateTransition)
|
||||
val time = SystemClock.elapsedRealtime()
|
||||
val passed = time - display.first
|
||||
val start = if (newDisplay && passed < (TRANSITION_OUT + TRANSITION_OUT_DELAY)) {
|
||||
if (passed <= TRANSITION_OUT_DELAY) {
|
||||
0L
|
||||
} else {
|
||||
time - ((TRANSITION_OUT_DELAY + TRANSITION_OUT - passed).toFloat() /
|
||||
TRANSITION_OUT * TRANSITION_IN).toLong()
|
||||
}
|
||||
} else if (!newDisplay && passed < TRANSITION_IN) {
|
||||
time - ((TRANSITION_IN - passed).toFloat() / TRANSITION_IN *
|
||||
TRANSITION_OUT).toLong() - TRANSITION_OUT_DELAY
|
||||
} else {
|
||||
if (!newDisplay) {
|
||||
recyclerView.postDelayed(invalidateTransition, TRANSITION_OUT_DELAY)
|
||||
}
|
||||
time
|
||||
}
|
||||
display = Pair(start, newDisplay)
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
private val scrollListener = object: RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
updateState(newState != RecyclerView.SCROLL_STATE_IDLE, fastScrolling)
|
||||
private val stateNormal = intArrayOf()
|
||||
private val statePressed = intArrayOf(android.R.attr.state_pressed)
|
||||
}
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
if (fastScrolling == null) {
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
private val thumbDrawable =
|
||||
recyclerView.context.getDrawableFromAttr(android.R.attr.fastScrollThumbDrawable)
|
||||
private val trackDrawable =
|
||||
recyclerView.context.getDrawableFromAttr(android.R.attr.fastScrollTrackDrawable)
|
||||
private val minTrackSize = recyclerView.resources.sizeScaled(16)
|
||||
|
||||
private inline fun withScroll(callback: (itemHeight: Int, thumbHeight: Int, range: Int) -> Unit): Boolean {
|
||||
val count = recyclerView.adapter?.itemCount ?: 0
|
||||
return count > 0 && run {
|
||||
val itemHeight = Rect().apply { recyclerView
|
||||
.getDecoratedBoundsWithMargins(recyclerView.getChildAt(0), this) }.height()
|
||||
val scrollCount = count - recyclerView.height / itemHeight
|
||||
scrollCount > 0 && run {
|
||||
val range = count * itemHeight
|
||||
val thumbHeight = max(recyclerView.height * recyclerView.height / range, thumbDrawable.intrinsicHeight)
|
||||
range >= recyclerView.height * 2 && run {
|
||||
callback(itemHeight, thumbHeight, range)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private data class FastScrolling(
|
||||
val startAtThumbOffset: Float?,
|
||||
val startY: Float,
|
||||
val currentY: Float
|
||||
)
|
||||
|
||||
private fun calculateOffset(thumbHeight: Int, fastScrolling: FastScrolling): Float {
|
||||
return if (fastScrolling.startAtThumbOffset != null) {
|
||||
(fastScrolling.startAtThumbOffset + (fastScrolling.currentY - fastScrolling.startY) /
|
||||
(recyclerView.height - thumbHeight)).coerceIn(0f, 1f)
|
||||
} else {
|
||||
((fastScrolling.currentY - thumbHeight / 2f) / (recyclerView.height - thumbHeight)).coerceIn(0f, 1f)
|
||||
}
|
||||
}
|
||||
private var scrolling = false
|
||||
private var fastScrolling: FastScrolling? = null
|
||||
private var display = Pair(0L, false)
|
||||
|
||||
private fun currentOffset(itemHeight: Int, range: Int): Float {
|
||||
val view = recyclerView.getChildAt(0)
|
||||
val position = recyclerView.getChildAdapterPosition(view)
|
||||
val positionOffset = -view.top
|
||||
val scrollPosition = position * itemHeight + positionOffset
|
||||
return scrollPosition.toFloat() / (range - recyclerView.height)
|
||||
}
|
||||
private val invalidateTransition = Runnable(recyclerView::invalidate)
|
||||
|
||||
private fun scroll(itemHeight: Int, thumbHeight: Int, range: Int, fastScrolling: FastScrolling) {
|
||||
val offset = calculateOffset(thumbHeight, fastScrolling)
|
||||
val scrollPosition = ((range - recyclerView.height) * offset).roundToInt()
|
||||
val position = scrollPosition / itemHeight
|
||||
val positionOffset = scrollPosition - position * itemHeight
|
||||
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
|
||||
layoutManager.scrollToPositionWithOffset(position, -positionOffset)
|
||||
}
|
||||
|
||||
private val touchListener = object: RecyclerView.OnItemTouchListener {
|
||||
private var disallowIntercept = false
|
||||
|
||||
private fun handleTouchEvent(event: MotionEvent, intercept: Boolean): Boolean {
|
||||
val recyclerView = recyclerView
|
||||
val lastFastScrolling = fastScrolling
|
||||
return when {
|
||||
intercept && disallowIntercept -> {
|
||||
false
|
||||
}
|
||||
event.action == MotionEvent.ACTION_DOWN -> {
|
||||
val rtl = recyclerView.layoutDirection == RecyclerView.LAYOUT_DIRECTION_RTL
|
||||
val trackWidth = max(minTrackSize, max(thumbDrawable.intrinsicWidth, trackDrawable.intrinsicWidth))
|
||||
val atThumbVertical = if (rtl) event.x <= trackWidth else event.x >= recyclerView.width - trackWidth
|
||||
atThumbVertical && run {
|
||||
withScroll { itemHeight, thumbHeight, range ->
|
||||
(recyclerView.parent as? ViewGroup)?.requestDisallowInterceptTouchEvent(true)
|
||||
val offset = currentOffset(itemHeight, range)
|
||||
val thumbY = ((recyclerView.height - thumbHeight) * offset).roundToInt()
|
||||
val atThumb = event.y >= thumbY && event.y <= thumbY + thumbHeight
|
||||
val fastScrolling = FastScrolling(if (atThumb) offset else null, event.y, event.y)
|
||||
scroll(itemHeight, thumbHeight, range, fastScrolling)
|
||||
updateState(scrolling, fastScrolling)
|
||||
recyclerView.invalidate()
|
||||
private fun updateState(scrolling: Boolean, fastScrolling: FastScrolling?) {
|
||||
val oldDisplay = this.scrolling || this.fastScrolling != null
|
||||
val newDisplay = scrolling || fastScrolling != null
|
||||
this.scrolling = scrolling
|
||||
this.fastScrolling = fastScrolling
|
||||
if (oldDisplay != newDisplay) {
|
||||
recyclerView.removeCallbacks(invalidateTransition)
|
||||
val time = SystemClock.elapsedRealtime()
|
||||
val passed = time - display.first
|
||||
val start = if (newDisplay && passed < (TRANSITION_OUT + TRANSITION_OUT_DELAY)) {
|
||||
if (passed <= TRANSITION_OUT_DELAY) {
|
||||
0L
|
||||
} else {
|
||||
time - ((TRANSITION_OUT_DELAY + TRANSITION_OUT - passed).toFloat() /
|
||||
TRANSITION_OUT * TRANSITION_IN).toLong()
|
||||
}
|
||||
} else if (!newDisplay && passed < TRANSITION_IN) {
|
||||
time - ((TRANSITION_IN - passed).toFloat() / TRANSITION_IN *
|
||||
TRANSITION_OUT).toLong() - TRANSITION_OUT_DELAY
|
||||
} else {
|
||||
if (!newDisplay) {
|
||||
recyclerView.postDelayed(invalidateTransition, TRANSITION_OUT_DELAY)
|
||||
}
|
||||
time
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> lastFastScrolling != null && run {
|
||||
val success = withScroll { itemHeight, thumbHeight, range ->
|
||||
val fastScrolling = lastFastScrolling.copy(currentY = event.y)
|
||||
scroll(itemHeight, thumbHeight, range, fastScrolling)
|
||||
updateState(scrolling, fastScrolling)
|
||||
display = Pair(start, newDisplay)
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
val cancel = event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL
|
||||
if (!success || cancel) {
|
||||
(recyclerView.parent as? ViewGroup)?.requestDisallowInterceptTouchEvent(false)
|
||||
updateState(scrolling, null)
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
|
||||
return handleTouchEvent(e, true)
|
||||
}
|
||||
|
||||
override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
|
||||
handleTouchEvent(e, false)
|
||||
}
|
||||
|
||||
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|
||||
this.disallowIntercept = disallowIntercept
|
||||
if (disallowIntercept && fastScrolling != null) {
|
||||
updateState(scrolling, null)
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDraw(canvas: Canvas) {
|
||||
withScroll { itemHeight, thumbHeight, range ->
|
||||
val display = display
|
||||
val time = SystemClock.elapsedRealtime()
|
||||
val passed = time - display.first
|
||||
val shouldInvalidate = display.second && passed < TRANSITION_IN ||
|
||||
!display.second && passed >= TRANSITION_OUT_DELAY && passed < TRANSITION_OUT_DELAY + TRANSITION_OUT
|
||||
val stateValue = (if (display.second) {
|
||||
passed.toFloat() / TRANSITION_IN
|
||||
} else {
|
||||
1f - (passed - TRANSITION_OUT_DELAY).toFloat() / TRANSITION_OUT
|
||||
}).coerceIn(0f, 1f)
|
||||
|
||||
if (stateValue > 0f) {
|
||||
val rtl = recyclerView.layoutDirection == RecyclerView.LAYOUT_DIRECTION_RTL
|
||||
val thumbDrawable = thumbDrawable
|
||||
val trackDrawable = trackDrawable
|
||||
val maxWidth = max(thumbDrawable.intrinsicWidth, trackDrawable.intrinsicHeight)
|
||||
val translateX = (maxWidth * (1f - stateValue)).roundToInt()
|
||||
val fastScrolling = fastScrolling
|
||||
|
||||
val scrollValue = (if (fastScrolling != null) {
|
||||
calculateOffset(thumbHeight, fastScrolling)
|
||||
} else {
|
||||
currentOffset(itemHeight, range)
|
||||
}).coerceIn(0f, 1f)
|
||||
val thumbY = ((recyclerView.height - thumbHeight) * scrollValue).roundToInt()
|
||||
|
||||
trackDrawable.state = if (fastScrolling != null) statePressed else stateNormal
|
||||
val trackExtra = (maxWidth - trackDrawable.intrinsicWidth) / 2
|
||||
if (rtl) {
|
||||
trackDrawable.setBounds(trackExtra - translateX, 0,
|
||||
trackExtra + trackDrawable.intrinsicWidth - translateX, recyclerView.height)
|
||||
} else {
|
||||
trackDrawable.setBounds(recyclerView.width - trackExtra - trackDrawable.intrinsicWidth + translateX,
|
||||
0, recyclerView.width - trackExtra + translateX, recyclerView.height)
|
||||
private val scrollListener = object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
updateState(newState != RecyclerView.SCROLL_STATE_IDLE, fastScrolling)
|
||||
}
|
||||
trackDrawable.draw(canvas)
|
||||
val thumbExtra = (maxWidth - thumbDrawable.intrinsicWidth) / 2
|
||||
thumbDrawable.state = if (fastScrolling != null) statePressed else stateNormal
|
||||
if (rtl) {
|
||||
thumbDrawable.setBounds(thumbExtra - translateX, thumbY,
|
||||
thumbExtra + thumbDrawable.intrinsicWidth - translateX, thumbY + thumbHeight)
|
||||
} else {
|
||||
thumbDrawable.setBounds(recyclerView.width - thumbExtra - thumbDrawable.intrinsicWidth + translateX,
|
||||
thumbY, recyclerView.width - thumbExtra + translateX, thumbY + thumbHeight)
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
if (fastScrolling == null) {
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
}
|
||||
thumbDrawable.draw(canvas)
|
||||
}
|
||||
|
||||
if (shouldInvalidate) {
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
recyclerView.addOnScrollListener(scrollListener)
|
||||
recyclerView.addOnItemTouchListener(touchListener)
|
||||
recyclerView.addItemDecoration(object: RecyclerView.ItemDecoration() {
|
||||
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) = handleDraw(c)
|
||||
})
|
||||
}
|
||||
private inline fun withScroll(callback: (itemHeight: Int, thumbHeight: Int, range: Int) -> Unit): Boolean {
|
||||
val count = recyclerView.adapter?.itemCount ?: 0
|
||||
return count > 0 && run {
|
||||
val itemHeight = Rect().apply {
|
||||
recyclerView
|
||||
.getDecoratedBoundsWithMargins(recyclerView.getChildAt(0), this)
|
||||
}.height()
|
||||
val scrollCount = count - recyclerView.height / itemHeight
|
||||
scrollCount > 0 && run {
|
||||
val range = count * itemHeight
|
||||
val thumbHeight = max(
|
||||
recyclerView.height * recyclerView.height / range,
|
||||
thumbDrawable.intrinsicHeight
|
||||
)
|
||||
range >= recyclerView.height * 2 && run {
|
||||
callback(itemHeight, thumbHeight, range)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateOffset(thumbHeight: Int, fastScrolling: FastScrolling): Float {
|
||||
return if (fastScrolling.startAtThumbOffset != null) {
|
||||
(fastScrolling.startAtThumbOffset + (fastScrolling.currentY - fastScrolling.startY) /
|
||||
(recyclerView.height - thumbHeight)).coerceIn(0f, 1f)
|
||||
} else {
|
||||
((fastScrolling.currentY - thumbHeight / 2f) / (recyclerView.height - thumbHeight)).coerceIn(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun currentOffset(itemHeight: Int, range: Int): Float {
|
||||
val view = recyclerView.getChildAt(0)
|
||||
val position = recyclerView.getChildAdapterPosition(view)
|
||||
val positionOffset = -view.top
|
||||
val scrollPosition = position * itemHeight + positionOffset
|
||||
return scrollPosition.toFloat() / (range - recyclerView.height)
|
||||
}
|
||||
|
||||
private fun scroll(
|
||||
itemHeight: Int,
|
||||
thumbHeight: Int,
|
||||
range: Int,
|
||||
fastScrolling: FastScrolling
|
||||
) {
|
||||
val offset = calculateOffset(thumbHeight, fastScrolling)
|
||||
val scrollPosition = ((range - recyclerView.height) * offset).roundToInt()
|
||||
val position = scrollPosition / itemHeight
|
||||
val positionOffset = scrollPosition - position * itemHeight
|
||||
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
|
||||
layoutManager.scrollToPositionWithOffset(position, -positionOffset)
|
||||
}
|
||||
|
||||
private val touchListener = object : RecyclerView.OnItemTouchListener {
|
||||
private var disallowIntercept = false
|
||||
|
||||
private fun handleTouchEvent(event: MotionEvent, intercept: Boolean): Boolean {
|
||||
val recyclerView = recyclerView
|
||||
val lastFastScrolling = fastScrolling
|
||||
return when {
|
||||
intercept && disallowIntercept -> {
|
||||
false
|
||||
}
|
||||
event.action == MotionEvent.ACTION_DOWN -> {
|
||||
val rtl = recyclerView.layoutDirection == RecyclerView.LAYOUT_DIRECTION_RTL
|
||||
val trackWidth = max(
|
||||
minTrackSize,
|
||||
max(thumbDrawable.intrinsicWidth, trackDrawable.intrinsicWidth)
|
||||
)
|
||||
val atThumbVertical =
|
||||
if (rtl) event.x <= trackWidth else event.x >= recyclerView.width - trackWidth
|
||||
atThumbVertical && run {
|
||||
withScroll { itemHeight, thumbHeight, range ->
|
||||
(recyclerView.parent as? ViewGroup)?.requestDisallowInterceptTouchEvent(
|
||||
true
|
||||
)
|
||||
val offset = currentOffset(itemHeight, range)
|
||||
val thumbY = ((recyclerView.height - thumbHeight) * offset).roundToInt()
|
||||
val atThumb = event.y >= thumbY && event.y <= thumbY + thumbHeight
|
||||
val fastScrolling =
|
||||
FastScrolling(if (atThumb) offset else null, event.y, event.y)
|
||||
scroll(itemHeight, thumbHeight, range, fastScrolling)
|
||||
updateState(scrolling, fastScrolling)
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> lastFastScrolling != null && run {
|
||||
val success = withScroll { itemHeight, thumbHeight, range ->
|
||||
val fastScrolling = lastFastScrolling.copy(currentY = event.y)
|
||||
scroll(itemHeight, thumbHeight, range, fastScrolling)
|
||||
updateState(scrolling, fastScrolling)
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
val cancel =
|
||||
event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL
|
||||
if (!success || cancel) {
|
||||
(recyclerView.parent as? ViewGroup)?.requestDisallowInterceptTouchEvent(
|
||||
false
|
||||
)
|
||||
updateState(scrolling, null)
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
|
||||
return handleTouchEvent(e, true)
|
||||
}
|
||||
|
||||
override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
|
||||
handleTouchEvent(e, false)
|
||||
}
|
||||
|
||||
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
|
||||
this.disallowIntercept = disallowIntercept
|
||||
if (disallowIntercept && fastScrolling != null) {
|
||||
updateState(scrolling, null)
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDraw(canvas: Canvas) {
|
||||
withScroll { itemHeight, thumbHeight, range ->
|
||||
val display = display
|
||||
val time = SystemClock.elapsedRealtime()
|
||||
val passed = time - display.first
|
||||
val shouldInvalidate = display.second && passed < TRANSITION_IN ||
|
||||
!display.second && passed >= TRANSITION_OUT_DELAY && passed < TRANSITION_OUT_DELAY + TRANSITION_OUT
|
||||
val stateValue = (if (display.second) {
|
||||
passed.toFloat() / TRANSITION_IN
|
||||
} else {
|
||||
1f - (passed - TRANSITION_OUT_DELAY).toFloat() / TRANSITION_OUT
|
||||
}).coerceIn(0f, 1f)
|
||||
|
||||
if (stateValue > 0f) {
|
||||
val rtl = recyclerView.layoutDirection == RecyclerView.LAYOUT_DIRECTION_RTL
|
||||
val thumbDrawable = thumbDrawable
|
||||
val trackDrawable = trackDrawable
|
||||
val maxWidth = max(thumbDrawable.intrinsicWidth, trackDrawable.intrinsicHeight)
|
||||
val translateX = (maxWidth * (1f - stateValue)).roundToInt()
|
||||
val fastScrolling = fastScrolling
|
||||
|
||||
val scrollValue = (if (fastScrolling != null) {
|
||||
calculateOffset(thumbHeight, fastScrolling)
|
||||
} else {
|
||||
currentOffset(itemHeight, range)
|
||||
}).coerceIn(0f, 1f)
|
||||
val thumbY = ((recyclerView.height - thumbHeight) * scrollValue).roundToInt()
|
||||
|
||||
trackDrawable.state = if (fastScrolling != null) statePressed else stateNormal
|
||||
val trackExtra = (maxWidth - trackDrawable.intrinsicWidth) / 2
|
||||
if (rtl) {
|
||||
trackDrawable.setBounds(
|
||||
trackExtra - translateX, 0,
|
||||
trackExtra + trackDrawable.intrinsicWidth - translateX, recyclerView.height
|
||||
)
|
||||
} else {
|
||||
trackDrawable.setBounds(
|
||||
recyclerView.width - trackExtra - trackDrawable.intrinsicWidth + translateX,
|
||||
0, recyclerView.width - trackExtra + translateX, recyclerView.height
|
||||
)
|
||||
}
|
||||
trackDrawable.draw(canvas)
|
||||
val thumbExtra = (maxWidth - thumbDrawable.intrinsicWidth) / 2
|
||||
thumbDrawable.state = if (fastScrolling != null) statePressed else stateNormal
|
||||
if (rtl) {
|
||||
thumbDrawable.setBounds(
|
||||
thumbExtra - translateX, thumbY,
|
||||
thumbExtra + thumbDrawable.intrinsicWidth - translateX, thumbY + thumbHeight
|
||||
)
|
||||
} else {
|
||||
thumbDrawable.setBounds(
|
||||
recyclerView.width - thumbExtra - thumbDrawable.intrinsicWidth + translateX,
|
||||
thumbY, recyclerView.width - thumbExtra + translateX, thumbY + thumbHeight
|
||||
)
|
||||
}
|
||||
thumbDrawable.draw(canvas)
|
||||
}
|
||||
|
||||
if (shouldInvalidate) {
|
||||
recyclerView.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
recyclerView.addOnScrollListener(scrollListener)
|
||||
recyclerView.addOnItemTouchListener(touchListener)
|
||||
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
|
||||
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) =
|
||||
handleDraw(c)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -2,26 +2,27 @@ package com.looker.droidify.widget
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
abstract class StableRecyclerAdapter<VT: Enum<VT>, VH: RecyclerView.ViewHolder>: EnumRecyclerAdapter<VT, VH>() {
|
||||
private var nextId = 1L
|
||||
private val descriptorToId = mutableMapOf<String, Long>()
|
||||
abstract class StableRecyclerAdapter<VT : Enum<VT>, VH : RecyclerView.ViewHolder> :
|
||||
EnumRecyclerAdapter<VT, VH>() {
|
||||
private var nextId = 1L
|
||||
private val descriptorToId = mutableMapOf<String, Long>()
|
||||
|
||||
init {
|
||||
super.setHasStableIds(true)
|
||||
}
|
||||
|
||||
final override fun setHasStableIds(hasStableIds: Boolean) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
val descriptor = getItemDescriptor(position)
|
||||
return descriptorToId[descriptor] ?: run {
|
||||
val id = nextId++
|
||||
descriptorToId[descriptor] = id
|
||||
id
|
||||
init {
|
||||
super.setHasStableIds(true)
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun getItemDescriptor(position: Int): String
|
||||
final override fun setHasStableIds(hasStableIds: Boolean) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
val descriptor = getItemDescriptor(position)
|
||||
return descriptorToId[descriptor] ?: run {
|
||||
val id = nextId++
|
||||
descriptorToId[descriptor] = id
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun getItemDescriptor(position: Int): String
|
||||
}
|
||||
|
@ -4,30 +4,35 @@ import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.Toolbar
|
||||
|
||||
class Toolbar: Toolbar {
|
||||
constructor(context: Context): super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?): super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int,
|
||||
defStyleRes: Int): super(context, attrs, defStyleAttr, defStyleRes)
|
||||
class Toolbar : Toolbar {
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
)
|
||||
|
||||
private var initalized = false
|
||||
private var layoutDirectionChanged: Int? = null
|
||||
constructor(
|
||||
context: Context, attrs: AttributeSet?, defStyleAttr: Int,
|
||||
defStyleRes: Int
|
||||
) : super(context, attrs, defStyleAttr, defStyleRes)
|
||||
|
||||
init {
|
||||
initalized = true
|
||||
val layoutDirection = layoutDirectionChanged
|
||||
layoutDirectionChanged = null
|
||||
if (layoutDirection != null) {
|
||||
onRtlPropertiesChanged(layoutDirection)
|
||||
private var initalized = false
|
||||
private var layoutDirectionChanged: Int? = null
|
||||
|
||||
init {
|
||||
initalized = true
|
||||
val layoutDirection = layoutDirectionChanged
|
||||
layoutDirectionChanged = null
|
||||
if (layoutDirection != null) onRtlPropertiesChanged(layoutDirection)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRtlPropertiesChanged(layoutDirection: Int) {
|
||||
if (initalized) {
|
||||
super.onRtlPropertiesChanged(layoutDirection)
|
||||
} else {
|
||||
layoutDirectionChanged = layoutDirection
|
||||
override fun onRtlPropertiesChanged(layoutDirection: Int) {
|
||||
if (initalized) {
|
||||
super.onRtlPropertiesChanged(layoutDirection)
|
||||
} else {
|
||||
layoutDirectionChanged = layoutDirection
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user