Last active
June 28, 2018 10:07
-
-
Save ch4vi/9d44933e10852a69b094970b296c09ce to your computer and use it in GitHub Desktop.
DotsIndicator written in Kotlin based on com.tbuonomo.viewpagerdotsindicator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <declare-styleable name="DotsIndicator"> | |
| <attr name="dotsColor"/> | |
| <attr name="dotsSize"/> | |
| <attr name="dotsWidthFactor"/> | |
| <attr name="dotsSpacing"/> | |
| <attr name="dotsCornerRadius"/> | |
| </declare-styleable> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| dependencies { | |
| // Anko | |
| implementation "org.jetbrains.anko:anko-appcompat-v7-commons:0.10.5" | |
| implementation "org.jetbrains.anko:anko-sdk19-listeners:0.10.5" | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?xml version="1.0" encoding="utf-8"?> | |
| <shape xmlns:android="http://schemas.android.com/apk/res/android" | |
| android:shape="rectangle"> | |
| <solid android:color="@android:color/white"/> | |
| <corners android:radius="0dp"/> | |
| </shape> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import android.content.Context | |
| import android.database.DataSetObserver | |
| import android.graphics.Color | |
| import android.graphics.drawable.GradientDrawable | |
| import android.support.v4.view.ViewPager | |
| import android.util.AttributeSet | |
| import android.util.Log | |
| import android.view.LayoutInflater | |
| import android.widget.FrameLayout | |
| import android.widget.ImageView | |
| import android.widget.LinearLayout | |
| import com.econocom.econocross.R | |
| import org.jetbrains.anko.dip | |
| import org.jetbrains.anko.find | |
| import org.jetbrains.anko.sdk19.listeners.onClick | |
| class DotsIndicator : LinearLayout { | |
| private object Const { | |
| const val DEFAULT_POINT_COLOR = Color.CYAN | |
| const val DEFAULT_WIDTH_FACTOR = 2.5f | |
| } | |
| private val dots: MutableList<ImageView> = mutableListOf() | |
| private var dotsSize = 0f | |
| private var dotsCornerRadius = 0f | |
| private var dotsSpacing = 0f | |
| private var dotsColor: Int = 0 | |
| private var dotsWidthFactor = 0f | |
| set(value) { | |
| if (value < 1) field = 2.5f | |
| field = value | |
| } | |
| private var viewPager: ViewPager? = null | |
| private var currentPage: Int = 0 | |
| private var dotsClickable: Boolean = false | |
| private var pageChangedListener: ViewPager.OnPageChangeListener? = null | |
| constructor(context: Context) : super(context) { | |
| init(null) | |
| } | |
| constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { | |
| init(attrs) | |
| } | |
| constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : | |
| super(context, attrs, defStyleAttr) { | |
| init(attrs) | |
| } | |
| private fun init(attrs: AttributeSet?) { | |
| orientation = LinearLayout.HORIZONTAL | |
| dotsSize = dip(16).toFloat() | |
| dotsSpacing = dip(4).toFloat() | |
| dotsCornerRadius = dotsSize / 2 | |
| dotsWidthFactor = Const.DEFAULT_WIDTH_FACTOR | |
| dotsColor = Const.DEFAULT_POINT_COLOR | |
| dotsClickable = true | |
| attrs?.let { | |
| val typedArray = context.obtainStyledAttributes(attrs, R.styleable.DotsIndicator) | |
| dotsWidthFactor = typedArray.getFloat(R.styleable.DotsIndicator_dotsWidthFactor, 2.5f) | |
| dotsSize = typedArray.getDimension(R.styleable.DotsIndicator_dotsSize, dotsSize) | |
| dotsCornerRadius = | |
| typedArray.getDimension(R.styleable.DotsIndicator_dotsCornerRadius, dotsSize / 2).toInt() | |
| .toFloat() | |
| dotsSpacing = typedArray.getDimension(R.styleable.DotsIndicator_dotsSpacing, dotsSpacing) | |
| dotsColor = | |
| typedArray.getColor(R.styleable.DotsIndicator_dotsColor, Const.DEFAULT_POINT_COLOR) | |
| setUpCircleColors(dotsColor) | |
| typedArray.recycle() | |
| } | |
| if (attrs == null) setUpCircleColors(Const.DEFAULT_POINT_COLOR) | |
| if (isInEditMode) { | |
| addDots(5) | |
| } | |
| } | |
| override fun onAttachedToWindow() { | |
| super.onAttachedToWindow() | |
| refreshDots() | |
| } | |
| private fun refreshDots() { | |
| viewPager?.adapter?.let { | |
| if (dots.size < it.count) addDots(it.count - dots.size) | |
| else if (dots.size > it.count) removeDots(dots.size - it.count) | |
| setUpDotsAnimators() | |
| } | |
| if (viewPager?.adapter == null) { | |
| Log.e(DotsIndicator::class.java.simpleName, | |
| "You have to set an adapter to the view pager before !") | |
| } | |
| } | |
| private fun addDots(count: Int) { | |
| for (i in 0 until count) { | |
| val dot = LayoutInflater.from(context).inflate(R.layout.view_indicator, this, false) | |
| val imageView = dot.find<ImageView>(R.id.dot) | |
| val params = imageView.layoutParams as FrameLayout.LayoutParams | |
| params.height = dotsSize.toInt() | |
| params.width = params.height | |
| params.setMargins(dotsSpacing.toInt(), 0, dotsSpacing.toInt(), 0) | |
| (imageView.background as GradientDrawable).cornerRadius = dotsCornerRadius | |
| (imageView.background as GradientDrawable).setColor(dotsColor) | |
| dot.onClick { | |
| viewPager?.adapter?.let { | |
| if (dotsClickable && i < it.count) viewPager?.setCurrentItem(i, true) | |
| } | |
| } | |
| dots.add(imageView) | |
| addView(dot) | |
| } | |
| } | |
| private fun removeDots(count: Int) { | |
| for (i in 0 until count) { | |
| removeViewAt(childCount - 1) | |
| dots.removeAt(dots.size - 1) | |
| } | |
| } | |
| private fun setUpDotsAnimators() { | |
| viewPager?.adapter?.let { | |
| if (it.count > 0) { | |
| if (currentPage < dots.size) { | |
| dots.getOrNull(currentPage)?.let { | |
| val params = it.layoutParams as FrameLayout.LayoutParams | |
| params.width = dotsSize.toInt() | |
| it.layoutParams = params | |
| } | |
| } | |
| viewPager?.currentItem?.let { | |
| if (it >= dots.size) { | |
| currentPage = dots.size - 1 | |
| viewPager?.setCurrentItem(currentPage, false) | |
| } | |
| dots.getOrNull(currentPage)?.let { | |
| val params = it.layoutParams as FrameLayout.LayoutParams | |
| params.width = (dotsSize * dotsWidthFactor).toInt() | |
| it.layoutParams = params | |
| } | |
| pageChangedListener?.let { | |
| viewPager?.removeOnPageChangeListener(it) | |
| } | |
| setUpOnPageChangedListener() | |
| pageChangedListener?.let { | |
| viewPager?.addOnPageChangeListener(it) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| private fun setUpOnPageChangedListener() { | |
| pageChangedListener = object : ViewPager.OnPageChangeListener { | |
| private var lastPage: Int = 0 | |
| override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { | |
| if (position != currentPage && positionOffset == 0f || currentPage < position) { | |
| setDotWidth(dots[currentPage], dotsSize.toInt()) | |
| currentPage = position | |
| } | |
| if (Math.abs(currentPage - position) > 1) { | |
| setDotWidth(dots[currentPage], dotsSize.toInt()) | |
| currentPage = lastPage | |
| } | |
| var dot = dots[currentPage] | |
| var nextDot: ImageView? = null | |
| if (currentPage == position && currentPage + 1 < dots.size) { | |
| nextDot = dots[currentPage + 1] | |
| } else if (currentPage > position) { | |
| nextDot = dot | |
| dot = dots[currentPage - 1] | |
| } | |
| val dotWidth = (dotsSize + dotsSize * (dotsWidthFactor - 1) * (1 - positionOffset)).toInt() | |
| setDotWidth(dot, dotWidth) | |
| if (nextDot != null) { | |
| val nextDotWidth = (dotsSize + dotsSize * (dotsWidthFactor - 1) * positionOffset).toInt() | |
| setDotWidth(nextDot, nextDotWidth) | |
| } | |
| lastPage = position | |
| } | |
| private fun setDotWidth(dot: ImageView, dotWidth: Int) { | |
| val dotParams = dot.layoutParams | |
| dotParams.width = dotWidth | |
| dot.layoutParams = dotParams | |
| } | |
| override fun onPageSelected(position: Int) {} | |
| override fun onPageScrollStateChanged(state: Int) {} | |
| } | |
| } | |
| private fun setUpCircleColors(color: Int) { | |
| for (elevationItem in dots) { | |
| (elevationItem.background as GradientDrawable).setColor(color) | |
| } | |
| } | |
| private fun setUpViewPager() { | |
| viewPager?.adapter?.registerDataSetObserver(object : DataSetObserver() { | |
| override fun onChanged() { | |
| super.onChanged() | |
| refreshDots() | |
| } | |
| }) | |
| } | |
| //********************************************************* | |
| // Users Methods | |
| //********************************************************* | |
| fun setPointsColor(color: Int) { | |
| setUpCircleColors(color) | |
| } | |
| fun setDotsClickable(dotsClickable: Boolean) { | |
| this.dotsClickable = dotsClickable | |
| } | |
| fun setViewPager(viewPager: ViewPager) { | |
| this.viewPager = viewPager | |
| setUpViewPager() | |
| refreshDots() | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <com.ch4vi.example.DotsIndicator | |
| android:id="@+id/dots_indicator" | |
| android:layout_width="wrap_content" | |
| android:layout_height="wrap_content" | |
| android:layout_marginEnd="8dp" | |
| android:layout_marginStart="8dp" | |
| app:dotsColor="@color/white" | |
| app:dotsCornerRadius="8dp" | |
| app:dotsSize="16dp" | |
| app:dotsSpacing="4dp" | |
| app:dotsWidthFactor="2.5" | |
| /> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| override fun onCreate(savedInstanceState: Bundle?) { | |
| super.onCreate(savedInstanceState) | |
| setContentView(R.layout.example_layout) | |
| if (savedInstanceState == null) { | |
| viewPager.adapter = PagerAdapter(supportFragmentManager) | |
| dotsIndicator.setViewPager(viewPager) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?xml version="1.0" encoding="utf-8"?> | |
| <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
| xmlns:app="http://schemas.android.com/apk/res-auto" | |
| xmlns:tools="http://schemas.android.com/tools" | |
| android:layout_width="match_parent" | |
| android:layout_height="match_parent" | |
| > | |
| <ImageView | |
| android:id="@+id/dot" | |
| android:layout_width="8dp" | |
| android:layout_height="8dp" | |
| android:contentDescription="@string/content_points" | |
| android:background="@drawable/dot_background" | |
| app:layout_constraintStart_toStartOf="parent" | |
| app:layout_constraintTop_toTopOf="parent" | |
| /> | |
| </FrameLayout> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment