@ -1,33 +0,0 @@ | |||||
package com.nivesh.production.niveshfd.fd.util.attacher | |||||
import com.nivesh.production.niveshfd.fd.util.dotIndicator.BaseDotsIndicator | |||||
internal abstract class DotsIndicatorAttacher<Attachable, Adapter> { | |||||
fun setup(baseDotsIndicator: BaseDotsIndicator, attachable: Attachable) { | |||||
val adapter = getAdapterFromAttachable(attachable) | |||||
?: throw IllegalStateException( | |||||
"Please set an adapter to the view pager (1 or 2) or the recycler before initializing the dots indicator" | |||||
) | |||||
registerAdapterDataChangedObserver(attachable, adapter) { | |||||
baseDotsIndicator.post { baseDotsIndicator.refreshDots() } | |||||
} | |||||
baseDotsIndicator.pager = buildPager(attachable, adapter) | |||||
baseDotsIndicator.refreshDots() | |||||
} | |||||
abstract fun getAdapterFromAttachable(attachable: Attachable): Adapter? | |||||
abstract fun registerAdapterDataChangedObserver( | |||||
attachable: Attachable, | |||||
adapter: Adapter, | |||||
onChanged: () -> Unit | |||||
) | |||||
abstract fun buildPager( | |||||
attachable: Attachable, | |||||
adapter: Adapter, | |||||
): BaseDotsIndicator.Pager | |||||
} | |||||
@ -1,66 +0,0 @@ | |||||
package com.nivesh.production.niveshfd.fd.util.attacher | |||||
import android.database.DataSetObserver | |||||
import androidx.viewpager.widget.PagerAdapter | |||||
import androidx.viewpager.widget.ViewPager | |||||
import com.nivesh.production.niveshfd.fd.util.dotIndicator.BaseDotsIndicator | |||||
import com.nivesh.production.niveshfd.fd.util.dotIndicator.OnPageChangeListenerHelper | |||||
import com.nivesh.production.niveshfd.fd.util.dotIndicator.isEmpty | |||||
internal class ViewPagerAttacher : | |||||
DotsIndicatorAttacher<ViewPager, PagerAdapter>() { | |||||
override fun getAdapterFromAttachable(attachable: ViewPager): PagerAdapter? = attachable.adapter | |||||
override fun registerAdapterDataChangedObserver( | |||||
attachable: ViewPager, | |||||
adapter: PagerAdapter, | |||||
onChanged: () -> Unit | |||||
) { | |||||
adapter.registerDataSetObserver(object : DataSetObserver() { | |||||
override fun onChanged() { | |||||
super.onChanged() | |||||
onChanged() | |||||
} | |||||
}) | |||||
} | |||||
override fun buildPager(attachable: ViewPager, adapter: PagerAdapter): BaseDotsIndicator.Pager { | |||||
return object : BaseDotsIndicator.Pager { | |||||
var onPageChangeListener: ViewPager.OnPageChangeListener? = null | |||||
override val isNotEmpty: Boolean get() = attachable.isEmpty | |||||
override val currentItem: Int get() = attachable.currentItem | |||||
override val isEmpty: Boolean get() = attachable.isEmpty | |||||
override val count: Int get() = attachable.adapter?.count ?: 0 | |||||
override fun setCurrentItem(item: Int, smoothScroll: Boolean) { | |||||
attachable.setCurrentItem(item, smoothScroll) | |||||
} | |||||
override fun removeOnPageChangeListener() { | |||||
onPageChangeListener?.let { attachable.removeOnPageChangeListener(it) } | |||||
} | |||||
override fun addOnPageChangeListener( | |||||
onPageChangeListenerHelper: | |||||
OnPageChangeListenerHelper | |||||
) { | |||||
onPageChangeListener = object : ViewPager.OnPageChangeListener { | |||||
override fun onPageScrolled( | |||||
position: Int, positionOffset: Float, | |||||
positionOffsetPixels: Int | |||||
) { | |||||
onPageChangeListenerHelper.onPageScrolled(position, positionOffset) | |||||
} | |||||
override fun onPageScrollStateChanged(state: Int) { | |||||
} | |||||
override fun onPageSelected(position: Int) { | |||||
} | |||||
} | |||||
attachable.addOnPageChangeListener(onPageChangeListener!!) | |||||
} | |||||
} | |||||
} | |||||
} |
@ -1,227 +0,0 @@ | |||||
package com.nivesh.production.niveshfd.fd.util.dotIndicator | |||||
import android.content.Context | |||||
import android.graphics.Color | |||||
import android.os.Build | |||||
import android.os.Parcelable | |||||
import android.util.AttributeSet | |||||
import android.view.View | |||||
import android.widget.FrameLayout | |||||
import android.widget.ImageView | |||||
import androidx.annotation.StyleableRes | |||||
import androidx.viewpager.widget.ViewPager | |||||
import androidx.viewpager2.widget.ViewPager2 | |||||
import com.nivesh.production.niveshfd.R | |||||
import com.nivesh.production.niveshfd.fd.util.attacher.ViewPagerAttacher | |||||
abstract class BaseDotsIndicator @JvmOverloads constructor( | |||||
context: Context, | |||||
attrs: AttributeSet? = null, | |||||
defStyleAttr: Int = 0 | |||||
) : FrameLayout(context, attrs, defStyleAttr) { | |||||
companion object { | |||||
const val DEFAULT_POINT_COLOR = Color.CYAN | |||||
} | |||||
enum class Type( | |||||
val defaultSize: Float, | |||||
val defaultSpacing: Float, | |||||
@StyleableRes val styleableId: IntArray, | |||||
@StyleableRes val dotsColorId: Int, | |||||
@StyleableRes val dotsSizeId: Int, | |||||
@StyleableRes val dotsSpacingId: Int, | |||||
@StyleableRes val dotsCornerRadiusId: Int, | |||||
@StyleableRes val dotsClickableId: Int | |||||
) { | |||||
DEFAULT( | |||||
16f, | |||||
8f, | |||||
R.styleable.SpringDotsIndicator, | |||||
R.styleable.SpringDotsIndicator_dotsColor, | |||||
R.styleable.SpringDotsIndicator_dotsSize, | |||||
R.styleable.SpringDotsIndicator_dotsSpacing, | |||||
R.styleable.SpringDotsIndicator_dotsCornerRadius, | |||||
R.styleable.SpringDotsIndicator_dotsClickable | |||||
), | |||||
SPRING( | |||||
16f, | |||||
4f, | |||||
R.styleable.DotsIndicator, | |||||
R.styleable.DotsIndicator_dotsColor, | |||||
R.styleable.DotsIndicator_dotsSize, | |||||
R.styleable.DotsIndicator_dotsSpacing, | |||||
R.styleable.DotsIndicator_dotsCornerRadius, | |||||
R.styleable.SpringDotsIndicator_dotsClickable | |||||
), | |||||
WORM( | |||||
16f, | |||||
4f, | |||||
R.styleable.WormDotsIndicator, | |||||
R.styleable.WormDotsIndicator_dotsColor, | |||||
R.styleable.WormDotsIndicator_dotsSize, | |||||
R.styleable.WormDotsIndicator_dotsSpacing, | |||||
R.styleable.WormDotsIndicator_dotsCornerRadius, | |||||
R.styleable.SpringDotsIndicator_dotsClickable | |||||
) | |||||
} | |||||
@JvmField | |||||
protected val dots = ArrayList<ImageView>() | |||||
var dotsClickable: Boolean = true | |||||
var dotsColor: Int = DEFAULT_POINT_COLOR | |||||
set(value) { | |||||
field = value | |||||
refreshDotsColors() | |||||
} | |||||
protected var dotsSize = dpToPxF(type.defaultSize) | |||||
protected var dotsCornerRadius = dotsSize / 2f | |||||
protected var dotsSpacing = dpToPxF(type.defaultSpacing) | |||||
init { | |||||
if (attrs != null) { | |||||
val a = context.obtainStyledAttributes(attrs, type.styleableId) | |||||
dotsColor = a.getColor(type.dotsColorId, DEFAULT_POINT_COLOR) | |||||
dotsSize = a.getDimension(type.dotsSizeId, dotsSize) | |||||
dotsCornerRadius = a.getDimension(type.dotsCornerRadiusId, dotsCornerRadius) | |||||
dotsSpacing = a.getDimension(type.dotsSpacingId, dotsSpacing) | |||||
dotsClickable = a.getBoolean(type.dotsClickableId, true) | |||||
a.recycle() | |||||
} | |||||
} | |||||
var pager: Pager? = null | |||||
interface Pager { | |||||
val isNotEmpty: Boolean | |||||
val currentItem: Int | |||||
val isEmpty: Boolean | |||||
val count: Int | |||||
fun setCurrentItem(item: Int, smoothScroll: Boolean) | |||||
fun removeOnPageChangeListener() | |||||
fun addOnPageChangeListener(onPageChangeListenerHelper: OnPageChangeListenerHelper) | |||||
} | |||||
override fun onAttachedToWindow() { | |||||
super.onAttachedToWindow() | |||||
post { refreshDots() } | |||||
} | |||||
private fun refreshDotsCount() { | |||||
if (dots.size < pager!!.count) { | |||||
addDots(pager!!.count - dots.size) | |||||
} else if (dots.size > pager!!.count) { | |||||
removeDots(dots.size - pager!!.count) | |||||
} | |||||
} | |||||
protected fun refreshDotsColors() { | |||||
for (i in dots.indices) { | |||||
refreshDotColor(i) | |||||
} | |||||
} | |||||
protected fun dpToPx(dp: Int): Int { | |||||
return (context.resources.displayMetrics.density * dp).toInt() | |||||
} | |||||
protected fun dpToPxF(dp: Float): Float { | |||||
return context.resources.displayMetrics.density * dp | |||||
} | |||||
protected fun addDots(count: Int) { | |||||
for (i in 0 until count) { | |||||
addDot(i) | |||||
} | |||||
} | |||||
private fun removeDots(count: Int) { | |||||
for (i in 0 until count) { | |||||
removeDot() | |||||
} | |||||
} | |||||
fun refreshDots() { | |||||
if (pager == null) { | |||||
return | |||||
} | |||||
post { | |||||
// Check if we need to refresh the dots count | |||||
refreshDotsCount() | |||||
refreshDotsColors() | |||||
refreshDotsSize() | |||||
refreshOnPageChangedListener() | |||||
} | |||||
} | |||||
private fun refreshOnPageChangedListener() { | |||||
if (pager!!.isNotEmpty) { | |||||
pager!!.removeOnPageChangeListener() | |||||
val onPageChangeListenerHelper = buildOnPageChangedListener() | |||||
pager!!.addOnPageChangeListener(onPageChangeListenerHelper) | |||||
onPageChangeListenerHelper.onPageScrolled(pager!!.currentItem, 0f) | |||||
} | |||||
} | |||||
private fun refreshDotsSize() { | |||||
dots.forEach { it.setWidth(dotsSize.toInt()) } | |||||
} | |||||
// ABSTRACT METHODS AND FIELDS | |||||
abstract fun refreshDotColor(index: Int) | |||||
abstract fun addDot(index: Int) | |||||
abstract fun removeDot() | |||||
abstract fun buildOnPageChangedListener(): OnPageChangeListenerHelper | |||||
abstract val type: Type | |||||
// PUBLIC METHODS | |||||
@Deprecated( | |||||
"Use setDotsColors(color) instead", ReplaceWith("setDotsColors(color)") | |||||
) | |||||
fun setPointsColor(color: Int) { | |||||
dotsColor = color | |||||
refreshDotsColors() | |||||
} | |||||
@Deprecated( | |||||
"Use attachTo(viewPager) instead", ReplaceWith("attachTo(viewPager)") | |||||
) | |||||
fun setViewPager(viewPager: ViewPager) { | |||||
ViewPagerAttacher().setup(this, viewPager) | |||||
} | |||||
@Deprecated( | |||||
"Use attachTo(viewPager) instead", ReplaceWith("attachTo(viewPager)") | |||||
) | |||||
// fun setViewPager2(viewPager2: ViewPager2) { | |||||
// ViewPager2Attacher().setup(this, viewPager2) | |||||
// } | |||||
fun attachTo(viewPager: ViewPager) { | |||||
ViewPagerAttacher().setup(this, viewPager) | |||||
} | |||||
// fun attachTo(viewPager2: ViewPager2) { | |||||
// ViewPager2Attacher().setup(this, viewPager2) | |||||
// } | |||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { | |||||
super.onLayout(changed, left, top, right, bottom) | |||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && layoutDirection == View.LAYOUT_DIRECTION_RTL) { | |||||
layoutDirection = View.LAYOUT_DIRECTION_LTR | |||||
rotation = 180f | |||||
requestLayout() | |||||
} | |||||
} | |||||
override fun onRestoreInstanceState(state: Parcelable?) { | |||||
super.onRestoreInstanceState(state) | |||||
post { refreshDots() } | |||||
} | |||||
} |
@ -1,13 +0,0 @@ | |||||
package com.nivesh.production.niveshfd.fd.util.dotIndicator | |||||
import android.graphics.drawable.GradientDrawable | |||||
class DotsGradientDrawable : GradientDrawable() { | |||||
var currentColor: Int = 0 | |||||
private set | |||||
override fun setColor(argb: Int) { | |||||
super.setColor(argb) | |||||
currentColor = argb | |||||
} | |||||
} |
@ -1,205 +0,0 @@ | |||||
package com.nivesh.production.niveshfd.fd.util.dotIndicator | |||||
import android.animation.ArgbEvaluator | |||||
import android.content.Context | |||||
import android.os.Build | |||||
import android.util.AttributeSet | |||||
import android.util.Log | |||||
import android.view.LayoutInflater | |||||
import android.view.View | |||||
import android.view.ViewGroup | |||||
import android.widget.ImageView | |||||
import android.widget.LinearLayout | |||||
import com.nivesh.production.niveshfd.R | |||||
class DotsIndicator @JvmOverloads constructor( | |||||
context: Context, attrs: AttributeSet? = null, | |||||
defStyleAttr: Int = 0 | |||||
) : BaseDotsIndicator(context, attrs, defStyleAttr) { | |||||
companion object { | |||||
const val DEFAULT_WIDTH_FACTOR = 2.5f | |||||
} | |||||
private lateinit var linearLayout: LinearLayout | |||||
private var dotsWidthFactor: Float = 0f | |||||
private var progressMode: Boolean = false | |||||
private var dotsElevation: Float = 0f | |||||
var selectedDotColor: Int = 0 | |||||
set(value) { | |||||
field = value | |||||
refreshDotsColors() | |||||
} | |||||
private val argbEvaluator = ArgbEvaluator() | |||||
init { | |||||
init(attrs) | |||||
} | |||||
private fun init(attrs: AttributeSet?) { | |||||
linearLayout = LinearLayout(context) | |||||
linearLayout.orientation = LinearLayout.HORIZONTAL | |||||
addView(linearLayout, | |||||
ViewGroup.LayoutParams.WRAP_CONTENT, | |||||
ViewGroup.LayoutParams.WRAP_CONTENT | |||||
) | |||||
dotsWidthFactor = DEFAULT_WIDTH_FACTOR | |||||
if (attrs != null) { | |||||
val a = context.obtainStyledAttributes(attrs, R.styleable.DotsIndicator) | |||||
selectedDotColor = | |||||
a.getColor(R.styleable.DotsIndicator_selectedDotColor, DEFAULT_POINT_COLOR) | |||||
dotsWidthFactor = a.getFloat(R.styleable.DotsIndicator_dotsWidthFactor, 2.5f) | |||||
if (dotsWidthFactor < 1) { | |||||
Log.w( | |||||
"DotsIndicator", | |||||
"The dotsWidthFactor can't be set under 1.0f, please set an higher value" | |||||
) | |||||
dotsWidthFactor = 1f | |||||
} | |||||
progressMode = a.getBoolean(R.styleable.DotsIndicator_progressMode, false) | |||||
dotsElevation = a.getDimension(R.styleable.DotsIndicator_dotsElevation, 0f) | |||||
a.recycle() | |||||
} | |||||
if (isInEditMode) { | |||||
addDots(5) | |||||
refreshDots() | |||||
} | |||||
} | |||||
override fun addDot(index: Int) { | |||||
val dot = LayoutInflater.from(context).inflate(R.layout.dot_layout, this, false) | |||||
val imageView = dot.findViewById<ImageView>(R.id.dot) | |||||
val params = imageView.layoutParams as LayoutParams | |||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { | |||||
dot.layoutDirection = View.LAYOUT_DIRECTION_LTR | |||||
} | |||||
params.height = dotsSize.toInt() | |||||
params.width = params.height | |||||
params.setMargins(dotsSpacing.toInt(), 0, dotsSpacing.toInt(), 0) | |||||
val background = DotsGradientDrawable() | |||||
background.cornerRadius = dotsCornerRadius | |||||
if (isInEditMode) { | |||||
background.setColor(if (0 == index) selectedDotColor else dotsColor) | |||||
} else { | |||||
background.setColor(if (pager!!.currentItem == index) selectedDotColor else dotsColor) | |||||
} | |||||
imageView.setBackgroundCompat(background) | |||||
dot.setOnClickListener { | |||||
if (dotsClickable && index < pager?.count ?: 0) { | |||||
pager!!.setCurrentItem(index, true) | |||||
} | |||||
} | |||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | |||||
dot.setPaddingHorizontal((dotsElevation * 0.8f).toInt()) | |||||
dot.setPaddingVertical((dotsElevation * 2).toInt()) | |||||
imageView.elevation = dotsElevation | |||||
} | |||||
dots.add(imageView) | |||||
linearLayout.addView(dot) | |||||
} | |||||
override fun removeDot() { | |||||
linearLayout.removeViewAt(linearLayout.childCount - 1) | |||||
dots.removeAt(dots.size - 1) | |||||
} | |||||
override fun buildOnPageChangedListener(): OnPageChangeListenerHelper { | |||||
return object : OnPageChangeListenerHelper() { | |||||
override fun onPageScrolled( | |||||
selectedPosition: Int, | |||||
nextPosition: Int, | |||||
positionOffset: Float | |||||
) { | |||||
val selectedDot = dots[selectedPosition] | |||||
// Selected dot | |||||
val selectedDotWidth = | |||||
(dotsSize + dotsSize * (dotsWidthFactor - 1) * (1 - positionOffset)).toInt() | |||||
selectedDot.setWidth(selectedDotWidth) | |||||
if (dots.isInBounds(nextPosition)) { | |||||
val nextDot = dots[nextPosition] | |||||
val nextDotWidth = | |||||
(dotsSize + dotsSize * (dotsWidthFactor - 1) * positionOffset).toInt() | |||||
nextDot.setWidth(nextDotWidth) | |||||
val selectedDotBackground = selectedDot.background as DotsGradientDrawable | |||||
val nextDotBackground = nextDot.background as DotsGradientDrawable | |||||
if (selectedDotColor != dotsColor) { | |||||
val selectedColor = argbEvaluator.evaluate( | |||||
positionOffset, selectedDotColor, | |||||
dotsColor | |||||
) as Int | |||||
val nextColor = argbEvaluator.evaluate( | |||||
positionOffset, dotsColor, | |||||
selectedDotColor | |||||
) as Int | |||||
nextDotBackground.setColor(nextColor) | |||||
if (progressMode && selectedPosition <= pager!!.currentItem) { | |||||
selectedDotBackground.setColor(selectedDotColor) | |||||
} else { | |||||
selectedDotBackground.setColor(selectedColor) | |||||
} | |||||
} | |||||
} | |||||
invalidate() | |||||
} | |||||
override fun resetPosition(position: Int) { | |||||
dots[position].setWidth(dotsSize.toInt()) | |||||
refreshDotColor(position) | |||||
} | |||||
override val pageCount: Int | |||||
get() = dots.size | |||||
} | |||||
} | |||||
override fun refreshDotColor(index: Int) { | |||||
val elevationItem = dots[index] | |||||
val background = elevationItem.background as? DotsGradientDrawable? | |||||
background?.let { | |||||
if (index == pager!!.currentItem || progressMode && index < pager!!.currentItem) { | |||||
background.setColor(selectedDotColor) | |||||
} else { | |||||
background.setColor(dotsColor) | |||||
} | |||||
} | |||||
elevationItem.setBackgroundCompat(background) | |||||
elevationItem.invalidate() | |||||
} | |||||
override val type get() = Type.DEFAULT | |||||
//********************************************************* | |||||
// Users Methods | |||||
//********************************************************* | |||||
@Deprecated("Use setSelectedDotColor() instead", ReplaceWith("setSelectedDotColor()")) | |||||
fun setSelectedPointColor(color: Int) { | |||||
selectedDotColor = color | |||||
} | |||||
} |
@ -1,46 +0,0 @@ | |||||
package com.nivesh.production.niveshfd.fd.util.dotIndicator | |||||
import android.content.Context | |||||
import android.graphics.drawable.Drawable | |||||
import android.os.Build | |||||
import android.util.TypedValue | |||||
import android.view.View | |||||
import androidx.viewpager.widget.ViewPager | |||||
import androidx.viewpager2.widget.ViewPager2 | |||||
import com.nivesh.production.niveshfd.R | |||||
internal fun View.setPaddingHorizontal(padding: Int) { | |||||
setPadding(padding, paddingTop, padding, paddingBottom) | |||||
} | |||||
internal fun View.setPaddingVertical(padding: Int) { | |||||
setPadding(paddingLeft, padding, paddingRight, padding) | |||||
} | |||||
internal fun View.setWidth(width: Int) { | |||||
layoutParams.apply { | |||||
this.width = width | |||||
requestLayout() | |||||
} | |||||
} | |||||
internal fun <T> ArrayList<T>.isInBounds(index: Int) = index in 0 until size | |||||
internal fun Context.getThemePrimaryColor(): Int { | |||||
val value = TypedValue() | |||||
this.theme.resolveAttribute(androidx.transition.R.attr.colorPrimary, value, true) | |||||
return value.data | |||||
} | |||||
internal val ViewPager.isNotEmpty: Boolean get() = (adapter?.count ?: 0) > 0 | |||||
internal val ViewPager2.isNotEmpty: Boolean get() = (adapter?.itemCount ?: 0) > 0 | |||||
internal val ViewPager?.isEmpty: Boolean get() = this?.adapter?.count == 0 | |||||
internal val ViewPager2?.isEmpty: Boolean get() = this?.adapter?.itemCount == 0 | |||||
fun View.setBackgroundCompat(background: Drawable?) { | |||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { | |||||
this.background = background | |||||
} else { | |||||
setBackgroundDrawable(background) | |||||
} | |||||
} |
@ -1,49 +0,0 @@ | |||||
package com.nivesh.production.niveshfd.fd.util.dotIndicator | |||||
abstract class OnPageChangeListenerHelper { | |||||
private var lastLeftPosition: Int = -1 | |||||
private var lastRightPosition: Int = -1 | |||||
internal abstract val pageCount: Int | |||||
fun onPageScrolled(position: Int, positionOffset: Float) { | |||||
var offset = (position + positionOffset) | |||||
val lastPageIndex = (pageCount - 1).toFloat() | |||||
if (offset == lastPageIndex) { | |||||
offset = lastPageIndex - .0001f | |||||
} | |||||
val leftPosition = offset.toInt() | |||||
val rightPosition = leftPosition + 1 | |||||
if (rightPosition > lastPageIndex || leftPosition < 0) { | |||||
return | |||||
} | |||||
onPageScrolled(leftPosition, rightPosition, offset % 1) | |||||
if (lastLeftPosition != -1) { | |||||
if (leftPosition > lastLeftPosition) { | |||||
(lastLeftPosition until leftPosition).forEach { | |||||
resetPosition(it) | |||||
} | |||||
} | |||||
if (rightPosition < lastRightPosition) { | |||||
resetPosition(lastRightPosition) | |||||
((rightPosition + 1)..lastRightPosition).forEach { | |||||
resetPosition(it) | |||||
} | |||||
} | |||||
} | |||||
lastLeftPosition = leftPosition | |||||
lastRightPosition = rightPosition | |||||
} | |||||
internal abstract fun onPageScrolled( | |||||
selectedPosition: Int, nextPosition: Int, | |||||
positionOffset: Float | |||||
) | |||||
internal abstract fun resetPosition(position: Int) | |||||
} |
Powered by TurnKey Linux.