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