@ -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.