Update recommendations item in explore section
parent
018c84b6af
commit
7f5ff1ab14
@ -0,0 +1,143 @@
|
|||||||
|
package org.koitharu.kotatsu.core.ui.widgets
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.content.withStyledAttributes
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
|
||||||
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.measureDimension
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.resolveDp
|
||||||
|
import org.koitharu.kotatsu.parsers.util.toIntUp
|
||||||
|
|
||||||
|
class DotsIndicator @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0,
|
||||||
|
) : View(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||||
|
private var indicatorSize = context.resources.resolveDp(12f)
|
||||||
|
private var dotSpacing = 0f
|
||||||
|
private var positionOffset: Float = 0f
|
||||||
|
var max: Int = 6
|
||||||
|
set(value) {
|
||||||
|
if (field != value) {
|
||||||
|
field = value
|
||||||
|
requestLayout()
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var position: Int = 2
|
||||||
|
set(value) {
|
||||||
|
if (field != value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
paint.strokeWidth = context.resources.resolveDp(1.5f)
|
||||||
|
context.withStyledAttributes(attrs, R.styleable.DotsIndicator, defStyleAttr) {
|
||||||
|
paint.color = getColor(
|
||||||
|
R.styleable.DotsIndicator_dotColor,
|
||||||
|
context.getThemeColor(com.google.android.material.R.attr.colorPrimary, Color.DKGRAY),
|
||||||
|
)
|
||||||
|
indicatorSize = getDimension(R.styleable.DotsIndicator_dotSize, indicatorSize)
|
||||||
|
dotSpacing = getDimension(R.styleable.DotsIndicator_dotSpacing, dotSpacing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
val dotSize = getDotSize()
|
||||||
|
val y = paddingTop + (height - paddingTop - paddingBottom) / 2f
|
||||||
|
var x = paddingLeft + dotSize / 2f
|
||||||
|
val radius = dotSize / 2f - paint.strokeWidth
|
||||||
|
val spacing = (width - paddingLeft - paddingRight) / max.toFloat() - dotSize
|
||||||
|
x += spacing / 2f
|
||||||
|
paint.style = Paint.Style.STROKE
|
||||||
|
for (i in 0 until max) {
|
||||||
|
canvas.drawCircle(x, y, radius, paint)
|
||||||
|
if (i == position) {
|
||||||
|
paint.style = Paint.Style.FILL
|
||||||
|
paint.alpha = (255 * (1f - positionOffset)).toInt()
|
||||||
|
canvas.drawCircle(x, y, radius, paint)
|
||||||
|
paint.alpha = 255
|
||||||
|
paint.style = Paint.Style.STROKE
|
||||||
|
}
|
||||||
|
if (i == position + 1 && positionOffset > 0f) {
|
||||||
|
paint.style = Paint.Style.FILL
|
||||||
|
paint.alpha = (255 * positionOffset).toInt()
|
||||||
|
canvas.drawCircle(x, y, radius, paint)
|
||||||
|
paint.alpha = 255
|
||||||
|
paint.style = Paint.Style.STROKE
|
||||||
|
}
|
||||||
|
x += spacing + dotSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
val dotSize = getDotSize()
|
||||||
|
val desiredHeight = (dotSize + paddingTop + paddingBottom).toIntUp()
|
||||||
|
val desiredWidth = ((dotSize + dotSpacing) * max).toIntUp() + paddingLeft + paddingRight
|
||||||
|
setMeasuredDimension(
|
||||||
|
measureDimension(desiredWidth, widthMeasureSpec),
|
||||||
|
measureDimension(desiredHeight, heightMeasureSpec),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bindToViewPager(pager: ViewPager2) {
|
||||||
|
pager.registerOnPageChangeCallback(ViewPagerCallback())
|
||||||
|
pager.adapter?.let {
|
||||||
|
it.registerAdapterDataObserver(AdapterObserver(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDotSize() = if (indicatorSize <= 0) {
|
||||||
|
(height - paddingTop - paddingBottom).toFloat()
|
||||||
|
} else {
|
||||||
|
indicatorSize
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ViewPagerCallback : ViewPager2.OnPageChangeCallback() {
|
||||||
|
|
||||||
|
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||||
|
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
|
||||||
|
this@DotsIndicator.position = position
|
||||||
|
this@DotsIndicator.positionOffset = positionOffset
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPageSelected(position: Int) {
|
||||||
|
super.onPageSelected(position)
|
||||||
|
this@DotsIndicator.position = position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class AdapterObserver(
|
||||||
|
private val adapter: RecyclerView.Adapter<*>,
|
||||||
|
) : AdapterDataObserver() {
|
||||||
|
|
||||||
|
override fun onChanged() {
|
||||||
|
super.onChanged()
|
||||||
|
max = adapter.itemCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||||
|
super.onItemRangeInserted(positionStart, itemCount)
|
||||||
|
max = adapter.itemCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||||
|
super.onItemRangeRemoved(positionStart, itemCount)
|
||||||
|
max = adapter.itemCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,70 +1,24 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/list_selector"
|
android:orientation="vertical">
|
||||||
android:clipChildren="false">
|
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:id="@+id/imageView_cover"
|
android:id="@+id/pager"
|
||||||
android:layout_width="40dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="40dp"
|
android:layout_height="@dimen/recommendation_item_height" />
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover.Small"
|
|
||||||
tools:src="@tools:sample/backgrounds/scenic" />
|
|
||||||
|
|
||||||
<TextView
|
<org.koitharu.kotatsu.core.ui.widgets.DotsIndicator
|
||||||
android:id="@+id/textView_title"
|
android:id="@+id/dots"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_gravity="center_horizontal"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginVertical="@dimen/margin_small"
|
||||||
android:ellipsize="end"
|
app:dotSize="8dp"
|
||||||
android:maxLines="1"
|
app:dotSpacing="6dp" />
|
||||||
android:textAppearance="?attr/textAppearanceBodyLarge"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/textView_subtitle"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/imageView_cover"
|
|
||||||
tools:text="@tools:sample/lorem" />
|
|
||||||
|
|
||||||
<TextView
|
</LinearLayout>
|
||||||
android:id="@+id/textView_subtitle"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/imageView_cover"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/textView_title"
|
|
||||||
tools:text="@tools:sample/lorem/random" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_more"
|
|
||||||
style="@style/Widget.Kotatsu.ExploreButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/more"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/textView_subtitle" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue