payloads) {
+ return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
+ }
+
+
+ /**
+ * 设置item添加动画初始化状态(比如默认透明度为0)
+ *
+ * @param holder 添加的ViewHolder
+ */
+ public abstract void setAddItemAnimationInit(ViewHolder holder);
+
+ /**
+ * 设置item添加动画
+ *
+ * @param holder 添加的ViewHolder
+ * @param animator 添加的ViewHolder对应动画对象
+ */
+ public abstract void setAddItemAnimation(ViewHolder holder, ViewPropertyAnimator animator);
+
+ /**
+ * 设置取消添加item动画,还原状态以复用
+ *
+ * @param holder 添加的ViewHolder
+ */
+ public abstract void setAddItemAnimationCancel(ViewHolder holder);
+
+
+ /**
+ * 设置item移除动画
+ *
+ * @param holder 添加的ViewHolder
+ * @param animator 添加的ViewHolder对应动画对象
+ */
+ public abstract void setRemoveAnimation(ViewHolder holder, ViewPropertyAnimator animator);
+
+
+ /**
+ * 设置结束移除item动画,还原状态以复用
+ *
+ * @param holder 添加的ViewHolder
+ */
+ public abstract void setRemoveAnimationEnd(ViewHolder holder);
+
+
+ public abstract void setOldChangeAnimation(ViewHolder holder, ViewPropertyAnimator animator);
+
+ public abstract void setOldChangeAnimationEnd(ViewHolder holder);
+
+ public abstract void setNewChangeAnimationInit(ViewHolder holder);
+
+ public abstract void setNewChangeAnimation(ViewHolder holder, ViewPropertyAnimator animator);
+
+ public abstract void setNewChangeAnimationEnd(ViewHolder holder);
+
+
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/anim/RotateXItemAnimation.kt b/app/src/main/java/com/allen/androidcustomview/anim/RotateXItemAnimation.kt
new file mode 100644
index 0000000..91a1d75
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/anim/RotateXItemAnimation.kt
@@ -0,0 +1,41 @@
+package com.allen.androidcustomview.anim
+
+import android.support.v7.widget.RecyclerView
+import android.view.ViewPropertyAnimator
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2019/05/15
+ * desc :
+ *
+ */
+class RotateXItemAnimation(animDuration: Long = 500) : SuperItemAnimation(animDuration) {
+
+ init {
+ addDuration = animDuration
+ removeDuration = animDuration
+ }
+
+ override fun setAddItemAnimInit(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.rotationX = -90f
+ }
+
+ override fun setAddItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ animator?.rotationX(0f)
+ }
+
+ override fun setAddItemAnimCancel(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.rotationX = 0f
+ }
+
+ override fun setRemoveItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ animator?.rotationX(-90f)
+ }
+
+ override fun setRemoveItemAnimEnd(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.rotationX = 0f
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/anim/RotateYItemAnimation.kt b/app/src/main/java/com/allen/androidcustomview/anim/RotateYItemAnimation.kt
new file mode 100644
index 0000000..429400a
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/anim/RotateYItemAnimation.kt
@@ -0,0 +1,36 @@
+package com.allen.androidcustomview.anim
+
+import android.support.v7.widget.RecyclerView
+import android.view.ViewPropertyAnimator
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2019/05/15
+ * desc :
+ *
+ */
+class RotateYItemAnimation(animDuration: Long = 500) : SuperItemAnimation(animDuration) {
+
+ override fun setAddItemAnimInit(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.rotationY = -90f
+ }
+
+ override fun setAddItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ animator?.rotationY(0f)
+ }
+
+ override fun setAddItemAnimCancel(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.rotationY = 0f
+ }
+
+ override fun setRemoveItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ animator?.rotationY(-90f)
+ }
+
+ override fun setRemoveItemAnimEnd(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.rotationY = 0f
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/anim/ScaleItemAnimation.kt b/app/src/main/java/com/allen/androidcustomview/anim/ScaleItemAnimation.kt
new file mode 100644
index 0000000..dcf47ef
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/anim/ScaleItemAnimation.kt
@@ -0,0 +1,43 @@
+package com.allen.androidcustomview.anim
+
+import android.support.v7.widget.RecyclerView
+import android.view.ViewPropertyAnimator
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2019/05/15
+ * desc :
+ *
+ */
+class ScaleItemAnimation(animDuration: Long = 500) : SuperItemAnimation(animDuration) {
+
+ override fun setAddItemAnimInit(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.scaleX = 0f
+ holder?.itemView?.scaleY = 0f
+ }
+
+ override fun setAddItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ holder?.itemView?.pivotX = 0f
+ holder?.itemView?.pivotY = 0f
+ animator?.scaleX(1f)?.scaleY(1f)
+ }
+
+ override fun setAddItemAnimCancel(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.scaleX = 1f
+ holder?.itemView?.scaleY = 1f
+ }
+
+ override fun setRemoveItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ holder?.itemView?.pivotX = 1f
+ holder?.itemView?.pivotY = 1f
+ animator?.scaleX(0f)?.scaleY(0f)
+ }
+
+ override fun setRemoveItemAnimEnd(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.scaleX = 1f
+ holder?.itemView?.scaleY = 1f
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/anim/SlideItemAnimation.kt b/app/src/main/java/com/allen/androidcustomview/anim/SlideItemAnimation.kt
new file mode 100644
index 0000000..ee63641
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/anim/SlideItemAnimation.kt
@@ -0,0 +1,38 @@
+package com.allen.androidcustomview.anim
+
+import android.support.v7.widget.RecyclerView
+import android.view.ViewPropertyAnimator
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2019/05/15
+ * desc :
+ *
+ */
+class SlideItemAnimation(animDuration: Long = 500) : SuperItemAnimation(animDuration) {
+
+ override fun setAddItemAnimInit(holder: RecyclerView.ViewHolder?) {
+ val with = holder?.itemView?.width ?: 0
+ holder?.itemView?.translationX = -with.toFloat()
+ }
+
+ override fun setAddItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ animator?.translationX(0f)
+ }
+
+ override fun setAddItemAnimCancel(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.translationX = 0f
+ }
+
+ override fun setRemoveItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ val with = holder?.itemView?.width ?: 0
+ animator?.translationX(-with.toFloat())
+ }
+
+ override fun setRemoveItemAnimEnd(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.translationX = 0f
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/anim/SuperItemAnimation.kt b/app/src/main/java/com/allen/androidcustomview/anim/SuperItemAnimation.kt
new file mode 100644
index 0000000..a177c98
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/anim/SuperItemAnimation.kt
@@ -0,0 +1,69 @@
+package com.allen.androidcustomview.anim
+
+import android.support.v7.widget.RecyclerView
+import android.view.ViewPropertyAnimator
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2019/05/15
+ * desc :
+ *
+ */
+abstract class SuperItemAnimation(animDuration: Long = 200) : BaseItemAnimation() {
+
+ init {
+ addDuration = animDuration
+ removeDuration = animDuration
+ }
+
+ abstract fun setAddItemAnimInit(holder: RecyclerView.ViewHolder?)
+ abstract fun setAddItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?)
+ abstract fun setAddItemAnimCancel(holder: RecyclerView.ViewHolder?)
+
+ abstract fun setRemoveItemAnim(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?)
+ abstract fun setRemoveItemAnimEnd(holder: RecyclerView.ViewHolder?)
+
+
+ override fun setAddItemAnimationInit(holder: RecyclerView.ViewHolder?) {
+ setAddItemAnimInit(holder)
+ }
+
+ override fun setAddItemAnimation(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ setAddItemAnim(holder, animator)
+ }
+
+ override fun setAddItemAnimationCancel(holder: RecyclerView.ViewHolder?) {
+ setAddItemAnimCancel(holder)
+ }
+
+ override fun setRemoveAnimation(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ setRemoveItemAnim(holder, animator)
+ }
+
+ override fun setRemoveAnimationEnd(holder: RecyclerView.ViewHolder?) {
+ setRemoveItemAnimEnd(holder)
+ }
+
+
+ override fun setOldChangeAnimation(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ animator?.alpha(0f)
+ }
+
+ override fun setOldChangeAnimationEnd(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.alpha = 1f
+ }
+
+ override fun setNewChangeAnimationInit(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.alpha = 0f
+ }
+
+ override fun setNewChangeAnimation(holder: RecyclerView.ViewHolder?, animator: ViewPropertyAnimator?) {
+ animator?.alpha(1f)
+ }
+
+ override fun setNewChangeAnimationEnd(holder: RecyclerView.ViewHolder?) {
+ holder?.itemView?.alpha = 1f
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/bean/TypeBean.java b/app/src/main/java/com/allen/androidcustomview/bean/TypeBean.java
new file mode 100644
index 0000000..a67554b
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/bean/TypeBean.java
@@ -0,0 +1,37 @@
+package com.allen.androidcustomview.bean;
+
+/**
+ *
+ * @author : xiaoyao
+ * e-mail : xiaoyao@51vest.com
+ * date : 2018/05/14
+ * desc :
+ * version : 1.0
+ *
+ */
+
+public class TypeBean {
+ private String title;
+ private int type;
+
+ public TypeBean(String title, int type) {
+ this.title = title;
+ this.type = type;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/bean/UserBean.java b/app/src/main/java/com/allen/androidcustomview/bean/UserBean.java
new file mode 100644
index 0000000..506ff43
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/bean/UserBean.java
@@ -0,0 +1,33 @@
+package com.allen.androidcustomview.bean;
+
+/**
+ *
+ * @author : xiaoyao
+ * e-mail : xiaoyao@51vest.com
+ * date : 2018/04/16
+ * desc :
+ * version : 1.0
+ *
+ */
+
+public class UserBean {
+ private String userName;
+ private String sortLetters="";
+
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getSortLetters() {
+ return sortLetters;
+ }
+
+ public void setSortLetters(String sortLetters) {
+ this.sortLetters = sortLetters;
+ }
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/bean/VoteBean.kt b/app/src/main/java/com/allen/androidcustomview/bean/VoteBean.kt
new file mode 100644
index 0000000..961f14c
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/bean/VoteBean.kt
@@ -0,0 +1,25 @@
+package com.allen.androidcustomview.bean
+
+
+/**
+ *
+ * @author : Allen
+ * date : 2019/08/01
+ * desc :
+ *
+ */
+
+class VoteBean(val id: Int = 0,
+ val title: String?,
+ val choiceType: String?,
+ val maxSelect: Int?,
+ var voted: Boolean?,
+ val sumVoteCount: Int?,
+ val options: ArrayList?
+)
+
+data class VoteOption(var id: Int?,
+ var content: String?,
+ var voteId: Int?,
+ var showCount: Int?,
+ var voted: Boolean?)
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/data/VoteData.kt b/app/src/main/java/com/allen/androidcustomview/data/VoteData.kt
new file mode 100644
index 0000000..67d4b9c
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/data/VoteData.kt
@@ -0,0 +1,49 @@
+package com.allen.androidcustomview.data
+
+import com.allen.androidcustomview.bean.VoteBean
+import com.allen.androidcustomview.bean.VoteOption
+import kotlin.random.Random
+
+/**
+ *
+ * @author : Allen
+ * date : 2019/08/06
+ * desc :
+ *
+ */
+
+fun getMockData(): ArrayList {
+ val list: ArrayList = arrayListOf()
+ for (i in 0..1)
+ list.add(getVoteBeanData(i))
+ return list
+}
+
+private fun getVoteBeanData(index: Int): VoteBean {
+ val voteTitle: String = when (index) {
+ 0 -> "哪吒票房能否突破30亿(多选)"
+ 1 -> "你觉得谁最火呢?(单选)"
+ else -> ""
+ }
+ return VoteBean(11, voteTitle, if (index == 0) "multiple" else "single", 2, false, Random.nextInt(10000, 20000), getVoteOptionsDatas(index))
+}
+
+private fun getVoteOptionsDatas(index: Int): java.util.ArrayList? {
+ var list: ArrayList = arrayListOf()
+ for (i in 0..3)
+ list.add(getVoteOptionData(index, i))
+ return list
+
+}
+
+private fun getVoteOptionData(index: Int, i: Int): VoteOption {
+
+ val voteContent: String = when (i) {
+ 0 -> if (index == 0) "当然可以" else "蔡徐坤"
+ 1 -> if (index == 0) "估计不能" else "肖战"
+ 2 -> if (index == 0) "拭目以待" else "李现"
+ 3 -> if (index == 0) "保持中立" else "邓伦"
+ else -> ""
+ }
+ return VoteOption(i, voteContent, i, Random.nextInt(6666,8888), false)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/helper/DragViewHelper.kt b/app/src/main/java/com/allen/androidcustomview/helper/DragViewHelper.kt
new file mode 100644
index 0000000..6022b9d
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/helper/DragViewHelper.kt
@@ -0,0 +1,69 @@
+package com.allen.androidcustomview.helper
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.RelativeLayout
+import com.allen.androidcustomview.R
+import com.allen.androidcustomview.listener.OnDragTouchListener
+import com.allen.androidcustomview.utils.DisplayUtils
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2019/03/14
+ * desc : 拖拽view辅助类
+ *
+ */
+object DragViewHelper {
+
+ @SuppressLint("ClickableViewAccessibility")
+ fun addDragView(context: Context,
+ parent: ViewGroup,
+ imgUrl: String,
+ defaultImgResId: Int = R.mipmap.ic_launcher,
+ dragViewSize: Float = 60f,
+ dragViewOriginalMarginRight: Float = 20f,
+ dragViewOriginalMarginBottom: Float = 20f,
+ autoPullToBorder: Boolean = true,
+ onClick: (() -> Unit)? = null): ImageView {
+ val dragView = ImageView(context)
+ parent.post {
+ val onDragTouchListener = OnDragTouchListener()
+ onDragTouchListener.clickListener = {
+ onClick?.invoke()
+ }
+ onDragTouchListener.mMaxWidth = parent.width
+ onDragTouchListener.mMaxHeight = parent.height
+ onDragTouchListener.mBorderMargin = DisplayUtils.dip2px(context, 15f).toFloat()
+ onDragTouchListener.mIsAutoToBorder = autoPullToBorder
+ dragView.scaleType = ImageView.ScaleType.CENTER_CROP
+ dragView.setOnTouchListener(onDragTouchListener)
+ val layoutParams = RelativeLayout.LayoutParams(DisplayUtils.dip2px(context, dragViewSize), DisplayUtils.dip2px(context, dragViewSize))
+ layoutParams.leftMargin = parent.width - DisplayUtils.dip2px(context, dragViewSize + dragViewOriginalMarginRight)
+ layoutParams.topMargin = parent.height - DisplayUtils.dip2px(context, dragViewSize + dragViewOriginalMarginBottom)
+// GlideApp.with(context).load(imgUrl).into(dragView)
+ dragView.setBackgroundResource(defaultImgResId)
+ parent.addView(dragView, layoutParams)
+ }
+
+ return dragView
+ }
+
+ fun removeDragView(parent: ViewGroup, view: View?) {
+ if (view != null) {
+ parent.removeView(view)
+ }
+ }
+
+ fun updateDragView(context: Context, dragView: ImageView?, imgUrl: String) {
+ if (dragView != null) {
+// GlideApp.with(context).load(imgUrl).into(dragView)
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/listener/OnDragTouchListener.kt b/app/src/main/java/com/allen/androidcustomview/listener/OnDragTouchListener.kt
new file mode 100644
index 0000000..587a745
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/listener/OnDragTouchListener.kt
@@ -0,0 +1,196 @@
+package com.allen.androidcustomview.listener
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.DecelerateInterpolator
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2019/03/14
+ * desc :
+ *
+ */
+class OnDragTouchListener : View.OnTouchListener {
+
+ //手指按下时的初始位置
+ private var mOriginalX: Float = 0f
+ private var mOriginalY: Float = 0f
+ //记录手指与view的左上角的距离
+ private var mDistanceX: Float = 0f
+ private var mDistanceY: Float = 0f
+ //拖拽view的上下左右距离
+ private var left: Int = 0
+ private var top: Int = 0
+ private var right: Int = 0
+ private var bottom: Int = 0
+ //最小的拖拽距离,小于这个值认为不是拖拽,区分是点击还是拖拽
+ private var minDragDistance = 10f
+ private var mLayoutParams: ViewGroup.MarginLayoutParams? = null
+
+ //可拖拽屏幕区域宽高
+ var mMaxWidth: Int = 0
+ var mMaxHeight: Int = 0
+
+ //点击事件
+ var clickListener: (() -> Unit)? = null
+
+ //标记是否自动吸附到边缘
+ var mIsAutoToBorder = true
+ //吸附在边缘时候距离边界的距离
+ var mBorderMargin = 0f
+
+ var mBorderMarginLeft = -1f
+ var mBorderMarginRight = -1f
+ var mBorderMarginTop = -1f
+ var mBorderMarginBottom = -1f
+
+ override fun onTouch(view: View, event: MotionEvent): Boolean {
+ when (event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ view.parent.requestDisallowInterceptTouchEvent(true)
+
+ mLayoutParams = mLayoutParams ?: view.layoutParams as ViewGroup.MarginLayoutParams
+
+ mOriginalX = event.rawX
+ mOriginalY = event.rawY
+
+ mDistanceX = event.rawX - view.left
+ mDistanceY = event.rawY - view.top
+ }
+
+ MotionEvent.ACTION_MOVE -> {
+ left = (event.rawX - mDistanceX).toInt()
+ top = (event.rawY - mDistanceY).toInt()
+ right = left + view.width
+ bottom = top + view.height
+ if (left < 0) {
+ left = 0
+ right = left + view.width
+ }
+ if (top < 0) {
+ top = 0
+ bottom = top + view.height
+ }
+ if (right > mMaxWidth) {
+ right = mMaxWidth
+ left = right - view.width
+ }
+ if (bottom > mMaxHeight) {
+ bottom = mMaxHeight
+ top = bottom - view.height
+ }
+
+ //如果其他view刷新导致重绘会调用layout方法,导致位置一闪一闪的,所有要用layoutParams设置位置
+ //view.layout(left, top, right, bottom)
+ mLayoutParams?.setMargins(left, top, 0, 0)
+ view.layoutParams = mLayoutParams
+ }
+ MotionEvent.ACTION_UP -> {
+ //如果移动距离过小,则判定为点击
+ if (Math.abs(event.rawX - mOriginalX) < minDragDistance && Math.abs(event.rawY - mOriginalY) < minDragDistance) {
+ clickListener?.invoke()
+ } else {
+ setAutoToBorder(view)
+ }
+ view.parent.requestDisallowInterceptTouchEvent(false)
+ //调取performClick()方法消除警告OnDragTouchListener#onTouch should call View#performClick when a click is detected more...
+ view.performClick()
+ }
+ }
+ return true
+ }
+
+
+ /**
+ * 开启自动拖拽
+ *
+ * @param v 拉动控件
+ */
+ private fun setAutoToBorder(v: View) {
+ if (!mIsAutoToBorder) return
+ setAnimation(v)
+ }
+
+ private fun setAnimation(v: View) {
+ val animatorSet = AnimatorSet()
+ if (getTopOrBottomAnimation(v) == null) {
+ animatorSet.play(getLeftOrRightAnimation(v))
+ } else {
+ animatorSet.play(getLeftOrRightAnimation(v)).with(getTopOrBottomAnimation(v))
+ }
+ animatorSet.duration = 300
+ animatorSet.start()
+ }
+
+
+ /**
+ * 获取吸附在左右边界的动画
+ */
+ private fun getLeftOrRightAnimation(v: View): Animator {
+ //当用户拖拽完后,让控件回到最近的边缘
+ var leftOrRightEnd = getBorderMargin(mBorderMarginLeft)
+ //吸附在右边边界处
+ if (left + v.width / 2 >= mMaxWidth / 2) {
+ leftOrRightEnd = (mMaxWidth - v.width - getBorderMargin(mBorderMarginRight))
+ }
+ val animator = ValueAnimator.ofFloat(left.toFloat(), leftOrRightEnd)
+ animator.interpolator = DecelerateInterpolator()
+ animator.addUpdateListener { animation ->
+ val leftMargin = (animation.animatedValue as Float).toInt()
+ mLayoutParams?.leftMargin = leftMargin
+ v.layoutParams = mLayoutParams
+ }
+ return animator
+ }
+
+ /**
+ * 获取吸附在顶部或者底部的动画
+ */
+ private fun getTopOrBottomAnimation(v: View): Animator? {
+ //吸附在上下边界处
+ var topOrBottomEnd: Float
+ //吸附在下边界处
+ return when {
+ //吸附到距离底部mBorderMargin的距离的位置
+ top + v.height >= mMaxHeight - mBorderMargin -> {
+ topOrBottomEnd = mMaxHeight - v.height - getBorderMargin(mBorderMarginBottom)
+ createTopOrBottomAnimation(v, topOrBottomEnd)
+ }
+ //吸附到距离顶部mBorderMargin的距离的位置
+ top <= mBorderMargin -> {
+ topOrBottomEnd = getBorderMargin(mBorderMarginTop)
+ createTopOrBottomAnimation(v, topOrBottomEnd)
+ }
+ else -> null
+ }
+ }
+
+
+ /**
+ * 创建底吸附到底部或顶部的动画
+ */
+ private fun createTopOrBottomAnimation(view: View, end: Float): Animator? {
+ val animator = ValueAnimator.ofFloat(top.toFloat(), end)
+ animator.interpolator = DecelerateInterpolator()
+ animator.addUpdateListener { animation ->
+ val topMargin = (animation.animatedValue as Float).toInt()
+ mLayoutParams?.topMargin = topMargin
+ view.layoutParams = mLayoutParams
+ }
+ return animator
+ }
+
+ /**
+ * 获取吸附的边界值
+ */
+ private fun getBorderMargin(margin: Float): Float {
+ return if (margin != -1f) margin else mBorderMargin
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/utils/CharacterParser.java b/app/src/main/java/com/allen/androidcustomview/utils/CharacterParser.java
new file mode 100644
index 0000000..41dc911
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/utils/CharacterParser.java
@@ -0,0 +1,131 @@
+package com.allen.androidcustomview.utils;
+
+/**
+ * Java汉字转换为拼音
+ *
+ */
+public class CharacterParser {
+ private static int[] pyvalue = new int[] {-20319, -20317, -20304, -20295, -20292, -20283, -20265, -20257, -20242, -20230, -20051, -20036, -20032,
+ -20026, -20002, -19990, -19986, -19982, -19976, -19805, -19784, -19775, -19774, -19763, -19756, -19751, -19746, -19741, -19739, -19728,
+ -19725, -19715, -19540, -19531, -19525, -19515, -19500, -19484, -19479, -19467, -19289, -19288, -19281, -19275, -19270, -19263, -19261,
+ -19249, -19243, -19242, -19238, -19235, -19227, -19224, -19218, -19212, -19038, -19023, -19018, -19006, -19003, -18996, -18977, -18961,
+ -18952, -18783, -18774, -18773, -18763, -18756, -18741, -18735, -18731, -18722, -18710, -18697, -18696, -18526, -18518, -18501, -18490,
+ -18478, -18463, -18448, -18447, -18446, -18239, -18237, -18231, -18220, -18211, -18201, -18184, -18183, -18181, -18012, -17997, -17988,
+ -17970, -17964, -17961, -17950, -17947, -17931, -17928, -17922, -17759, -17752, -17733, -17730, -17721, -17703, -17701, -17697, -17692,
+ -17683, -17676, -17496, -17487, -17482, -17468, -17454, -17433, -17427, -17417, -17202, -17185, -16983, -16970, -16942, -16915, -16733,
+ -16708, -16706, -16689, -16664, -16657, -16647, -16474, -16470, -16465, -16459, -16452, -16448, -16433, -16429, -16427, -16423, -16419,
+ -16412, -16407, -16403, -16401, -16393, -16220, -16216, -16212, -16205, -16202, -16187, -16180, -16171, -16169, -16158, -16155, -15959,
+ -15958, -15944, -15933, -15920, -15915, -15903, -15889, -15878, -15707, -15701, -15681, -15667, -15661, -15659, -15652, -15640, -15631,
+ -15625, -15454, -15448, -15436, -15435, -15419, -15416, -15408, -15394, -15385, -15377, -15375, -15369, -15363, -15362, -15183, -15180,
+ -15165, -15158, -15153, -15150, -15149, -15144, -15143, -15141, -15140, -15139, -15128, -15121, -15119, -15117, -15110, -15109, -14941,
+ -14937, -14933, -14930, -14929, -14928, -14926, -14922, -14921, -14914, -14908, -14902, -14894, -14889, -14882, -14873, -14871, -14857,
+ -14678, -14674, -14670, -14668, -14663, -14654, -14645, -14630, -14594, -14429, -14407, -14399, -14384, -14379, -14368, -14355, -14353,
+ -14345, -14170, -14159, -14151, -14149, -14145, -14140, -14137, -14135, -14125, -14123, -14122, -14112, -14109, -14099, -14097, -14094,
+ -14092, -14090, -14087, -14083, -13917, -13914, -13910, -13907, -13906, -13905, -13896, -13894, -13878, -13870, -13859, -13847, -13831,
+ -13658, -13611, -13601, -13406, -13404, -13400, -13398, -13395, -13391, -13387, -13383, -13367, -13359, -13356, -13343, -13340, -13329,
+ -13326, -13318, -13147, -13138, -13120, -13107, -13096, -13095, -13091, -13076, -13068, -13063, -13060, -12888, -12875, -12871, -12860,
+ -12858, -12852, -12849, -12838, -12831, -12829, -12812, -12802, -12607, -12597, -12594, -12585, -12556, -12359, -12346, -12320, -12300,
+ -12120, -12099, -12089, -12074, -12067, -12058, -12039, -11867, -11861, -11847, -11831, -11798, -11781, -11604, -11589, -11536, -11358,
+ -11340, -11339, -11324, -11303, -11097, -11077, -11067, -11055, -11052, -11045, -11041, -11038, -11024, -11020, -11019, -11018, -11014,
+ -10838, -10832, -10815, -10800, -10790, -10780, -10764, -10587, -10544, -10533, -10519, -10331, -10329, -10328, -10322, -10315, -10309,
+ -10307, -10296, -10281, -10274, -10270, -10262, -10260, -10256, -10254};
+ public static String[] pystr = new String[] {"a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian",
+ "biao", "bie", "bin", "bing", "bo", "bu", "ca", "cai", "can", "cang", "cao", "ce", "ceng", "cha", "chai", "chan", "chang", "chao", "che",
+ "chen", "cheng", "chi", "chong", "chou", "chu", "chuai", "chuan", "chuang", "chui", "chun", "chuo", "ci", "cong", "cou", "cu", "cuan",
+ "cui", "cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "deng", "di", "dian", "diao", "die", "ding", "diu", "dong", "dou", "du",
+ "duan", "dui", "dun", "duo", "e", "en", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fo", "fou", "fu", "ga", "gai", "gan", "gang",
+ "gao", "ge", "gei", "gen", "geng", "gong", "gou", "gu", "gua", "guai", "guan", "guang", "gui", "gun", "guo", "ha", "hai", "han", "hang",
+ "hao", "he", "hei", "hen", "heng", "hong", "hou", "hu", "hua", "huai", "huan", "huang", "hui", "hun", "huo", "ji", "jia", "jian",
+ "jiang", "jiao", "jie", "jin", "jing", "jiong", "jiu", "ju", "juan", "jue", "jun", "ka", "kai", "kan", "kang", "kao", "ke", "ken",
+ "keng", "kong", "kou", "ku", "kua", "kuai", "kuan", "kuang", "kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le", "lei", "leng",
+ "li", "lia", "lian", "liang", "liao", "lie", "lin", "ling", "liu", "long", "lou", "lu", "lv", "luan", "lue", "lun", "luo", "ma", "mai",
+ "man", "mang", "mao", "me", "mei", "men", "meng", "mi", "mian", "miao", "mie", "min", "ming", "miu", "mo", "mou", "mu", "na", "nai",
+ "nan", "nang", "nao", "ne", "nei", "nen", "neng", "ni", "nian", "niang", "niao", "nie", "nin", "ning", "niu", "nong", "nu", "nv", "nuan",
+ "nue", "nuo", "o", "ou", "pa", "pai", "pan", "pang", "pao", "pei", "pen", "peng", "pi", "pian", "piao", "pie", "pin", "ping", "po", "pu",
+ "qi", "qia", "qian", "qiang", "qiao", "qie", "qin", "qing", "qiong", "qiu", "qu", "quan", "que", "qun", "ran", "rang", "rao", "re",
+ "ren", "reng", "ri", "rong", "rou", "ru", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang", "sao", "se", "sen", "seng", "sha",
+ "shai", "shan", "shang", "shao", "she", "shen", "sheng", "shi", "shou", "shu", "shua", "shuai", "shuan", "shuang", "shui", "shun",
+ "shuo", "si", "song", "sou", "su", "suan", "sui", "sun", "suo", "ta", "tai", "tan", "tang", "tao", "te", "teng", "ti", "tian", "tiao",
+ "tie", "ting", "tong", "tou", "tu", "tuan", "tui", "tun", "tuo", "wa", "wai", "wan", "wang", "wei", "wen", "weng", "wo", "wu", "xi",
+ "xia", "xian", "xiang", "xiao", "xie", "xin", "xing", "xiong", "xiu", "xu", "xuan", "xue", "xun", "ya", "yan", "yang", "yao", "ye", "yi",
+ "yin", "ying", "yo", "yong", "you", "yu", "yuan", "yue", "yun", "za", "zai", "zan", "zang", "zao", "ze", "zei", "zen", "zeng", "zha",
+ "zhai", "zhan", "zhang", "zhao", "zhe", "zhen", "zheng", "zhi", "zhong", "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui",
+ "zhun", "zhuo", "zi", "zong", "zou", "zu", "zuan", "zui", "zun", "zuo"};
+ private StringBuilder buffer;
+ private String resource;
+ private static CharacterParser characterParser = new CharacterParser();
+
+ public static CharacterParser getInstance() {
+ return characterParser;
+ }
+
+ public String getResource() {
+ return resource;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ /** * 汉字转成ASCII码 * * @param chs * @return */
+ private int getChsAscii(String chs) {
+ int asc = 0;
+ try {
+ byte[] bytes = chs.getBytes("gb2312");
+ if (bytes == null || bytes.length > 2 || bytes.length <= 0) {
+ throw new RuntimeException("illegal resource string");
+ }
+ if (bytes.length == 1) {
+ asc = bytes[0];
+ }
+ if (bytes.length == 2) {
+ int hightByte = 256 + bytes[0];
+ int lowByte = 256 + bytes[1];
+ asc = (256 * hightByte + lowByte) - 256 * 256;
+ }
+ } catch (Exception e) {
+ System.out.println("ERROR:ChineseSpelling.class-getChsAscii(String chs)" + e);
+ }
+ return asc;
+ }
+
+ /** * 单字解析 * * @param str * @return */
+ public String convert(String str) {
+ String result = null;
+ int ascii = getChsAscii(str);
+ if (ascii > 0 && ascii < 160) {
+ result = String.valueOf((char) ascii);
+ } else {
+ for (int i = (pyvalue.length - 1); i >= 0; i--) {
+ if (pyvalue[i] <= ascii) {
+ result = pystr[i];
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ /** * 词组解析 * * @param chs * @return */
+ public String getSelling(String chs) {
+ String key, value;
+ buffer = new StringBuilder();
+ for (int i = 0; i < chs.length(); i++) {
+ key = chs.substring(i, i + 1);
+ if (key.getBytes().length >= 2) {
+ value = (String) convert(key);
+ if (value == null) {
+ value = "unknown";
+ }
+ } else {
+ value = key;
+ }
+ buffer.append(value);
+ }
+ return buffer.toString();
+ }
+
+ public String getSpelling() {
+ return this.getSelling(this.getResource());
+ }
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/utils/PinyinComparator.java b/app/src/main/java/com/allen/androidcustomview/utils/PinyinComparator.java
new file mode 100644
index 0000000..3e3474a
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/utils/PinyinComparator.java
@@ -0,0 +1,22 @@
+package com.allen.androidcustomview.utils;
+
+
+import com.allen.androidcustomview.bean.UserBean;
+
+import java.util.Comparator;
+
+public class PinyinComparator implements Comparator {
+
+ @Override
+ public int compare(UserBean o1, UserBean o2) {
+ if (o1.getSortLetters().equals("@")
+ || o2.getSortLetters().equals("#")) {
+ return -1;
+ } else if (o1.getSortLetters().equals("#")
+ || o2.getSortLetters().equals("@")) {
+ return 1;
+ } else {
+ return o1.getSortLetters().compareTo(o2.getSortLetters());
+ }
+ }
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/ArcView.kt b/app/src/main/java/com/allen/androidcustomview/widget/ArcView.kt
new file mode 100644
index 0000000..ef06f5f
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/ArcView.kt
@@ -0,0 +1,87 @@
+package com.allen.androidcustomview.widget
+
+import android.content.Context
+import android.graphics.*
+import android.util.AttributeSet
+import android.view.View
+import com.allen.androidcustomview.R
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2019/07/07
+ * desc :
+ *
+ */
+class ArcView : View {
+ private var mWidth = 0
+ private var mHeight = 0
+ /**
+ * 弧形高度
+ */
+ private var mArcHeight = 0
+ /**
+ * 背景颜色
+ */
+ private var mBgColor = Color.WHITE
+ private var mPaint = Paint()
+ private var mContext: Context? = null
+
+ private var mPath = Path()
+
+ constructor(context: Context) : this(context, null)
+ constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
+ constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
+ initView(context, attrs, defStyleAttr)
+ }
+
+ private fun initView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
+ val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ArcView)
+ mArcHeight = typedArray.getDimensionPixelSize(R.styleable.ArcView_arcHeight, 0)
+ mBgColor = typedArray.getColor(R.styleable.ArcView_bgColor, Color.WHITE)
+ typedArray.recycle()
+
+ mContext = context
+ mPaint.style = Paint.Style.FILL_AND_STROKE
+ mPaint.color = mBgColor
+ mPaint.isAntiAlias = true
+ mPaint.strokeWidth = 1f
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ mWidth = w
+ mHeight = h
+ resetPath()
+ }
+
+ private fun resetPath() {
+ mPath.reset()
+ mPath.moveTo(0f, 0f)
+ mPath.lineTo(0f, (mHeight - mArcHeight).toFloat())
+ mPath.quadTo((mWidth / 2).toFloat(), (mHeight + mArcHeight).toFloat(), mWidth.toFloat(), (mHeight - mArcHeight).toFloat())
+ mPath.lineTo(mWidth.toFloat(), 0f)
+ mPath.close()
+ }
+
+ override fun onDraw(canvas: Canvas?) {
+ super.onDraw(canvas)
+ canvas?.drawPath(mPath, mPaint)
+ }
+
+ fun setArcViewBgColor(color: Int) {
+ mBgColor = color
+ mPaint.color = color
+ invalidate()
+ }
+
+ fun getArcViewBgColor() = mBgColor
+
+ fun setArcViewHeight(height: Int) {
+ if (height == mHeight) return
+ mHeight = height
+ resetPath()
+ invalidate()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/CarMoveView.kt b/app/src/main/java/com/allen/androidcustomview/widget/CarMoveView.kt
new file mode 100644
index 0000000..d88eeee
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/CarMoveView.kt
@@ -0,0 +1,159 @@
+package com.allen.androidcustomview.widget
+
+import android.animation.Animator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.*
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.View
+import com.allen.androidcustomview.R
+import kotlin.math.atan2
+
+
+/**
+ *
+ * @author : Allen
+ * date : 2019/07/23
+ * desc : 小汽车跟随轨迹运动
+ *
+ */
+class CarMoveView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
+
+ private var mWidth = 0
+ private var mHeight = 0
+
+ /**
+ * 背景颜色
+ */
+ private var mPathColor = Color.RED
+ private var mPaint = Paint()
+ private var mCarPaint = Paint()
+
+ private var mRect = Rect()
+ private var mMovePath = Path()
+
+ private var mStartX = 0f
+ private var mStartY = 0f
+
+ private var pathMeasure: PathMeasure = PathMeasure()
+
+ private val pos = FloatArray(2)
+ private val tan = FloatArray(2)
+
+ private var isMoveCar = false
+
+ private var mRectWidth = 30
+
+ private var mDuration = 5
+ private var mCarDrawableRes: Drawable? = null
+ private var mCarBitmapRes: Bitmap? = null
+
+ init {
+ initView(context, attrs, defStyleAttr)
+ initPaint()
+ mCarBitmapRes = (mCarDrawableRes as? BitmapDrawable)?.bitmap
+ }
+
+ private fun initView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
+ val typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarMoveView)
+ mPathColor = typedArray.getColor(R.styleable.CarMoveView_carMovePathColor, mPathColor)
+ mDuration = typedArray.getInt(R.styleable.CarMoveView_carMoveDuration, mDuration)
+ mCarDrawableRes = typedArray.getDrawable(R.styleable.CarMoveView_carMoveDrawableRes)
+ typedArray.recycle()
+ }
+
+ private fun initPaint() {
+ mPaint.color = mPathColor
+ mPaint.style = Paint.Style.STROKE
+ mPaint.isAntiAlias = true
+
+ mCarPaint.color = mPathColor
+ mCarPaint.style = Paint.Style.FILL
+ mCarPaint.isAntiAlias = true
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ mWidth = w
+ mHeight = h
+ }
+
+ override fun onDraw(canvas: Canvas?) {
+ super.onDraw(canvas)
+ drawPath(canvas)
+ drawMoveCar(canvas)
+ }
+
+ private fun drawPath(canvas: Canvas?) {
+ canvas?.drawPath(mMovePath, mPaint)
+ }
+
+ private fun drawMoveCar(canvas: Canvas?) {
+ pathMeasure.setPath(mMovePath, false)
+ if (isMoveCar) {
+ // 计算图片旋转角度
+ val degrees = (atan2(tan[1].toDouble(), tan[0].toDouble()) * 180.0 / Math.PI).toFloat()
+ canvas?.rotate(degrees, pos[0], pos[1])
+ //小车中心点在运行轨道上
+ mRect.set((pos[0] - mRectWidth).toInt(), (pos[1] - mRectWidth).toInt(), (pos[0] + mRectWidth).toInt(), (pos[1] + mRectWidth).toInt())
+ //小车轮子在运行轨道上
+ //mRect.set((pos[0] - mRectWidth).toInt(), (pos[1] - mRectWidth * 2).toInt(), (pos[0] + mRectWidth).toInt(), (pos[1]).toInt())
+ if (mCarBitmapRes == null) {
+ canvas?.drawRect(mRect, mCarPaint)
+ } else {
+ canvas?.drawBitmap(mCarBitmapRes, null, mRect, mPaint)
+ }
+ }
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ when (event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ mStartX = event.x
+ mStartY = event.y
+ mMovePath.reset()
+ mMovePath.moveTo(mStartX, mStartY)
+ return true
+ }
+ MotionEvent.ACTION_MOVE -> {
+ val endX = (mStartX + event.x) / 2
+ val endY = (mStartY + event.y) / 2
+ mMovePath.quadTo(mStartX, mStartY, endX, endY)
+ mStartX = event.x
+ mStartY = event.y
+ invalidate()
+ return true
+ }
+ }
+ return super.onTouchEvent(event)
+ }
+
+ fun startAnim() {
+ isMoveCar = true
+ val valueAnimator = ValueAnimator.ofFloat(0f, pathMeasure.length)
+ valueAnimator.duration = mDuration * 1000L
+ valueAnimator.addUpdateListener {
+ val distance: Float = it.animatedValue as Float
+ pathMeasure.getPosTan(distance, pos, tan)
+ invalidate()
+ }
+ valueAnimator.addListener(object : Animator.AnimatorListener {
+ override fun onAnimationRepeat(animation: Animator?) {}
+
+ override fun onAnimationEnd(animation: Animator?) {
+ isMoveCar = false
+ }
+
+ override fun onAnimationCancel(animation: Animator?) {
+ isMoveCar = false
+ }
+
+ override fun onAnimationStart(animation: Animator?) {}
+
+ })
+ valueAnimator.start()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/ClearScreenView.kt b/app/src/main/java/com/allen/androidcustomview/widget/ClearScreenView.kt
new file mode 100644
index 0000000..3b5e0bf
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/ClearScreenView.kt
@@ -0,0 +1,371 @@
+package com.allen.androidcustomview.widget
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.content.Context
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.VelocityTracker
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import kotlin.math.abs
+
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2021/01/28
+ * desc : 滑动清屏view
+ *
+ */
+enum class ClearScreenType {
+ LEFT_TO_RIGHT,//从左滑到右清屏
+ RIGHT_TO_LEFT //从右滑到左清屏
+}
+
+enum class ClearScreenStatus {
+ NORMAL,//正常状态
+ CLEARED//已经清屏状态
+}
+
+enum class ClearScreenMode {
+ QUICK_SCROLL,//快速滑动才触发清屏
+ SLOW_SCROLL//滑动出发清屏
+}
+
+class ClearScreenView @JvmOverloads constructor(private val mContext: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : FrameLayout(mContext, attrs, defStyleAttr) {
+
+ companion object {
+ /**
+ * 最小移动距离
+ */
+ private const val MIN_SCROLL_SIZE = 50
+
+ /**
+ * 水平方向最小滑动速度
+ */
+ private const val MIN_X_VELOCITY = 10
+
+ /**
+ * 清屏动画时长
+ */
+ private const val DURATION = 300L
+ }
+
+ /**
+ * 手指按下的x轴位置
+ */
+ private var mDownX = 0
+
+ /**
+ * 手指按下的y轴位置
+ */
+ private var mDownY = 0
+
+ /**
+ * 清屏view清屏时需要在x轴的偏移量
+ */
+ private var translateX = 0
+
+ /**
+ * 清屏view起始偏移量(例如:从无到有迁移量从-width到0)
+ */
+ private var startTranslateX = 0
+
+ /**
+ * 滑动速度对象
+ */
+ private var mVelocityTracker: VelocityTracker? = null
+
+ /**
+ * 清屏动画对象
+ */
+ private var mAnimator: ValueAnimator? = null
+
+ /**
+ * 需要清除的Views
+ */
+ private var listClearViews: ArrayList = ArrayList()
+
+ /**
+ * 清屏事件
+ */
+ private var clearScreenListener: OnClearScreenListener? = null
+
+ /**
+ * 清屏类型 左滑清屏 or 右滑清屏 (默认从左滑到右清屏)
+ */
+ private var clearScreenType = ClearScreenType.LEFT_TO_RIGHT
+
+ /**
+ * 当前清屏状态
+ */
+ private var clearScreenStatus = ClearScreenStatus.NORMAL
+
+ /**
+ * 清屏模式
+ */
+ var clearScreenMode = ClearScreenMode.QUICK_SCROLL
+
+ /**
+ * 是否正在处在滑动清屏状态
+ */
+ private var isScrolling = false
+
+ init {
+ initView()
+ initAnim()
+ }
+
+ private fun initView() {
+ val view = View(mContext)
+ view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ view.isClickable = true
+ addView(view, 0)
+ }
+
+ private fun initAnim() {
+ mVelocityTracker = VelocityTracker.obtain()
+ mAnimator = ValueAnimator.ofFloat(0f, 1.0f).setDuration(DURATION)
+ mAnimator?.addUpdateListener {
+ val value = it.animatedValue as Float
+ val translate = startTranslateX + value * translateX
+ translateChild(translate)
+ }
+ mAnimator?.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ when (clearScreenStatus) {
+ ClearScreenStatus.CLEARED -> {
+ clearScreenStatus = ClearScreenStatus.NORMAL
+ clearScreenListener?.onRestored()
+ }
+ ClearScreenStatus.NORMAL -> {
+ clearScreenStatus = ClearScreenStatus.CLEARED
+ clearScreenListener?.onCleared()
+ }
+ }
+ isScrolling = false
+ }
+ })
+ }
+
+ override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
+ if (clearScreenListener?.isForbidClearScreen() == true) {
+ return super.onInterceptTouchEvent(ev)
+ }
+ val x = ev.x.toInt()
+ val y = ev.y.toInt()
+ when (ev.action) {
+ MotionEvent.ACTION_DOWN -> {
+ mDownX = x
+ mDownY = y
+ }
+ }
+ return isInterceptClearScreenEvent(x, y)
+ }
+
+ private fun isInterceptClearScreenEvent(x: Int, y: Int): Boolean {
+ return when (clearScreenMode) {
+ ClearScreenMode.QUICK_SCROLL -> {
+ val isIntercept = isMoveForHorizontal(x, y) && !isAnimRunning() && isGreaterThanMinSize(mDownX, x)
+ requestDisallowInterceptTouchEvent(isIntercept)
+ isIntercept
+ }
+ ClearScreenMode.SLOW_SCROLL -> {
+ val isIntercept = isMoveForHorizontal(x, y) && !isAnimRunning() || isScrolling
+ requestDisallowInterceptTouchEvent(isIntercept)
+ isIntercept
+ }
+ }
+ }
+
+ /**
+ * 是否水平方向滑动
+ */
+ private fun isMoveForHorizontal(x: Int, y: Int): Boolean {
+ return abs(x - mDownX) > abs(y - mDownY)
+ }
+
+ private fun isAnimRunning(): Boolean {
+ return mAnimator?.isRunning == true
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ if (isAnimRunning()) return true
+ when (clearScreenMode) {
+ ClearScreenMode.QUICK_SCROLL -> {
+ handleQuickScrollMode(event)
+ }
+ ClearScreenMode.SLOW_SCROLL -> {
+ handleSlowScrollMode(event)
+ }
+ }
+ return true
+ }
+
+ private fun handleQuickScrollMode(event: MotionEvent) {
+ mVelocityTracker?.addMovement(event)
+ when (event.action) {
+ MotionEvent.ACTION_UP -> {
+ mVelocityTracker?.computeCurrentVelocity(10)
+ val xVelocity = mVelocityTracker?.xVelocity ?: 0f
+ when {
+ //从左往右滑动(是正数)速度大于阈值
+ xVelocity > MIN_X_VELOCITY -> {
+ setLeft2RightMoveTranslateX()
+ }
+ //从右往左滑动(是负数)速度大于阈值
+ xVelocity < -MIN_X_VELOCITY -> {
+ setRight2LeftMoveTranslateX()
+ }
+ else -> {
+ translateX = 0
+ }
+ }
+ if (translateX != 0) {
+ mAnimator?.start()
+ }
+ }
+ }
+ }
+
+ private fun handleSlowScrollMode(event: MotionEvent) {
+ val x = event.x.toInt()
+ when (event.action) {
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
+ when (clearScreenStatus) {
+ ClearScreenStatus.NORMAL -> {
+ if (startTranslateX >= width / 2) {
+ translateX = width - startTranslateX
+ clearScreenStatus = ClearScreenStatus.NORMAL
+ } else {
+ translateX = -startTranslateX
+ clearScreenStatus = ClearScreenStatus.CLEARED
+ }
+ mAnimator?.start()
+ }
+ ClearScreenStatus.CLEARED -> {
+ if (startTranslateX >= width / 2) {
+ translateX = width - startTranslateX
+ clearScreenStatus = ClearScreenStatus.NORMAL
+ } else {
+ translateX = (-startTranslateX)
+ clearScreenStatus = ClearScreenStatus.CLEARED
+ }
+ mAnimator?.start()
+ }
+ }
+ }
+ MotionEvent.ACTION_MOVE -> {
+ translateX = 0
+ val move = x - mDownX
+ when (clearScreenStatus) {
+ ClearScreenStatus.NORMAL -> {
+ val translate = if (x <= mDownX) 0 else move
+ startTranslateX = translate
+ if (translate != 0) {
+ translateChild(translate.toFloat())
+ }
+ }
+ ClearScreenStatus.CLEARED -> {
+ val translate = if (x > mDownX) 0 else mDownX - x
+ startTranslateX = width - translate
+ if (startTranslateX != 0) {
+ translateChild(startTranslateX.toFloat())
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun setLeft2RightMoveTranslateX() {
+ when (clearScreenType) {
+ ClearScreenType.LEFT_TO_RIGHT -> {
+ startTranslateX = 0
+ translateX = if (clearScreenStatus == ClearScreenStatus.NORMAL) width else 0
+ }
+ ClearScreenType.RIGHT_TO_LEFT -> {
+ startTranslateX = -width
+ translateX = if (clearScreenStatus == ClearScreenStatus.NORMAL) 0 else width
+ }
+ }
+ }
+
+ private fun setRight2LeftMoveTranslateX() {
+ when (clearScreenType) {
+ ClearScreenType.LEFT_TO_RIGHT -> {
+ startTranslateX = width
+ translateX = if (clearScreenStatus == ClearScreenStatus.NORMAL) 0 else -width
+ }
+ ClearScreenType.RIGHT_TO_LEFT -> {
+ startTranslateX = 0
+ translateX = if (clearScreenStatus == ClearScreenStatus.NORMAL) -width else 0
+ }
+ }
+ }
+
+ /**
+ * 是否大于清屏方向的最小值
+ */
+ private fun isGreaterThanMinSize(downX: Int, moveX: Int): Boolean {
+ return when (clearScreenType) {
+ ClearScreenType.LEFT_TO_RIGHT -> {
+ when (clearScreenStatus) {
+ ClearScreenStatus.NORMAL -> moveX - downX > MIN_SCROLL_SIZE
+ ClearScreenStatus.CLEARED -> downX - moveX > MIN_SCROLL_SIZE
+ }
+ }
+ ClearScreenType.RIGHT_TO_LEFT -> {
+ when (clearScreenStatus) {
+ ClearScreenStatus.NORMAL -> downX - moveX > MIN_SCROLL_SIZE
+ ClearScreenStatus.CLEARED -> moveX - downX > MIN_SCROLL_SIZE
+ }
+ }
+ }
+ }
+
+ private fun translateChild(translate: Float) {
+ isScrolling = true
+ for (view in listClearViews) {
+ view.translationX = translate
+ }
+ }
+
+ fun addClearViews(views: List) {
+ for (view in views) {
+ if (!listClearViews.contains(view)) {
+ listClearViews.add(view)
+ }
+ }
+ }
+
+ fun addClearView(view: View) {
+ if (!listClearViews.contains(view)) {
+ listClearViews.add(view)
+ }
+ }
+
+ fun removeClearViews(views: List) {
+ for (view in views) {
+ listClearViews.remove(view)
+ }
+ }
+
+ fun removeAllClearViews() {
+ listClearViews.clear()
+ }
+
+ fun setOnClearScreenListener(listener: OnClearScreenListener?) {
+ this.clearScreenListener = listener
+ }
+
+ interface OnClearScreenListener {
+ fun onCleared()
+ fun onRestored()
+ fun isForbidClearScreen(): Boolean = false
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/DividerItemDecoration.java b/app/src/main/java/com/allen/androidcustomview/widget/DividerItemDecoration.java
new file mode 100644
index 0000000..040e988
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/DividerItemDecoration.java
@@ -0,0 +1,117 @@
+package com.allen.androidcustomview.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+/**
+ *
+ * @author : xiaoyao
+ * e-mail : xiaoyao@51vest.com
+ * date : 2018/02/05
+ * desc : recyclerview的分割线
+ * version : 1.0
+ *
+ */
+
+public class DividerItemDecoration extends RecyclerView.ItemDecoration {
+ private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
+
+ public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
+
+ public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
+
+
+ private Drawable mDivider;
+
+ private int mOrientation;
+
+ private Context context;
+
+ public DividerItemDecoration(Context context, int orientation) {
+ final TypedArray a = context.obtainStyledAttributes(ATTRS);
+ mDivider = a.getDrawable(0);
+ this.context = context;
+ a.recycle();
+ setOrientation(orientation);
+ }
+
+ public void setOrientation(int orientation) {
+ if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
+ throw new IllegalArgumentException("invalid orientation");
+ }
+ mOrientation = orientation;
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ super.onDraw(c, parent, state);
+ if (mOrientation == VERTICAL_LIST) {
+ drawVertical(c, parent);
+ } else {
+ drawHorizontal(c, parent);
+ }
+ }
+
+ public void drawVertical(Canvas c, RecyclerView parent) {
+ final int left = parent.getPaddingLeft() + dip2px(context, 0);
+ final int right = parent.getWidth() - parent.getPaddingRight() - dip2px(context, 0);
+
+ final int childCount = parent.getChildCount();
+
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+ android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(
+ parent.getContext());
+ final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+ .getLayoutParams();
+ final int top = child.getBottom() + params.bottomMargin;
+ final int bottom = top + mDivider.getIntrinsicHeight();
+ mDivider.setBounds(left, top, right, bottom);
+ mDivider.draw(c);
+ }
+ }
+
+ public void drawHorizontal(Canvas c, RecyclerView parent) {
+ final int top = parent.getPaddingTop();
+ final int bottom = parent.getHeight() - parent.getPaddingBottom();
+
+ final int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+ final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
+ .getLayoutParams();
+ final int left = child.getRight() + params.rightMargin;
+ final int right = left + mDivider.getIntrinsicHeight();
+ mDivider.setBounds(left, top, right, bottom);
+ mDivider.draw(c);
+ }
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+ super.getItemOffsets(outRect, view, parent, state);
+ if (mOrientation == VERTICAL_LIST) {
+ outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
+ } else {
+ outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
+ }
+ }
+
+ /**
+ * 单位转换工具类
+ *
+ * @param context 上下文对象
+ * @param dipValue 值
+ * @return 返回值
+ */
+ private int dip2px(Context context, float dipValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dipValue * scale + 0.5f);
+ }
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/HorizontalProgressBar.java b/app/src/main/java/com/allen/androidcustomview/widget/HorizontalProgressBar.java
index e418c2a..e18d347 100644
--- a/app/src/main/java/com/allen/androidcustomview/widget/HorizontalProgressBar.java
+++ b/app/src/main/java/com/allen/androidcustomview/widget/HorizontalProgressBar.java
@@ -345,6 +345,7 @@ public void onAnimationUpdate(ValueAnimator valueAnimator) {
moveDis = currentProgress - tipWidth / 2;
}
invalidate();
+ setCurrentProgress(value);
}
});
progressAnimator.start();
@@ -373,6 +374,15 @@ public HorizontalProgressBar setCurrentProgress(float progress) {
mProgress = progress;
currentProgress = progress * mWidth / 100;
textString = formatNum(format2Int(progress));
+
+ //移动百分比提示框,只有当前进度到提示框中间位置之后开始移动,
+ //当进度框移动到最右边的时候停止移动,但是进度条还可以继续移动
+ //moveDis是tip框移动的距离
+ if (currentProgress >= (tipWidth / 2) &&
+ currentProgress <= (mWidth - tipWidth / 2)) {
+ moveDis = currentProgress - tipWidth / 2;
+ }
+
invalidate();
return this;
}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/HoverItemDecoration.java b/app/src/main/java/com/allen/androidcustomview/widget/HoverItemDecoration.java
new file mode 100644
index 0000000..46570b2
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/HoverItemDecoration.java
@@ -0,0 +1,223 @@
+package com.allen.androidcustomview.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
+import android.view.View;
+
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2018/04/15
+ * desc : 悬停吸顶效果的ItemDecoration
+ * version : 1.0
+ *
+ */
+public class HoverItemDecoration extends RecyclerView.ItemDecoration {
+
+ private Context context;
+
+ private int width;
+
+ /**
+ * 分组item的高度
+ */
+ private int itemHeight;
+ /**
+ * 分割线的高度
+ */
+ private int itemDivideHeight;
+ /**
+ * 分组text距离左边的距离
+ */
+ private int itemTextPaddingLeft;
+
+ /**
+ * 分组item的画笔
+ */
+ private Paint itemPaint;
+ /**
+ * 分组item的颜色
+ */
+ private int itemHoverPaintColor=0xFFf4f4f4;
+ /**
+ * 分组文字的颜色
+ */
+ private int textPaintColor=0xFF999999;
+
+ /**
+ * 悬停item的画笔
+ */
+ private Paint itemHoverPaint;
+ /**
+ * 文字的画笔
+ */
+ private Paint textPaint;
+ /**
+ * 绘制文字的矩形边框
+ */
+ private Rect textRect = new Rect();
+ /**
+ * 分组字母的回调(一般是取的分组的大写字母)
+ */
+ private BindItemTextCallback bindItemTextCallback;
+
+
+ public HoverItemDecoration(Context content, BindItemTextCallback bindItemTextCallback) {
+ this.context = content;
+ this.bindItemTextCallback = bindItemTextCallback;
+
+ width = content.getResources().getDisplayMetrics().widthPixels;
+
+ itemPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ itemPaint.setColor(itemHoverPaintColor);
+
+ itemHoverPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ itemHoverPaint.setColor(itemHoverPaintColor);
+
+ itemHeight = dp2px(30);
+ itemTextPaddingLeft = dp2px(20);
+ itemDivideHeight = dp2px(1);
+
+ textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ textPaint.setColor(textPaintColor);
+ textPaint.setTextSize(sp2px(15));
+
+
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ super.onDraw(c, parent, state);
+ int count = parent.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View view = parent.getChildAt(i);
+ //分组item的顶部和底部
+ int itemTop = view.getTop() - itemHeight;
+ int itemBottom = view.getTop();
+
+ //可见item在adapter中真实的位置
+ int position = parent.getChildAdapterPosition(view);
+
+ //获取回调的分组文字(一般是字母)
+ String text = bindItemTextCallback.getItemText(position);
+
+ //如果是一组中第一个的话绘制出分组的item和文字,否则绘制分割线
+ if (isFirstInGroup(position)) {
+ c.drawRect(0, itemTop, width, itemBottom, itemPaint);
+ drawText(c, itemTop, itemBottom, text);
+ } else {
+ c.drawRect(0, view.getTop() - itemDivideHeight, width, view.getTop(), itemHoverPaint);
+ }
+
+ }
+ }
+
+ @Override
+ public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ super.onDrawOver(c, parent, state);
+ //绘制悬停的view
+
+ int count = parent.getChildCount();
+
+ if (count > 0) {
+ //悬停只是第一个位置悬停,所以只取第一个view进行设置
+ View firstView = parent.getChildAt(0);
+
+ int position = parent.getChildAdapterPosition(firstView);
+ String text = bindItemTextCallback.getItemText(position);
+
+ //如果悬停view的底部小于悬停布局的高度说明正在上滑,就让他随着滑动逐渐滑进去,否则就固定悬停位置不边
+ //isFirstInGroup(position+1)是下一个item是另外分组第一个的时候当前item才滚动上去
+ if (firstView.getBottom() <= itemHeight && isFirstInGroup(position + 1)) {
+ c.drawRect(0, 0, width, firstView.getBottom(), itemHoverPaint);
+ drawText(c, firstView.getBottom() - itemHeight, firstView.getBottom(), text);
+ } else {
+ c.drawRect(0, 0, width, itemHeight, itemHoverPaint);
+ drawText(c, 0, itemHeight, text);
+ }
+ }
+
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+ super.getItemOffsets(outRect, view, parent, state);
+ int position = parent.getChildAdapterPosition(view);
+ //如果是分组第一个就留出绘制item的高度
+ if (isFirstInGroup(position)) {
+ outRect.top = itemHeight;
+ }else {
+ outRect.top = itemDivideHeight;
+ }
+
+ }
+
+
+ /**
+ * 绘制文字
+ *
+ * @param canvas 画布
+ */
+ private void drawText(Canvas canvas, int itemTop, int itemBottom, String textString) {
+
+ textRect.left = itemTextPaddingLeft;
+ textRect.top = itemTop;
+ textRect.right = textString.length();
+ textRect.bottom = itemBottom;
+
+ Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
+ int baseline = (textRect.bottom + textRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
+ //文字绘制到整个布局的中心位置
+ canvas.drawText(textString, textRect.left, baseline, textPaint);
+ }
+
+
+ private boolean isFirstInGroup(int position) {
+ if (position == 0) {
+ return true;
+ } else {
+ String prevItemText = bindItemTextCallback.getItemText(position - 1);
+ String currentItemText = bindItemTextCallback.getItemText(position);
+ //上一个和当前位置的值一样说明是同一个组的否则就是新的一组
+ if (prevItemText.equals(currentItemText)) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+ }
+
+ public interface BindItemTextCallback {
+ String getItemText(int position);
+ }
+
+ /**
+ * dp 2 px
+ *
+ * @param dpVal
+ */
+ protected int dp2px(int dpVal) {
+ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ dpVal, context.getResources().getDisplayMetrics());
+ }
+
+ /**
+ * sp 2 px
+ *
+ * @param spVal
+ * @return
+ */
+ protected int sp2px(int spVal) {
+ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+ spVal, context.getResources().getDisplayMetrics());
+
+ }
+
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/IndexView.java b/app/src/main/java/com/allen/androidcustomview/widget/IndexView.java
new file mode 100644
index 0000000..17eaa3d
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/IndexView.java
@@ -0,0 +1,154 @@
+package com.allen.androidcustomview.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * Created by allen on 2016/10/26.
+ */
+
+public class IndexView extends View {
+
+ private Context mContext;
+ private TextView mShowTextDialog;
+
+
+ private Paint mPaint;
+
+ private int mWidth;
+ private int mHeight;
+
+ private int mCellWidth;
+ private int mCellHeight;
+
+ private int mWordSize;
+ private int mwordColor;
+ private int mChoose = -1;// 选中
+
+
+ private int GRAY = 0xFFe8e8e8;
+ private int DEFAULT_TEXT_COLOR = 0xFF999999;
+
+ private static final String[] WORDS = new String[]{
+ "↑", "☆", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
+ "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
+ "X", "Y", "Z", "#"
+ };
+
+ private OnTouchingLetterChangedListener mOnTouchingLetterChangedListener;
+
+ public IndexView(Context context) {
+ this(context, null);
+ }
+
+ public IndexView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public IndexView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mContext = context;
+ mWordSize = sp2px(mContext, 12);
+ mwordColor = 0;
+ initPaint();
+ }
+
+
+ public void setShowTextDialog(TextView textDialog) {
+ this.mShowTextDialog = textDialog;
+ }
+
+ private void initPaint() {
+ mPaint = new Paint();
+ mPaint.setColor(DEFAULT_TEXT_COLOR);
+ mPaint.setAntiAlias(true);
+ mPaint.setTextSize(mWordSize);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mWidth = getMeasuredWidth();
+ mHeight = getMeasuredHeight();
+
+ mCellHeight = mHeight / WORDS.length;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ for (int i = 0; i < WORDS.length; i++) {
+ float xPos = mWidth / 2 - mPaint.measureText(WORDS[i]) / 2;
+ float yPos = mCellHeight * i + mCellHeight;
+ canvas.drawText(WORDS[i], xPos, yPos, mPaint);
+ }
+
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ final int action = event.getAction();
+ final float y = event.getY();// 点击y坐标
+ final int oldChoose = mChoose;
+ final int c = (int) (y / getHeight() * WORDS.length);// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数.
+
+
+ switch (action) {
+ case MotionEvent.ACTION_UP:
+ setBackgroundColor(0x00000000);
+ mChoose = -1;//
+ invalidate();
+ if (mShowTextDialog != null) {
+ mShowTextDialog.setVisibility(View.INVISIBLE);
+ }
+ break;
+
+ default:
+ setBackgroundColor(GRAY);
+ if (oldChoose != c) {
+ if (c >= 0 && c < WORDS.length) {
+ if (mOnTouchingLetterChangedListener != null) {
+ mOnTouchingLetterChangedListener.onTouchingLetterChanged(WORDS[c]);
+ }
+ if (mShowTextDialog != null) {
+ mShowTextDialog.setText(WORDS[c]);
+ mShowTextDialog.setVisibility(View.VISIBLE);
+ }
+
+ mChoose = c;
+ invalidate();
+ }
+ }
+
+ break;
+ }
+ return true;
+ }
+
+
+ public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener letterChangedListener) {
+ mOnTouchingLetterChangedListener = letterChangedListener;
+
+ }
+
+ public interface OnTouchingLetterChangedListener {
+ void onTouchingLetterChanged(String letter);
+ }
+
+ /**
+ * 文字字体大小sp转换px
+ *
+ * @param context 上下文对象
+ * @param spValue sp的值
+ * @return 返回值
+ */
+ public int sp2px(Context context, float spValue) {
+ final float scale = context.getResources().getDisplayMetrics().scaledDensity;
+ return (int) (spValue * scale + 0.5f);
+ }
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/IndicatorView.java b/app/src/main/java/com/allen/androidcustomview/widget/IndicatorView.java
new file mode 100644
index 0000000..251dad0
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/IndicatorView.java
@@ -0,0 +1,240 @@
+package com.allen.androidcustomview.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+
+/**
+ *
+ * @author : xiaoyao
+ * e-mail : xiaoyao@51vest.com
+ * date : 2018/02/27
+ * desc : 首页banner指示器
+ * version : 1.0
+ *
+ */
+
+public class IndicatorView extends View {
+
+ private int mWidth;
+ private int mHeight;
+
+ private float startX;
+
+ /**
+ * 动画起点x坐标
+ */
+ private int centerX;
+
+ /**
+ * 动画起点y坐标
+ */
+ private int centerY;
+
+ /**
+ * view真实高度
+ */
+ private int mViewHeight;
+
+ private int mViewWidth;
+
+ /**
+ * 画笔宽度(等于vie高度)
+ */
+ private int paintWidth;
+
+ private int radius;
+ private int R;
+
+ /**
+ * 圆的颜色
+ */
+ private int circleBgColor = 0xFFCED3D6;
+
+ /**
+ * 当前指示器的颜色
+ */
+ private int currentColor = 0xFFA0946C;
+
+
+ /**
+ * 圆画笔
+ */
+ private Paint circlePaint;
+ /**
+ * 当前指示器的画笔
+ */
+ private Paint indicatorPaint;
+
+
+ private int pointNum = 0;
+ private int currentIndex = 0;
+ /**
+ * 圆之间的间距
+ */
+ private int dis;
+
+ private RectF rectF;
+
+ public IndicatorView(Context context) {
+ this(context, null);
+ }
+
+ public IndicatorView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public IndicatorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ private void init() {
+ rectF = new RectF();
+ paintWidth = dp2px(1);
+ mViewHeight = dp2px(8);
+ radius = dp2px(3);
+ R = 2 * radius;
+ dis = 2 * radius;
+ circlePaint = getPaint(paintWidth, circleBgColor, Paint.Style.FILL);
+ indicatorPaint = getPaint(paintWidth, currentColor, Paint.Style.FILL);
+ }
+
+
+ /**
+ * 统一处理paint
+ *
+ * @param strokeWidth 画笔宽度
+ * @param color 颜色
+ * @param style 风格
+ * @return paint
+ */
+ private Paint getPaint(int strokeWidth, int color, Paint.Style style) {
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setStrokeWidth(strokeWidth);
+ paint.setColor(color);
+ paint.setAntiAlias(true);
+ paint.setStyle(style);
+ return paint;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mWidth = w;
+ mHeight = h;
+
+ centerX = w / 2;
+ centerY = h / 2;
+
+ mViewWidth = (2 * pointNum + 1) * R;
+
+ startX = (mWidth - mViewWidth) / 2;
+
+ }
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ setMeasuredDimension(measureWidth(widthMode, width), measureHeight(heightMode, height));
+ }
+
+ /**
+ * 测量宽度
+ *
+ * @param mode
+ * @param width
+ * @return
+ */
+ private int measureWidth(int mode, int width) {
+ switch (mode) {
+ case MeasureSpec.UNSPECIFIED:
+ case MeasureSpec.AT_MOST:
+ break;
+ case MeasureSpec.EXACTLY:
+ mWidth = width;
+ break;
+ }
+ return mWidth;
+ }
+
+ /**
+ * 测量高度
+ *
+ * @param mode
+ * @param height
+ * @return
+ */
+ private int measureHeight(int mode, int height) {
+ switch (mode) {
+ case MeasureSpec.UNSPECIFIED:
+ case MeasureSpec.AT_MOST:
+ mHeight = mViewHeight;
+ break;
+ case MeasureSpec.EXACTLY:
+ mHeight = height;
+ break;
+ }
+ return mHeight;
+ }
+
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ for (int i = 0; i < pointNum + 1; i++) {
+ drawCircle(canvas, i);
+ }
+ drawCurrentIndicator(canvas, currentIndex);
+ }
+
+
+ private void drawCircle(Canvas canvas, int i) {
+ float x = startX + radius + i * 2 * dis;
+ canvas.drawCircle(x, centerY, radius, circlePaint);
+ }
+
+
+ private void drawCurrentIndicator(Canvas canvas, int i) {
+ float x = startX + 2 * i * dis;
+ rectF.left = x;
+ rectF.top = centerY - radius;
+ rectF.right = x + 3 * dis;
+ rectF.bottom = centerY + radius;
+ canvas.drawRoundRect(rectF, radius, radius, indicatorPaint);
+ }
+
+
+ public IndicatorView setPointNum(int pointNum) {
+ this.pointNum = pointNum;
+ return this;
+ }
+
+ public IndicatorView setCurrentPosition(int position) {
+ currentIndex = position;
+ invalidate();
+ return this;
+ }
+
+ /**
+ * dp 2 px
+ *
+ * @param dpVal dp
+ */
+ protected int dp2px(int dpVal) {
+ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ dpVal, getResources().getDisplayMetrics());
+ }
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/StudyPlanProgressView.kt b/app/src/main/java/com/allen/androidcustomview/widget/StudyPlanProgressView.kt
new file mode 100644
index 0000000..468cf0f
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/StudyPlanProgressView.kt
@@ -0,0 +1,163 @@
+package com.allen.androidcustomview.widget
+
+import android.content.Context
+import android.graphics.*
+import android.graphics.drawable.BitmapDrawable
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.View
+import com.allen.androidcustomview.R
+import kotlin.math.min
+
+/**
+ *
+ * @author : Allen
+ * date : 2019/08/12
+ * desc : 学习计划view
+ *
+ */
+class StudyPlanProgressView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
+ private var mWidth = 0
+ private var mHeight = 0
+
+ private var iconPaint: Paint? = null
+ private var linePaint: Paint = Paint()
+ private var textPaint: Paint = Paint()
+ private var iconRect = Rect()
+ private var textRectF = Rect()
+
+ private val ALL_POINT_SIZE = 7
+ private var cellWidth = 0
+ private var iconSize = 0
+ private var textSize = 0
+ private var textColor = 0
+ private var progressWidth = 0
+ private var uncheckedProgressColor = Color.GRAY
+ private var checkedProgressColor = Color.YELLOW
+
+ private var iconUncheckedBitmapRes: Bitmap? = null
+ private var iconCheckedBitmapRes: Bitmap? = null
+
+ private var dates: MutableList = mutableListOf()
+
+ private var marginLeftAndRight = 0
+
+ init {
+ marginLeftAndRight = dp2px(27f).toInt()
+ iconSize = dp2px(14f).toInt()
+ initAttr(context, attrs, defStyleAttr)
+ initPaint()
+ }
+
+ private fun initAttr(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
+ val typedArray = context.obtainStyledAttributes(attrs, R.styleable.StudyPlanProgressView)
+ iconUncheckedBitmapRes = (typedArray.getDrawable(R.styleable.StudyPlanProgressView_sppv_iconUnchecked) as? BitmapDrawable)?.bitmap
+ iconCheckedBitmapRes = (typedArray.getDrawable(R.styleable.StudyPlanProgressView_sppv_iconChecked) as? BitmapDrawable)?.bitmap
+ iconSize = typedArray.getDimensionPixelOffset(R.styleable.StudyPlanProgressView_sppv_iconSize, dp2px(14f).toInt())
+ progressWidth = typedArray.getDimensionPixelOffset(R.styleable.StudyPlanProgressView_sppv_progressWidth, dp2px(2f).toInt())
+ uncheckedProgressColor = typedArray.getColor(R.styleable.StudyPlanProgressView_sppv_uncheckedProgressColor, uncheckedProgressColor)
+ checkedProgressColor = typedArray.getColor(R.styleable.StudyPlanProgressView_sppv_checkedProgressColor, checkedProgressColor)
+ textSize = typedArray.getDimensionPixelSize(R.styleable.StudyPlanProgressView_sppv_textSize, sp2px(11))
+ textColor = typedArray.getColor(R.styleable.StudyPlanProgressView_sppv_textColor, checkedProgressColor)
+
+ typedArray.recycle()
+ }
+
+ private fun initPaint() {
+ iconPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ iconPaint?.isFilterBitmap = true
+ iconPaint?.isDither = true
+
+ linePaint.strokeWidth = progressWidth.toFloat()
+ linePaint.color = uncheckedProgressColor
+ linePaint.isAntiAlias = true
+ linePaint.style = Paint.Style.FILL_AND_STROKE
+
+ textPaint.textSize = textSize.toFloat()
+ textPaint.color = textColor
+ textPaint.textAlign = Paint.Align.CENTER
+ textPaint.isAntiAlias = true
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ mWidth = w
+ mHeight = h
+
+ cellWidth = (mWidth - marginLeftAndRight * 2 - iconSize * ALL_POINT_SIZE) / (ALL_POINT_SIZE - 1)
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ drawProgressLine(canvas)
+ drawProgress(canvas)
+ }
+
+ private fun drawProgressLine(canvas: Canvas) {
+ val lineStart = (marginLeftAndRight + iconSize / 2).toFloat()
+ linePaint.color = uncheckedProgressColor
+ canvas.drawLine(lineStart, (mHeight / 2).toFloat(), (mWidth - marginLeftAndRight - iconSize / 2).toFloat(), (mHeight / 2).toFloat(), linePaint)
+ if (getDataSize() == 0) return
+ val lineEnd = lineStart + (getDataSize() - 1) * (cellWidth + iconSize)
+ linePaint.color = checkedProgressColor
+ canvas.drawLine(lineStart, (mHeight / 2).toFloat(), lineEnd, (mHeight / 2).toFloat(), linePaint)
+ }
+
+ private fun drawProgress(canvas: Canvas) {
+ if (iconUncheckedBitmapRes == null || iconCheckedBitmapRes == null) return
+ for (i in 0 until ALL_POINT_SIZE) {
+ val left = marginLeftAndRight + (cellWidth + iconSize) * i
+ iconRect.set(left, (mHeight - iconSize) / 2, left + iconSize, (mHeight + iconSize) / 2)
+ val bitmap = if (isFinished(i)) iconCheckedBitmapRes else iconUncheckedBitmapRes
+ canvas.drawBitmap(bitmap, null, iconRect, iconPaint)
+ drawText(canvas, i)
+ }
+ }
+
+ private fun isFinished(position: Int): Boolean {
+ return if (position <= getDataSize() - 1) {
+ dates[position].isFinished
+ } else false
+ }
+
+ private fun drawText(canvas: Canvas, position: Int) {
+ if (getDataSize() == 0) return
+ if (position < getDataSize()) {
+ val textWidth = textPaint.measureText(dates[position].content).toInt()
+ val isTop = position % 2 == 0
+ val left = marginLeftAndRight + iconSize / 2 + (cellWidth + iconSize) * position - textWidth / 2
+ textRectF.left = left
+ textRectF.right = left + textWidth
+ textRectF.top = if (isTop) 0 else (mHeight + iconSize) / 2
+ textRectF.bottom = if (isTop) (mHeight - iconSize) / 2 else mHeight
+
+ val fontMetrics = textPaint.fontMetricsInt
+ val baseline = (textRectF.bottom + textRectF.top - fontMetrics.bottom - fontMetrics.top) / 2
+ //文字绘制到整个布局的中心位置
+ canvas.drawText(dates[position].content, textRectF.centerX().toFloat(), baseline.toFloat(), textPaint)
+ }
+ }
+
+ fun setData(date: List?) {
+ dates.clear()
+ val timeList = date?.toMutableList() ?: mutableListOf()
+ timeList.forEach {
+ dates.add(ProgressData(it, true))
+ }
+ invalidate()
+ }
+
+ private fun getDataSize() = min(dates.size, 7)
+
+ fun dp2px(dpVal: Float): Float {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ dpVal, resources.displayMetrics)
+ }
+
+ fun sp2px(spVal: Int): Int {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+ spVal.toFloat(), resources.displayMetrics).toInt()
+ }
+
+ class ProgressData(var content: String, var isFinished: Boolean)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/SuperDividerItemDecoration.java b/app/src/main/java/com/allen/androidcustomview/widget/SuperDividerItemDecoration.java
new file mode 100644
index 0000000..a7ae5ca
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/SuperDividerItemDecoration.java
@@ -0,0 +1,255 @@
+package com.allen.androidcustomview.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2018/04/15
+ * desc : 万能分割线
+ * version : 1.0
+ *
+ */
+public class SuperDividerItemDecoration extends RecyclerView.ItemDecoration {
+
+
+ public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
+ public static final int VERTICAL = LinearLayout.VERTICAL;
+
+ private static Context context;
+
+
+ /**
+ * 默认分割线的颜色
+ */
+ private int dividerDefaultColor = 0xFFE1E5E8;
+
+ /**
+ * 分割线的颜色
+ */
+ private int dividerColor;
+ /**
+ * 分割线的宽度
+ */
+ private int dividerWidth;
+ /**
+ * 分割线距离左右两边的距离
+ */
+ private int dividerPadding;
+ /**
+ * 分割线距离左边的距离
+ */
+ private int dividerPaddingLeft;
+ /**
+ * 分割线距离右边的距离
+ */
+ private int dividerPaddingRight;
+
+ /**
+ * 分割线距离上边的距离
+ */
+ private int dividerPaddingTop;
+ /**
+ * 分割线距离下边的距离
+ */
+ private int dividerPaddingBottom;
+ /**
+ * 是否显示列表最后一条分割线
+ */
+ private boolean dividerIsShowLastDivide;
+
+
+ /**
+ * 分割线item的画笔
+ */
+ private Paint dividerPaint;
+
+ /**
+ * 分割线开始的位置(解决recyclerView添加头布局的时候,要从header下边的position位置算起)
+ */
+ private int dividerFromPosition = 0;
+
+
+ /**
+ * recyclerView布局方式(水平或者垂直)
+ */
+ private int orientation;
+
+
+ public SuperDividerItemDecoration(Builder builder) {
+
+ context = builder.context;
+
+ dividerColor = builder.dividerColor == 0 ? dividerDefaultColor : builder.dividerColor;
+ dividerPadding = dp2px(builder.dividerPadding);
+ dividerPaddingLeft = dp2px(builder.dividerPaddingLeft);
+ dividerPaddingRight = dp2px(builder.dividerPaddingRight);
+ dividerPaddingTop = dp2px(builder.dividerPaddingTop);
+ dividerPaddingBottom = dp2px(builder.dividerPaddingBottom);
+ dividerWidth = builder.dividerWidth == 0 ? dp2px(0.5f) : dp2px(builder.dividerWidth);
+ dividerFromPosition = builder.dividerFromPosition;
+ dividerIsShowLastDivide = builder.dividerIsShowLastDivide;
+ orientation = builder.orientation;
+ dividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ dividerPaint.setColor(dividerColor);
+
+ if (dividerPadding != 0) {
+ dividerPaddingLeft = dividerPaddingRight = dividerPadding;
+ dividerPaddingTop = dividerPaddingBottom = dividerPadding;
+ }
+ }
+
+ public static class Builder {
+
+ private Context context;
+ private int dividerColor;
+ private int dividerWidth;
+ private int dividerPadding;
+ private int dividerPaddingLeft;
+ private int dividerPaddingRight;
+ private int dividerPaddingTop;
+ private int dividerPaddingBottom;
+ private int dividerFromPosition;
+ private boolean dividerIsShowLastDivide;
+ private int orientation = VERTICAL;
+
+ public Builder(Context context) {
+ this.context = context;
+ }
+
+ public Builder setDividerColor(int dividerColor) {
+ this.dividerColor = dividerColor;
+ return this;
+ }
+
+ public Builder setDividerWidth(int dividerWidth) {
+ this.dividerWidth = dividerWidth;
+ return this;
+ }
+
+ public Builder setDividerPadding(int dividerPadding) {
+ this.dividerPadding = dividerPadding;
+ return this;
+ }
+
+ public Builder setDividerPaddingLeft(int dividerPaddingLeft) {
+ this.dividerPaddingLeft = dividerPaddingLeft;
+ return this;
+ }
+
+ public Builder setDividerPaddingRight(int dividerPaddingRight) {
+ this.dividerPaddingRight = dividerPaddingRight;
+ return this;
+ }
+
+ public Builder setDividerPaddingTop(int dividerPaddingTop) {
+ this.dividerPaddingTop = dividerPaddingTop;
+ return this;
+ }
+
+ public Builder setDividerPaddingBottom(int dividerPaddingBottom) {
+ this.dividerPaddingBottom = dividerPaddingBottom;
+ return this;
+ }
+
+ public Builder setDividerFromPosition(int dividerFromPosition) {
+ this.dividerFromPosition = dividerFromPosition;
+ return this;
+ }
+
+ public Builder setIsShowLastDivide(boolean dividerIsShowLastDivide) {
+ this.dividerIsShowLastDivide = dividerIsShowLastDivide;
+ return this;
+ }
+
+ public Builder setOrientation(int orientation) {
+ this.orientation = orientation;
+ return this;
+ }
+
+ public SuperDividerItemDecoration build() {
+ return new SuperDividerItemDecoration(this);
+ }
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ super.onDraw(c, parent, state);
+
+ if (orientation == VERTICAL) {
+ drawVertical(c, parent);
+ } else {
+ drawHorizontal(c, parent);
+ }
+
+ }
+
+
+ private void drawVertical(Canvas c, RecyclerView parent) {
+ int count = parent.getChildCount();
+
+ if (count > 0) {
+ int showCount = dividerIsShowLastDivide ? count : count - 1;
+ for (int i = dividerFromPosition; i < showCount; i++) {
+ View view = parent.getChildAt(i);
+ //可见item的底部
+ int itemBottom = view.getBottom();
+
+ c.drawRect(parent.getPaddingLeft() + dividerPaddingLeft,
+ itemBottom,
+ parent.getWidth() - parent.getPaddingRight() - dividerPaddingRight,
+ itemBottom + dividerWidth,
+ dividerPaint);
+ }
+ }
+ }
+
+ private void drawHorizontal(Canvas c, RecyclerView parent) {
+ int count = parent.getChildCount();
+ if (count > 0) {
+ int showCount = dividerIsShowLastDivide ? count : count - 1;
+ for (int i = dividerFromPosition; i < showCount; i++) {
+ View view = parent.getChildAt(i);
+ //可见item的底部
+ int itemRight = view.getRight();
+
+ c.drawRect(itemRight,
+ parent.getPaddingTop() + dividerPaddingTop,
+ itemRight + dividerWidth,
+ parent.getHeight() - parent.getPaddingBottom() - dividerPaddingBottom,
+ dividerPaint);
+ }
+ }
+ }
+
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+ super.getItemOffsets(outRect, view, parent, state);
+ if (orientation == VERTICAL) {
+ outRect.bottom = dividerWidth;
+ } else {
+ outRect.right = dividerWidth;
+ }
+ }
+
+ /**
+ * dp 2 px
+ *
+ * @param dpVal
+ */
+ public static int dp2px(float dpVal) {
+ return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ dpVal, context.getResources().getDisplayMetrics());
+ }
+
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/banner/BannerView.java b/app/src/main/java/com/allen/androidcustomview/widget/banner/BannerView.java
new file mode 100644
index 0000000..1a5048a
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/banner/BannerView.java
@@ -0,0 +1,173 @@
+package com.allen.androidcustomview.widget.banner;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import com.allen.androidcustomview.R;
+import com.allen.androidcustomview.widget.banner.adapter.BannerViewPagerAdapter;
+import com.allen.androidcustomview.widget.banner.holder.BannerViewHolderCreator;
+
+import java.util.List;
+
+/**
+ *
+ * @author : xiaoyao
+ * e-mail : xiaoyao@51vest.com
+ * date : 2018/03/02
+ * desc :
+ * version : 1.0
+ *
+ */
+
+public class BannerView extends RelativeLayout implements ViewPager.OnPageChangeListener {
+ private static final String TAG = BannerView.class.getSimpleName();
+
+ public ViewPager getViewPager() {
+ return mViewPager;
+ }
+
+ private ViewPager mViewPager;
+ private LinearLayout mIndicatorContainer;
+ private BannerViewPagerAdapter> mAdapter;
+
+ private PagerOptions mPagerOptions;
+ private ImageView mCurrentIndicator;
+
+ public BannerView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public BannerView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public BannerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ private void init(Context context) {
+ LayoutInflater.from(context).inflate(R.layout.banner_view_layout, this);
+ mViewPager = (ViewPager) findViewById(R.id.banner_view_pager);
+ mIndicatorContainer = (LinearLayout) findViewById(R.id.banner_indicator_container);
+
+ mPagerOptions = new PagerOptions.Builder(context).build();
+
+ mViewPager.setOffscreenPageLimit(4);
+ mViewPager.addOnPageChangeListener(this);
+ }
+
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ // 切换indicator
+ setIndicatorSelected(position);
+
+ setOnPageSelectedListener(position);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+
+
+ private void setOnPageSelectedListener(int position) {
+ if (mPagerOptions.mOnPageChangeListener != null) {
+ mPagerOptions.mOnPageChangeListener.onPageSelected(position);
+ }
+ }
+
+ /**
+ * 设置 PagerOptions
+ *
+ * @param options options
+ * @return BannerPager
+ */
+ public BannerView setPagerOptions(PagerOptions options) {
+ mPagerOptions = options;
+ return this;
+ }
+
+ /**
+ * 设置 page data
+ *
+ * @param data List
+ * @param creator BannerViewHolderCreator
+ */
+ public void setPages(@NonNull List> data, @NonNull BannerViewHolderCreator creator) {
+ if (data == null || creator == null) {
+ return;
+ }
+ mAdapter = new BannerViewPagerAdapter<>(data, creator);
+
+ initIndicator();
+ handlePagerOptions();
+
+ mViewPager.setAdapter(mAdapter);
+
+
+ }
+
+ private void initIndicator() {
+
+ int count = mAdapter.getRealCount();
+
+ mIndicatorContainer.removeAllViews();
+ ImageView indicator;
+ LinearLayout.LayoutParams layoutParams;
+
+ for (int i = 0; i < count; i++) {
+ layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ layoutParams.setMargins(10,0,10,0);
+ indicator = new ImageView(getContext());
+ indicator.setImageDrawable(mPagerOptions.mIndicatorDrawable[0]);
+ indicator.setLayoutParams(layoutParams);
+ mIndicatorContainer.addView(indicator);
+ }
+ setIndicatorSelected(mViewPager.getCurrentItem());
+
+ }
+
+ private void setIndicatorSelected(int currentItem) {
+ if (mCurrentIndicator != null) {
+ mCurrentIndicator.setImageDrawable(mPagerOptions.mIndicatorDrawable[0]);
+ mCurrentIndicator.setSelected(false);
+ }
+ if (mIndicatorContainer.getChildCount()>0){
+ final ImageView indicator = (ImageView) mIndicatorContainer.getChildAt(currentItem);
+ indicator.setSelected(true);
+ indicator.setImageDrawable(mPagerOptions.mIndicatorDrawable[1]);
+ mCurrentIndicator = indicator;
+ }
+ }
+
+ private void handlePagerOptions() {
+ //设置每页之间间距
+ mViewPager.setPageMargin(mPagerOptions.mPageMargin);
+ //设置预显示宽
+ final ViewGroup.MarginLayoutParams mp = (MarginLayoutParams) mViewPager.getLayoutParams();
+ mp.leftMargin = mp.rightMargin = mPagerOptions.mPrePagerWidth;
+ mViewPager.setLayoutParams(mp);
+
+ //设置切换效果
+ mViewPager.setPageTransformer(true, mPagerOptions.mPageTransformer);
+ mAdapter.setPageClickListener(mPagerOptions.mOnPageClickListener);
+
+ }
+
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/banner/PagerOptions.java b/app/src/main/java/com/allen/androidcustomview/widget/banner/PagerOptions.java
new file mode 100644
index 0000000..9a68aee
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/banner/PagerOptions.java
@@ -0,0 +1,259 @@
+package com.allen.androidcustomview.widget.banner;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.support.annotation.ColorInt;
+import android.support.annotation.DrawableRes;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewPager;
+import android.util.TypedValue;
+
+import com.allen.androidcustomview.widget.banner.listener.OnPageChangeListener;
+import com.allen.androidcustomview.widget.banner.listener.OnPageClickListener;
+
+/**
+ *
+ * @author : xiaoyao
+ * e-mail : xiaoyao@51vest.com
+ * date : 2018/03/02
+ * desc :
+ * version : 1.0
+ *
+ */
+
+public class PagerOptions {
+
+
+ int mPageMargin;
+ int mPrePagerWidth;
+ int mIndicatorVisibility;
+ Drawable[] mIndicatorDrawable;
+ int mIndicatorDistance;
+ boolean mLoopEnable;
+ int mDelayedTime;
+ int mIndicatorAlign;
+ ViewPager.PageTransformer mPageTransformer;
+ int mScrollDuration;
+ int mIndicatorMarginBottom;
+ int mIndicatorSize;
+ OnPageClickListener mOnPageClickListener;
+ OnPageChangeListener mOnPageChangeListener;
+
+ private PagerOptions() {
+ }
+
+ public static class Builder {
+
+ private Context mContext;
+ private int mPageMargin;
+ private int mPrePagerWidth;
+ private int mIndicatorAlign;
+ private int mIndicatorVisibility;
+ private Drawable[] mIndicatorDrawable = new Drawable[2];
+ private int mIndicatorDistance = 8;
+ private boolean mLoopEnable = true;
+ private ViewPager.PageTransformer mPageTransformer;
+ private int mDelayedTime = 3000;
+ private int mScrollDuration = 800;
+ private int mIndicatorMarginBottom = -1;
+ private int mIndicatorSize = -1;
+ private OnPageClickListener mOnPageClickListener;
+ private OnPageChangeListener mOnPageChangeListener;
+
+ public Builder(Context context) {
+ mContext = context;
+ //设置默认指示器
+// setIndicatorDrawable(R.drawable.indicator_normal_default, R.drawable.indicator_selected_default);
+ }
+
+
+ /**
+ * 设置每个 page 之间间隔
+ *
+ * @param px px value
+ * @return Builder
+ */
+ public Builder setPageMargin(int px) {
+ mPageMargin = px;
+ return this;
+ }
+
+ /**
+ * 左右两侧预显示宽度
+ *
+ * @param px px value
+ * @return Builder
+ */
+ public Builder setPrePagerWidth(int px) {
+ mPrePagerWidth = px;
+ return this;
+ }
+
+ /**
+ * 设置指示器间距
+ *
+ * @param distance px value
+ * @return Builder
+ */
+ public Builder setIndicatorDistance(int distance) {
+ mIndicatorDistance = distance;
+ return this;
+ }
+
+ /**
+ * 设置指示器距离底部间距
+ *
+ * @param marginBottom marginBottom
+ * @return Builder
+ */
+ public Builder setIndicatorMarginBottom(int marginBottom) {
+ mIndicatorMarginBottom = marginBottom;
+ return this;
+ }
+
+ /**
+ * 设置指示器位置
+ *
+ * @param align RelativeLayout.ALIGN_PARENT_LEFT || RelativeLayout.CENTER_IN_PARENT || RelativeLayout.ALIGN_PARENT_RIGHT
+ * @return Builder
+ */
+ public Builder setIndicatorAlign(int align) {
+ mIndicatorAlign = align;
+ return this;
+ }
+
+ /**
+ * 设置Indicator 是否可见
+ *
+ * @param visibility One of VISIBLE, INVISIBLE, GONE.
+ * @return Builder
+ */
+ public Builder setIndicatorVisibility(int visibility) {
+ mIndicatorVisibility = visibility;
+ return this;
+ }
+
+ /**
+ * 设置轮播切换效果
+ *
+ * @param transformer PageTransformer
+ * @return Builder
+ */
+ public Builder setPageTransformer(ViewPager.PageTransformer transformer) {
+ mPageTransformer = transformer;
+ return this;
+ }
+
+ /**
+ * 设置指示器
+ *
+ * @param unSelected 未选中
+ * @param selected 选中
+ * @return Builder
+ */
+ public Builder setIndicatorDrawable(@DrawableRes int unSelected, @DrawableRes int selected) {
+ mIndicatorDrawable[0] = ContextCompat.getDrawable(mContext, unSelected);
+ mIndicatorDrawable[1] = ContextCompat.getDrawable(mContext, selected);
+ return this;
+ }
+
+ /**
+ * 设置指示器
+ *
+ * @param unSelected 未选中
+ * @param selected 选中
+ * @return Builder
+ */
+ public Builder setIndicatorColor(@ColorInt int unSelected, @ColorInt int selected) {
+ mIndicatorDrawable[0] = createDrawable(unSelected);
+ mIndicatorDrawable[1] = createDrawable(selected);
+ return this;
+ }
+
+ private Drawable createDrawable(@ColorInt int color) {
+ final int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, mContext.getResources().getDisplayMetrics());
+ final GradientDrawable gd = new GradientDrawable();
+ gd.setColor(color);
+ gd.setShape(GradientDrawable.OVAL);
+ gd.setSize(size, size);
+ return gd;
+ }
+
+ /**
+ * 设置指示器
+ *
+ * @param size px
+ * @return Builder
+ */
+ public Builder setIndicatorSize(int size) {
+ mIndicatorSize = size;
+ return this;
+ }
+
+ /**
+ * 设置可否循环
+ *
+ * @param loop loop
+ * @return Builder
+ */
+ public Builder setLoopEnable(boolean loop) {
+ mLoopEnable = loop;
+ return this;
+ }
+
+ /**
+ * 设置切换时间
+ *
+ * @param duration ms
+ * @return Builder
+ */
+ public Builder setTurnDuration(int duration) {
+ mDelayedTime = duration;
+ return this;
+ }
+
+ /**
+ * 设置ViewPager的滚动速度
+ *
+ * @param duration ms
+ */
+ public Builder setScrollDuration(int duration) {
+ mScrollDuration = duration;
+ return this;
+ }
+
+
+ public Builder setOnPageClickListener(OnPageClickListener onPageClickListener) {
+ this.mOnPageClickListener = onPageClickListener;
+ return this;
+ }
+
+ public Builder setOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
+ this.mOnPageChangeListener = onPageChangeListener;
+ return this;
+ }
+
+ public PagerOptions build() {
+ final PagerOptions options = new PagerOptions();
+
+ options.mPageMargin = mPageMargin;
+ options.mPrePagerWidth = mPrePagerWidth;
+ options.mIndicatorDistance = mIndicatorDistance;
+ options.mIndicatorDrawable = mIndicatorDrawable;
+ options.mIndicatorSize = mIndicatorSize;
+ options.mIndicatorAlign = mIndicatorAlign;
+ options.mIndicatorVisibility = mIndicatorVisibility;
+ options.mLoopEnable = mLoopEnable;
+ options.mPageTransformer = mPageTransformer;
+ options.mDelayedTime = mDelayedTime;
+ options.mScrollDuration = mScrollDuration;
+ options.mIndicatorMarginBottom = mIndicatorMarginBottom;
+ options.mOnPageClickListener = mOnPageClickListener;
+ options.mOnPageChangeListener = mOnPageChangeListener;
+ mContext = null;
+ return options;
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/banner/adapter/BannerViewPagerAdapter.java b/app/src/main/java/com/allen/androidcustomview/widget/banner/adapter/BannerViewPagerAdapter.java
new file mode 100644
index 0000000..2a14a6d
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/banner/adapter/BannerViewPagerAdapter.java
@@ -0,0 +1,122 @@
+package com.allen.androidcustomview.widget.banner.adapter;
+
+import android.support.annotation.NonNull;
+import android.support.v4.view.PagerAdapter;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.allen.androidcustomview.widget.banner.listener.OnPageClickListener;
+import com.allen.androidcustomview.widget.banner.holder.BannerViewHolder;
+import com.allen.androidcustomview.widget.banner.holder.BannerViewHolderCreator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author : xiaoyao
+ * e-mail : xiaoyao@51vest.com
+ * date : 2018/03/02
+ * desc :
+ * version : 1.0
+ *
+ */
+
+public class BannerViewPagerAdapter extends PagerAdapter {
+
+ private List mData;
+ private BannerViewHolderCreator mCreator;
+ private OnPageClickListener mPageClickListener;
+
+ public BannerViewPagerAdapter(@NonNull List data, @NonNull BannerViewHolderCreator creator) {
+
+ if (mData == null) {
+ mData = new ArrayList<>();
+ }
+ mData = data;
+ mCreator = creator;
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return view == object;
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, final int position) {
+ View view = getView(position, container);
+ container.addView(view);
+ return view;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ container.removeView((View) object);
+ }
+
+
+
+ /**
+ * 获取真实的Count
+ *
+ * @return
+ */
+ public int getRealCount() {
+ return mData == null ? 0 : mData.size();
+ }
+
+ @Override
+ public int getCount() {
+ return getRealCount();
+ }
+
+ /**
+ * @param position
+ * @param container
+ * @return
+ */
+ private View getView(int position, ViewGroup container) {
+
+ final int realPosition = position % getRealCount();
+ BannerViewHolder holder = null;
+ // create holder
+ holder = mCreator.createViewHolder();
+
+ if (holder == null) {
+ throw new RuntimeException("can not return a null holder");
+ }
+ // create View
+ View view = holder.createView(container.getContext());
+
+ if (mData != null && mData.size() > 0) {
+ holder.onBind(container.getContext(), realPosition, mData.get(realPosition));
+ }
+
+ // 添加page点击事件
+ if (view != null) {
+ view.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mPageClickListener != null) {
+ mPageClickListener.onPageClick(v, realPosition);
+ }
+ }
+ });
+ }
+
+ return view;
+ }
+
+
+ public List getData() {
+ return mData;
+ }
+
+ public void setData(List mData) {
+ this.mData = mData;
+ }
+
+ public void setPageClickListener(OnPageClickListener pageClickListener) {
+ this.mPageClickListener = pageClickListener;
+ }
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/banner/holder/BannerViewHolder.java b/app/src/main/java/com/allen/androidcustomview/widget/banner/holder/BannerViewHolder.java
new file mode 100644
index 0000000..35165dd
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/banner/holder/BannerViewHolder.java
@@ -0,0 +1,33 @@
+package com.allen.androidcustomview.widget.banner.holder;
+
+import android.content.Context;
+import android.view.View;
+
+/**
+ *
+ * @author : xiaoyao
+ * e-mail : xiaoyao@51vest.com
+ * date : 2018/03/02
+ * desc :
+ * version : 1.0
+ *
+ */
+
+public interface BannerViewHolder {
+
+ /**
+ * 创建View
+ * @param context
+ * @return
+ */
+ View createView(Context context);
+
+ /**
+ * 绑定数据
+ * @param context
+ * @param position
+ * @param data
+ */
+ void onBind(Context context, int position, T data);
+
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/banner/holder/BannerViewHolderCreator.java b/app/src/main/java/com/allen/androidcustomview/widget/banner/holder/BannerViewHolderCreator.java
new file mode 100644
index 0000000..5192566
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/banner/holder/BannerViewHolderCreator.java
@@ -0,0 +1,20 @@
+package com.allen.androidcustomview.widget.banner.holder;
+
+/**
+ *
+ * @author : xiaoyao
+ * e-mail : xiaoyao@51vest.com
+ * date : 2018/03/02
+ * desc :
+ * version : 1.0
+ *
+ */
+
+public interface BannerViewHolderCreator {
+ /**
+ * 创建 BannerViewHolder
+ *
+ * @return BannerViewHolder
+ */
+ VH createViewHolder();
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/banner/listener/OnPageChangeListener.java b/app/src/main/java/com/allen/androidcustomview/widget/banner/listener/OnPageChangeListener.java
new file mode 100644
index 0000000..521f52d
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/banner/listener/OnPageChangeListener.java
@@ -0,0 +1,21 @@
+package com.allen.androidcustomview.widget.banner.listener;
+
+/**
+ *
+ * @author : xiaoyao
+ * e-mail : xiaoyao@51vest.com
+ * date : 2018/03/09
+ * desc :
+ * version : 1.0
+ *
+ */
+
+public interface OnPageChangeListener {
+
+ /**
+ * item 选中事件
+ *
+ * @param position position
+ */
+ void onPageSelected(int position);
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/banner/listener/OnPageClickListener.java b/app/src/main/java/com/allen/androidcustomview/widget/banner/listener/OnPageClickListener.java
new file mode 100644
index 0000000..e1ac8cd
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/banner/listener/OnPageClickListener.java
@@ -0,0 +1,25 @@
+package com.allen.androidcustomview.widget.banner.listener;
+
+import android.view.View;
+
+/**
+ *
+ * @author : xiaoyao
+ * e-mail : xiaoyao@51vest.com
+ * date : 2018/03/02
+ * desc :
+ * version : 1.0
+ *
+ */
+
+public interface OnPageClickListener {
+
+ /**
+ * item 点击事件
+ *
+ * @param view view
+ * @param position position
+ */
+ void onPageClick(View view, int position);
+
+}
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/status/StatusBuilder.kt b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusBuilder.kt
new file mode 100644
index 0000000..a036c92
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusBuilder.kt
@@ -0,0 +1,50 @@
+package com.allen.androidcustomview.widget.status
+
+import android.view.View
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2019/03/19
+ * desc :
+ *
+ */
+class StatusBuilder {
+
+ class Builder{
+
+ var contentView: View? = null
+ var errorView: View? = null
+ var emptyView: View? = null
+ var loadingView: View? = null
+
+ fun setContentView(contentView: View):Builder {
+ this.contentView = contentView
+ return this
+ }
+ fun setContentView(layoutResId: Int):Builder {
+ this.contentView = contentView
+ return this
+ }
+
+ fun setErrorView(errorView: View):Builder {
+ this.errorView = errorView
+ return this
+ }
+
+ fun setEmptyView(emptyView: View):Builder {
+ this.emptyView = emptyView
+ return this
+ }
+
+ fun setLoadingView(loadingView: View):Builder {
+ this.loadingView = loadingView
+ return this
+ }
+
+ fun build(){
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/status/StatusLayout.kt b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusLayout.kt
new file mode 100644
index 0000000..470b9bc
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusLayout.kt
@@ -0,0 +1,27 @@
+package com.allen.androidcustomview.widget.status
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2019/03/19
+ * desc : 状态布局
+ *
+ */
+
+class StatusLayout : FrameLayout {
+
+ constructor(context: Context) : this(context, null)
+ constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
+ constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
+ initAttrs(attrs)
+ }
+
+ private fun initAttrs(attrs: AttributeSet?) {
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/status/StatusManager.kt b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusManager.kt
new file mode 100644
index 0000000..383baf7
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/status/StatusManager.kt
@@ -0,0 +1,29 @@
+package com.allen.androidcustomview.widget.status
+
+/**
+ *
+ * @author : Allen
+ * e-mail : lygttpod@163.com
+ * date : 2019/03/19
+ * desc :
+ *
+ */
+object StatusManager {
+
+
+ fun setEmptyView(){
+
+ }
+
+ fun setErrorView(){
+
+ }
+
+ fun setContentView() {
+
+ }
+
+ fun setLoadingView() {
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/vote/VoteContainerView.kt b/app/src/main/java/com/allen/androidcustomview/widget/vote/VoteContainerView.kt
new file mode 100644
index 0000000..9cf7f2c
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/vote/VoteContainerView.kt
@@ -0,0 +1,283 @@
+package com.allen.androidcustomview.widget.vote
+
+import android.content.Context
+import android.graphics.Outline
+import android.os.Build
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewOutlineProvider
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import android.widget.Toast
+import com.allen.androidcustomview.R
+import com.allen.androidcustomview.bean.VoteBean
+import com.allen.androidcustomview.bean.VoteOption
+import kotlinx.android.synthetic.main.widget_vote_layout.view.*
+import java.lang.ref.WeakReference
+
+/**
+ *
+ * @author : Allen
+ * date : 2019/08/01
+ * desc :
+ *
+ */
+class VoteContainerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : FrameLayout(context, attrs, defStyleAttr) {
+
+ private var voteViewHolders: ArrayList = arrayListOf()
+
+ var onVoteClickListener: OnVoteClickListener? = null
+
+ companion object {
+ val VOTE_TYPE_MULTIPLE = "multiple"
+ val VOTE_TYPE_SINGLE = "single"
+ }
+
+ private var optionIds = arrayListOf()
+
+ private var mData: VoteBean? = null
+
+ init {
+ initView()
+ }
+
+ private fun initView() {
+ LayoutInflater.from(context).inflate(R.layout.widget_vote_layout, this)
+
+ vote_container_vote_btn.setOnClickListener {
+ if (mData == null) return@setOnClickListener
+ onVoteClickListener?.onVoteCommitBtnClick(mData, optionIds)
+ }
+ }
+
+ fun setVoteData(data: VoteBean?) {
+ if (data == null || data.options.isNullOrEmpty()) {
+ visibility = View.GONE
+ } else {
+ mData = data
+ setVoteTitle(data.title)
+ voteViewHolders.clear()
+ optionIds.clear()
+ setVoteStatus(data)
+ setVoteBtnStatus()
+ vote_item_ll.removeAllViews()
+ data.options?.forEachIndexed { index, voteOption ->
+ val viewHolder = onCreateViewHolder()
+ viewHolder.bind(index, voteOption, data)
+ voteViewHolders.add(viewHolder)
+ vote_item_ll.addView(viewHolder.voteView)
+ }
+ }
+ }
+
+ private fun setVoteTitle(title: String?) {
+ vote_container_title.text = title ?: ""
+ }
+
+ private fun setVoteStatus(vote: VoteBean) {
+ val isVoteMulti = vote.choiceType == VOTE_TYPE_MULTIPLE
+ val voteResult = vote.sumVoteCount ?: 0
+ val voted = vote.voted
+ vote_container_vote_btn.visibility = if (voted != true && isVoteMulti) View.VISIBLE else View.GONE
+ vote_container_vote_result.visibility = if (voted == true) View.VISIBLE else View.GONE
+ setVoteResult("共${voteResult}人参与了投票")
+ }
+
+ private fun addOptionIds(id: Int) {
+ optionIds.add(id)
+ }
+
+
+ private fun removeOptionIds(id: Int) {
+ optionIds.remove(id)
+ }
+
+ private fun getOptionIdsSize(): Int {
+ return optionIds.size
+ }
+
+ private fun setVoteBtnStatus() {
+ val clickable = getOptionIdsSize() > 0
+ if (clickable) {
+ vote_container_vote_btn.setBackgroundResource(R.drawable.shape_bg_clickable)
+ } else {
+ vote_container_vote_btn.setBackgroundResource(R.drawable.shape_bg_un_clickable)
+ }
+ vote_container_vote_btn.isClickable = clickable
+ }
+
+ fun refreshDataAfterVoteSuccess() {
+ vote_container_vote_result.visibility = View.VISIBLE
+ vote_container_vote_btn.visibility = View.GONE
+ refreshVoteResult()
+ startProgressAnim()
+ }
+
+ fun refreshDataAfterVoteFailed() {
+ voteViewHolders.forEach {
+ it.resetDataAfterSingleVoteFailed()
+ }
+ }
+
+ fun onDestroy() {
+ voteViewHolders.forEach {
+ it.onVoteDestroy()
+ }
+ }
+
+ private fun refreshVoteResult() {
+ val voteResult = (mData?.sumVoteCount ?: 0)
+ setVoteResult("共${(voteResult + 1)}人参与了投票")
+ }
+
+ private fun setVoteResult(result: String?) {
+ vote_container_vote_result.text = result ?: ""
+ }
+
+ private fun startProgressAnim() {
+ voteViewHolders.forEach {
+ it.setProgress()
+ }
+ }
+
+ private fun onCreateViewHolder(): VoteItemViewHolder {
+ return VoteItemViewHolder(getVoteView(context), this)
+ }
+
+ class VoteItemViewHolder(var voteView: VoteView, voteContainerView: VoteContainerView) {
+
+ private val voteContainerViewRef = WeakReference(voteContainerView)
+
+ private fun ref(): VoteContainerView? {
+ return voteContainerViewRef.get()
+ }
+
+ private var data: VoteOption? = null
+ private var mainVote: VoteBean? = null
+
+ var isVoteMulti = true
+
+ fun bind(position: Int, voteOption: VoteOption, mainVote: VoteBean) {
+ this.data = voteOption
+ this.mainVote = mainVote
+
+ isVoteMulti = mainVote.choiceType == VOTE_TYPE_MULTIPLE
+
+ val voteResultCount = voteOption.showCount ?: 0
+
+ voteView.setVoteIsSelected(voteOption.voted ?: false)
+ .setVoteContent(voteOption.content)
+ .setVoteResultText("${voteResultCount}人").refreshView()
+
+ if (isHaveVoted()) {
+ val sum = mainVote?.sumVoteCount ?: 0
+ val showCount = data?.showCount ?: 0
+ val progress = if (sum == 0) 0f else showCount.toFloat() / sum.toFloat()
+
+ voteView.setProgress(progress)
+ }
+
+ voteView.setOnClickListener {
+ if (isHaveVoted()) return@setOnClickListener
+ if (isVoteMulti) {
+ setMultiChoice(voteView, voteOption)
+ ref()?.onVoteClickListener?.onVoteItemClick(mainVote, data)
+ } else {
+ if (ref()?.getOptionIdsSize() ?: 0 > 0) return@setOnClickListener
+ setSingleChoice(voteView, voteOption)
+ ref()?.onVoteClickListener?.onVoteItemClick(mainVote, data)
+ }
+ }
+
+ }
+
+ private fun isHaveVoted(): Boolean {
+ return mainVote?.voted ?: false
+ }
+
+ fun setProgress() {
+ val sum = mainVote?.sumVoteCount ?: 0
+ var showCount = data?.showCount ?: 0
+ val realShowCount = if (data?.voted == true) showCount + 1 else showCount
+ voteView.setVoteResultText("${realShowCount}人")
+ mainVote?.voted = true
+ val progress = if (sum == 0) 0f else realShowCount.toFloat() / sum.toFloat()
+ voteView.setProgressWithAnim(progress)
+ }
+
+ fun resetDataAfterSingleVoteFailed() {
+ if (isVoteMulti) return
+ data?.voted = false
+ ref()?.removeOptionIds(data?.id ?: 0)
+ voteView.setVoteIsSelected(data?.voted ?: false).refreshView()
+ }
+
+ fun onVoteDestroy() {
+ voteView.onDestroy()
+ }
+
+ private fun setMultiChoice(voteView: VoteView, voteOption: VoteOption) {
+ if (voteOption.voted == true) {
+ voteOption.voted = false
+ ref()?.removeOptionIds(voteOption.id ?: 0)
+ } else {
+ val optionsIdSize = ref()?.optionIds?.size ?: 0
+ val maxSelect = mainVote?.maxSelect ?: 0
+ if (optionsIdSize < maxSelect) {
+ voteOption.voted = true
+ ref()?.addOptionIds(voteOption.id ?: 0)
+ } else {
+ Toast.makeText(ref()?.context, "最多可选${maxSelect}个", Toast.LENGTH_SHORT).show()
+ }
+ }
+ ref()?.setVoteBtnStatus()
+ voteView.setVoteIsSelected(voteOption.voted ?: false).refreshView()
+ }
+
+ private fun setSingleChoice(voteView: VoteView, voteOption: VoteOption) {
+ voteOption.voted = true
+ ref()?.addOptionIds(voteOption.id ?: 0)
+ voteView.setVoteIsSelected(voteOption.voted ?: false).refreshView()
+ ref()?.onVoteClickListener?.onVoteCommitBtnClick(mainVote, ref()?.optionIds
+ ?: arrayListOf())
+ }
+
+ }
+
+ private fun getVoteView(context: Context): VoteView {
+ val voteView = VoteView(context)
+ voteView.setVoteTextSize(voteView.sp2px(15))
+ .setVoteUncheckedContentTextColor(resources.getColor(R.color.unchecked_content_text_color))
+ .setVoteCheckedContentTextColor(resources.getColor(R.color.checked_content_text_color))
+ .setVoteUncheckedResultTextColor(resources.getColor(R.color.unchecked_result_text_color))
+ .setVoteCheckedResultTextColor(resources.getColor(R.color.checked_result_text_color))
+ .setVoteUncheckedProgressColor(resources.getColor(R.color.unchecked_progress_color))
+ .setVoteCheckedProgressColor(resources.getColor(R.color.checked_progress_color))
+ .setVoteCheckedIcon(resources.getDrawable(R.mipmap.icon_vote_check))
+ .setVoteBorderRadius(voteView.dp2px(3f))
+ .setVoteBorderColor(resources.getColor(R.color.border_color))
+ .setVoteRightIconSize(voteView.dp2px(18f).toInt())
+ .setVoteAnimDuration(2000L)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ voteView.clipToOutline = true
+ voteView.outlineProvider = object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ outline.setRoundRect(0, 0, view.width, view.height, voteView.dp2px(3f))
+ }
+ }
+ }
+
+ val layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, voteView.dp2px(40f).toInt())
+ layoutParams.bottomMargin = voteView.dp2px(12f).toInt()
+ voteView.layoutParams = layoutParams
+ return voteView
+ }
+
+
+ interface OnVoteClickListener {
+ fun onVoteCommitBtnClick(mainVote: VoteBean?, optionIds: ArrayList)
+ fun onVoteItemClick(mainVote: VoteBean?, voteOption: VoteOption?)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/vote/VoteLayoutAdapter.kt b/app/src/main/java/com/allen/androidcustomview/widget/vote/VoteLayoutAdapter.kt
new file mode 100644
index 0000000..95a7eca
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/vote/VoteLayoutAdapter.kt
@@ -0,0 +1,89 @@
+package com.allen.androidcustomview.widget.vote
+
+import android.view.View
+import android.view.ViewGroup
+import com.allen.androidcustomview.bean.VoteBean
+import com.allen.androidcustomview.bean.VoteOption
+import java.lang.ref.WeakReference
+
+/**
+ *
+ * @author : Allen
+ * date : 2019/08/03
+ * desc :
+ *
+ */
+class VoteLayoutAdapter(private val viewGroup: ViewGroup) {
+
+ private var viewHolders = mutableListOf()
+
+ var onVoteClickListener: OnVoteClickListener? = null
+
+ fun setData(vote: ArrayList?) {
+ viewGroup.removeAllViews()
+ viewHolders.clear()
+ if (vote == null || vote.size <= 0) {
+ viewGroup.visibility = View.GONE
+ } else {
+ viewGroup.visibility = View.VISIBLE
+ val size = vote.size
+ for (i in 0 until size) {
+ val viewHolder = onCreateViewHolder(viewGroup, i)
+ viewHolder.bind(vote[i])
+ viewHolders.add(viewHolder)
+ viewGroup.addView(viewHolder.voteContainerView)
+ }
+ }
+ }
+
+ fun refreshDataAfterVotedSuccess(position: Int) {
+ viewHolders[position].voteContainerView.refreshDataAfterVoteSuccess()
+ }
+
+ fun refreshDataAfterVotedFailed(position: Int) {
+ viewHolders[position].voteContainerView.refreshDataAfterVoteFailed()
+ }
+
+ fun onDestroy() {
+ viewHolders.forEach {
+ it.voteContainerView.onDestroy()
+ }
+ }
+
+ private fun onCreateViewHolder(viewGroup: ViewGroup, position: Int): VoteViewHolder {
+ val view = VoteContainerView(viewGroup.context)
+ return VoteViewHolder(view, this, position)
+ }
+
+ class VoteViewHolder(view: VoteContainerView, adapter: VoteLayoutAdapter, var position: Int) {
+
+ private val adapterRef = WeakReference(adapter)
+ private fun ref(): VoteLayoutAdapter? {
+ return adapterRef.get()
+ }
+
+ var voteContainerView = view
+ private var mMainVote: VoteBean? = null
+
+ fun bind(mainVote: VoteBean) {
+ mMainVote = mainVote
+ voteContainerView.setVoteData(mainVote)
+ voteContainerView.onVoteClickListener = object : VoteContainerView.OnVoteClickListener {
+ override fun onVoteCommitBtnClick(mainVote: VoteBean?, optionIds: ArrayList) {
+ ref()?.onVoteClickListener?.onVoteCommitBtnClick(mainVote, optionIds, position)
+ }
+
+ override fun onVoteItemClick(mainVote: VoteBean?, voteOption: VoteOption?) {
+ ref()?.onVoteClickListener?.onVoteItemClick(mainVote, voteOption, position)
+ }
+
+ }
+ }
+
+ }
+
+ interface OnVoteClickListener {
+ fun onVoteCommitBtnClick(mainVote: VoteBean?, optionIds: ArrayList, position: Int)
+ fun onVoteItemClick(mainVote: VoteBean?, voteOption: VoteOption?, position: Int)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/allen/androidcustomview/widget/vote/VoteView.kt b/app/src/main/java/com/allen/androidcustomview/widget/vote/VoteView.kt
new file mode 100644
index 0000000..7a0845c
--- /dev/null
+++ b/app/src/main/java/com/allen/androidcustomview/widget/vote/VoteView.kt
@@ -0,0 +1,419 @@
+package com.allen.androidcustomview.widget.vote
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.*
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.View
+import android.view.animation.DecelerateInterpolator
+import com.allen.androidcustomview.R
+import kotlin.math.max
+
+/**
+ *
+ * @author : Allen
+ * date : 2019/07/30
+ * desc :
+ *
+ */
+class VoteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
+
+ private var mWidth = 0
+ private var mHeight = 0
+
+ private var bgRectF = RectF()
+ private var progressRectF = RectF()
+ private var voteContentRectF = Rect()
+ private var voteResultRectF = Rect()
+ private var voteRightIconRectF = Rect()
+
+ private var voteResultBaseline = 0
+ private var voteContentBaseline = 0
+
+ private var progressPaint: Paint? = null
+ private var iconPaint: Paint? = null
+ private var bgPaint: Paint? = null
+ private var borderPaint: Paint? = null
+
+ private var voteContentTextPaint: Paint? = null
+ private var voteResultTextPaint: Paint? = null
+
+ private var animDuration = 1000L
+
+ private var mScale = 1f
+
+ private var mProgress = -1f
+ private var mVoteContent: String? = null
+ private var mVoteResult: String? = null
+
+ private var valueAnimator: ValueAnimator? = null
+
+ private var textMarginLeft = 0
+ private var voteResultMarginRight = 0
+
+ private var textPaintSize: Int = 0
+
+ private var rightCheckedBitmapRes: Bitmap? = null
+
+ private var rightIconWidth = 0
+ private var rightIconHeight = 0
+
+ private var checkedProgressColor = 0
+ private var unCheckedProgressColor = 0
+
+ private var checkedContentTextColor = 0
+ private var uncheckedContentTextColor = 0
+
+ private var checkedResultTextColor = 0
+ private var uncheckedResultTextColor = 0
+
+ private var borderColor = 0
+ private var borderRadius = 0f
+
+ private var isVoteChecked = false
+ private var textWidth = 0
+
+ private val defaultCheckedProgressColor = Color.argb(1, 255, 124, 5)
+ private val defaultUncheckedProgressColor = Color.parseColor("#F3F3F3")
+ private val defaultCheckedTextColor = Color.parseColor("#FF7C05")
+ private val defaultUncheckedTextColor = Color.parseColor("#1a1a1a")
+ private val defaultBorderColor = Color.parseColor("#e6e6e6")
+
+ init {
+
+ textMarginLeft = dp2px(15f).toInt()
+ voteResultMarginRight = dp2px(15f).toInt()
+
+ initAttr(context, attrs, defStyleAttr)
+
+ initPaint()
+
+ initVoteRightIcon()
+
+ initColor()
+ }
+
+ private fun initColor() {
+ voteContentTextPaint?.color = if (isVoteChecked) checkedContentTextColor else uncheckedContentTextColor
+ voteResultTextPaint?.color = if (isVoteChecked) checkedResultTextColor else uncheckedResultTextColor
+ progressPaint?.color = if (isVoteChecked) checkedProgressColor else unCheckedProgressColor
+ bgPaint?.color = if (isVoteChecked) checkedProgressColor else unCheckedProgressColor
+ }
+
+ private fun initVoteRightIcon() {
+ if (rightCheckedBitmapRes != null) {
+ if (rightIconWidth == 0 || rightIconHeight == 0) {
+ rightIconWidth = dp2px(36f).toInt()
+ rightIconHeight = dp2px(36f).toInt()
+ }
+ }
+ }
+
+ private fun initAttr(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
+ val typedArray = context.obtainStyledAttributes(attrs, R.styleable.VoteView)
+
+ checkedProgressColor = typedArray.getColor(R.styleable.VoteView_voteCheckedProgressColor, defaultCheckedProgressColor)
+ unCheckedProgressColor = typedArray.getColor(R.styleable.VoteView_voteUncheckedProgressColor, defaultUncheckedProgressColor)
+
+ checkedContentTextColor = typedArray.getColor(R.styleable.VoteView_voteCheckedContentTextColor, defaultCheckedTextColor)
+ uncheckedContentTextColor = typedArray.getColor(R.styleable.VoteView_voteUncheckedContentTextColor, defaultUncheckedTextColor)
+
+ checkedResultTextColor = typedArray.getColor(R.styleable.VoteView_voteCheckedResultTextColor, defaultCheckedTextColor)
+ uncheckedResultTextColor = typedArray.getColor(R.styleable.VoteView_voteUncheckedResultTextColor, defaultUncheckedTextColor)
+
+ textPaintSize = typedArray.getDimensionPixelSize(R.styleable.VoteView_voteTextSize, sp2px(15))
+
+ borderColor = typedArray.getColor(R.styleable.VoteView_voteBorderColor, defaultBorderColor)
+ borderRadius = typedArray.getDimensionPixelOffset(R.styleable.VoteView_voteBorderRadius, dp2px(1f).toInt()).toFloat()
+
+ animDuration = typedArray.getInt(R.styleable.VoteView_voteAnimDuration, 500).toLong()
+
+ rightCheckedBitmapRes = (typedArray.getDrawable(R.styleable.VoteView_voteCheckedIcon) as? BitmapDrawable)?.bitmap
+
+ rightIconWidth = typedArray.getDimensionPixelOffset(R.styleable.VoteView_voteRightIconWidth, 0)
+ rightIconHeight = typedArray.getDimensionPixelOffset(R.styleable.VoteView_voteRightIconHeight, 0)
+
+ typedArray.recycle()
+ }
+
+ private fun initPaint() {
+ iconPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ iconPaint?.isFilterBitmap = true
+ iconPaint?.isDither = true
+
+ bgPaint = getPaint(dp2px(0.5f), Color.WHITE, Paint.Style.FILL)
+ progressPaint = getPaint(dp2px(0.5f), unCheckedProgressColor, Paint.Style.FILL)
+ borderPaint = getPaint(dp2px(0.5f), borderColor, Paint.Style.STROKE)
+
+ voteContentTextPaint = getTextPaint(uncheckedContentTextColor, textPaintSize.toFloat())
+ voteResultTextPaint = getTextPaint(uncheckedResultTextColor, textPaintSize.toFloat())
+ }
+
+ private fun getPaint(strokeWidth: Float, color: Int, style: Paint.Style): Paint {
+ val paint = Paint(Paint.ANTI_ALIAS_FLAG)
+ paint.strokeWidth = strokeWidth
+ paint.color = color
+ paint.isAntiAlias = true
+ paint.style = style
+ return paint
+ }
+
+ private fun getTextPaint(color: Int, textSize: Float): Paint {
+ val textPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ textPaint.textSize = textSize
+ textPaint.color = color
+ textPaint.textAlign = Paint.Align.CENTER
+ textPaint.isAntiAlias = true
+// textPaint.typeface = Typeface.DEFAULT_BOLD
+ return textPaint
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ mWidth = w
+ mHeight = h
+
+ setBgRect()
+ setProgressRect()
+
+ setVoteResultRect()
+ setVoteContentRect()
+ setVoteRightIconRect()
+
+ }
+
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ drawBg(canvas)
+ drawProgress(canvas)
+ drawBorder(canvas)
+
+ drawVoteContentText(canvas)
+ drawVoteResultText(canvas)
+ drawVoteRightIcon(canvas)
+ }
+
+
+ private fun setBgRect() {
+ bgRectF.set(0f, 0f, mWidth.toFloat(), mHeight.toFloat())
+ }
+
+ private fun setProgressRect() {
+ progressRectF.set(0f, 0f, 0f, mHeight.toFloat())
+ }
+
+ private fun setVoteResultRect() {
+ if (mVoteResult.isNullOrBlank()) return
+ voteResultTextPaint!!.getTextBounds(mVoteResult, 0, mVoteResult!!.length, voteResultRectF)
+
+ voteResultRectF.top = 0
+ voteResultRectF.bottom = mHeight
+
+ val fontMetrics = voteResultTextPaint!!.fontMetricsInt
+ voteResultBaseline = (voteResultRectF.bottom + voteResultRectF.top - fontMetrics.bottom - fontMetrics.top) / 2
+ }
+
+ private fun setVoteContentRect() {
+ if (mVoteContent.isNullOrBlank()) return
+ voteContentTextPaint!!.getTextBounds(mVoteContent, 0, mVoteContent!!.length, voteContentRectF)
+
+ textWidth = (voteContentRectF.right - voteContentRectF.left)
+ voteContentRectF.top = 0
+ voteContentRectF.bottom = mHeight
+ voteContentRectF.left = (mWidth - textWidth) / 2
+ voteContentRectF.right = voteContentRectF.left + textWidth
+
+ val fontMetrics = voteContentTextPaint!!.fontMetricsInt
+ voteContentBaseline = (voteContentRectF.bottom + voteContentRectF.top - fontMetrics.bottom - fontMetrics.top) / 2
+ }
+
+ private fun setVoteRightIconRect() {
+ voteRightIconRectF.set(voteContentRectF.right + voteResultMarginRight, (mHeight - rightIconHeight) / 2, voteContentRectF.right + voteResultMarginRight + rightIconWidth, (mHeight + rightIconHeight) / 2)
+ }
+
+
+ private fun drawBg(canvas: Canvas) {
+ if (mProgress != -1f) {
+ bgPaint?.color = Color.WHITE
+ }
+ canvas.drawRoundRect(bgRectF, 0f, 0f, bgPaint!!)
+ }
+
+ private fun drawProgress(canvas: Canvas) {
+ if (mProgress == -1f) return
+ canvas.drawRoundRect(getProgressRectF(), 0f, 0f, progressPaint!!)
+ }
+
+ private fun drawBorder(canvas: Canvas) {
+ borderPaint?.color = borderColor
+ canvas.drawRoundRect(bgRectF, borderRadius, borderRadius, borderPaint)
+ }
+
+ private fun drawVoteContentText(canvas: Canvas) {
+ if (mVoteContent.isNullOrBlank()) return
+ //文字绘制到整个布局的中心位置
+ if (mProgress == -1f) {
+ voteContentRectF.left = (mWidth - textWidth) / 2
+ voteContentRectF.right = voteContentRectF.left + textWidth
+ } else {
+ voteContentRectF.left = max(((1 - mScale) * (mWidth - textWidth) / 2).toInt(), textMarginLeft)
+ voteContentRectF.right = voteContentRectF.left + textWidth
+ }
+
+ canvas.drawText(mVoteContent, voteContentRectF.centerX().toFloat(), voteContentBaseline.toFloat(), voteContentTextPaint)
+ }
+
+ private fun drawVoteResultText(canvas: Canvas) {
+ if (mProgress == -1f || mVoteResult.isNullOrBlank()) return
+ //文字绘制到整个布局的中心位置
+ voteResultTextPaint?.alpha = (255 * mScale).toInt()
+ canvas.drawText(mVoteResult, mWidth - voteResultMarginRight - voteResultRectF.centerX().toFloat(), voteResultBaseline.toFloat(), voteResultTextPaint)
+ }
+
+ private fun drawVoteRightIcon(canvas: Canvas) {
+ if (rightCheckedBitmapRes != null && isVoteChecked) {
+ voteRightIconRectF.left = voteContentRectF.right + voteResultMarginRight
+ voteRightIconRectF.right = voteRightIconRectF.left + rightIconWidth
+ canvas.drawBitmap(rightCheckedBitmapRes!!, null, voteRightIconRectF, iconPaint)
+ }
+ }
+
+ private fun getProgressRectF(): RectF {
+// val currentProgress = mProgress * mWidth * mScale / 100
+ val currentProgress = mProgress * mWidth * mScale
+ progressRectF.set(0f, 0f, currentProgress, mHeight.toFloat())
+ return progressRectF
+ }
+
+
+ fun setVoteCheckedProgressColor(color: Int): VoteView {
+ this.checkedProgressColor = color
+ return this
+ }
+
+ fun setVoteUncheckedProgressColor(color: Int): VoteView {
+ this.unCheckedProgressColor = color
+ return this
+ }
+
+ fun setVoteBorderRadius(radius: Float): VoteView {
+ this.borderRadius = radius
+ return this
+ }
+
+ fun setVoteBorderColor(color: Int): VoteView {
+ this.borderColor = color
+ return this
+ }
+
+ fun setVoteCheckedContentTextColor(color: Int): VoteView {
+ this.checkedContentTextColor = color
+ return this
+ }
+
+ fun setVoteUncheckedContentTextColor(color: Int): VoteView {
+ this.uncheckedContentTextColor = color
+ return this
+ }
+
+
+ fun setVoteCheckedResultTextColor(color: Int): VoteView {
+ this.checkedResultTextColor = color
+ return this
+ }
+
+ fun setVoteUncheckedResultTextColor(color: Int): VoteView {
+ this.uncheckedResultTextColor = color
+ return this
+ }
+
+ fun setVoteCheckedIcon(iconBitmap: Drawable): VoteView {
+ this.rightCheckedBitmapRes = (iconBitmap as? BitmapDrawable)?.bitmap
+ return this
+ }
+
+ fun setVoteRightIconSize(width_height: Int): VoteView {
+ this.rightIconWidth = width_height
+ this.rightIconHeight = width_height
+ return this
+ }
+
+ fun setVoteTextSize(textSize: Int): VoteView {
+ this.textPaintSize = textSize
+ return this
+ }
+
+ fun setVoteAnimDuration(duration: Long): VoteView {
+ this.animDuration = duration
+ return this
+ }
+
+ fun setVoteContent(content: String?): VoteView {
+ mVoteContent = content ?: ""
+ setVoteContentRect()
+ return this
+ }
+
+ fun setVoteResultText(voteResult: String?): VoteView {
+ mVoteResult = voteResult ?: ""
+ setVoteResultRect()
+ return this
+ }
+
+ fun refreshView() {
+ initColor()
+ invalidate()
+ }
+
+
+ fun setProgress(progress: Float) {
+ mProgress = progress
+ if (mProgress != -1f) {
+ invalidate()
+ }
+ }
+
+ fun setProgressWithAnim(progress: Float) {
+ mProgress = progress
+ startAnim()
+ }
+
+ private fun startAnim() {
+ valueAnimator?.cancel()
+ if (valueAnimator == null) {
+ valueAnimator = ValueAnimator.ofFloat(0f, 1f)
+ }
+ valueAnimator?.duration = animDuration
+ valueAnimator?.interpolator = DecelerateInterpolator()
+ valueAnimator?.addUpdateListener {
+ mScale = it.animatedValue as Float
+ invalidate()
+ }
+ valueAnimator?.start()
+ }
+
+ fun onDestroy() {
+ valueAnimator?.cancel()
+ valueAnimator = null
+ }
+
+ fun setVoteIsSelected(isVoteSelected: Boolean): VoteView {
+ this.isVoteChecked = isVoteSelected
+ return this
+ }
+
+ fun dp2px(dpVal: Float): Float {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ dpVal, resources.displayMetrics)
+ }
+
+ fun sp2px(spVal: Int): Int {
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+ spVal.toFloat(), resources.displayMetrics).toInt()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_bg_clickable.xml b/app/src/main/res/drawable/shape_bg_clickable.xml
new file mode 100644
index 0000000..5994224
--- /dev/null
+++ b/app/src/main/res/drawable/shape_bg_clickable.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_bg_un_clickable.xml b/app/src/main/res/drawable/shape_bg_un_clickable.xml
new file mode 100644
index 0000000..902bdea
--- /dev/null
+++ b/app/src/main/res/drawable/shape_bg_un_clickable.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_circle_bg.xml b/app/src/main/res/drawable/shape_circle_bg.xml
new file mode 100644
index 0000000..5c92444
--- /dev/null
+++ b/app/src/main/res/drawable/shape_circle_bg.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_item_bg.xml b/app/src/main/res/drawable/shape_item_bg.xml
new file mode 100644
index 0000000..a15600f
--- /dev/null
+++ b/app/src/main/res/drawable/shape_item_bg.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_search.xml b/app/src/main/res/drawable/shape_search.xml
new file mode 100644
index 0000000..b45f287
--- /dev/null
+++ b/app/src/main/res/drawable/shape_search.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_vote_item_bg.xml b/app/src/main/res/drawable/shape_vote_item_bg.xml
new file mode 100644
index 0000000..594eaf2
--- /dev/null
+++ b/app/src/main/res/drawable/shape_vote_item_bg.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_alipay_home.xml b/app/src/main/res/layout/activity_alipay_home.xml
new file mode 100644
index 0000000..ff55fd7
--- /dev/null
+++ b/app/src/main/res/layout/activity_alipay_home.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_banner.xml b/app/src/main/res/layout/activity_banner.xml
new file mode 100644
index 0000000..7cfb9df
--- /dev/null
+++ b/app/src/main/res/layout/activity_banner.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_clear_screen.xml b/app/src/main/res/layout/activity_clear_screen.xml
new file mode 100644
index 0000000..675b590
--- /dev/null
+++ b/app/src/main/res/layout/activity_clear_screen.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_hover_item.xml b/app/src/main/res/layout/activity_hover_item.xml
new file mode 100644
index 0000000..8530110
--- /dev/null
+++ b/app/src/main/res/layout/activity_hover_item.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 6c6a5df..0df5fab 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,76 +1,13 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="match_parent" />
+
diff --git a/app/src/main/res/layout/activity_path.xml b/app/src/main/res/layout/activity_path.xml
new file mode 100644
index 0000000..338c4f9
--- /dev/null
+++ b/app/src/main/res/layout/activity_path.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_pay_psd_view.xml b/app/src/main/res/layout/activity_pay_psd_view.xml
index 01a8df9..d3481b0 100644
--- a/app/src/main/res/layout/activity_pay_psd_view.xml
+++ b/app/src/main/res/layout/activity_pay_psd_view.xml
@@ -5,14 +5,15 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
+ android:paddingTop="50dp"
tools:context="com.allen.androidcustomview.activity.PayPsdViewActivity">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_reveal_animation.xml b/app/src/main/res/layout/activity_reveal_animation.xml
new file mode 100644
index 0000000..63ba912
--- /dev/null
+++ b/app/src/main/res/layout/activity_reveal_animation.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_sina_vote.xml b/app/src/main/res/layout/activity_sina_vote.xml
new file mode 100644
index 0000000..cb60371
--- /dev/null
+++ b/app/src/main/res/layout/activity_sina_vote.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/adapter_item_hover_user.xml b/app/src/main/res/layout/adapter_item_hover_user.xml
new file mode 100644
index 0000000..1054cef
--- /dev/null
+++ b/app/src/main/res/layout/adapter_item_hover_user.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/adapter_item_main.xml b/app/src/main/res/layout/adapter_item_main.xml
new file mode 100644
index 0000000..eac4730
--- /dev/null
+++ b/app/src/main/res/layout/adapter_item_main.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/banner_item.xml b/app/src/main/res/layout/banner_item.xml
new file mode 100644
index 0000000..d6b4566
--- /dev/null
+++ b/app/src/main/res/layout/banner_item.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/banner_view_layout.xml b/app/src/main/res/layout/banner_view_layout.xml
new file mode 100644
index 0000000..f2cf4a4
--- /dev/null
+++ b/app/src/main/res/layout/banner_view_layout.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/include_default_layout.xml b/app/src/main/res/layout/include_default_layout.xml
new file mode 100644
index 0000000..f5eb9ea
--- /dev/null
+++ b/app/src/main/res/layout/include_default_layout.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/include_toolbar_close.xml b/app/src/main/res/layout/include_toolbar_close.xml
new file mode 100644
index 0000000..1348361
--- /dev/null
+++ b/app/src/main/res/layout/include_toolbar_close.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/include_toolbar_open.xml b/app/src/main/res/layout/include_toolbar_open.xml
new file mode 100644
index 0000000..2703831
--- /dev/null
+++ b/app/src/main/res/layout/include_toolbar_open.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_danmu_layout.xml b/app/src/main/res/layout/item_danmu_layout.xml
new file mode 100644
index 0000000..0906697
--- /dev/null
+++ b/app/src/main/res/layout/item_danmu_layout.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/widget_vote_layout.xml b/app/src/main/res/layout/widget_vote_layout.xml
new file mode 100644
index 0000000..38cef29
--- /dev/null
+++ b/app/src/main/res/layout/widget_vote_layout.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
index cde69bc..3c9de7c 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
index c133a0c..9df99a2 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/icon_study_progress_completed.png b/app/src/main/res/mipmap-mdpi/icon_study_progress_completed.png
new file mode 100755
index 0000000..cb766a8
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/icon_study_progress_completed.png differ
diff --git a/app/src/main/res/mipmap-mdpi/icon_study_progress_not_check.png b/app/src/main/res/mipmap-mdpi/icon_study_progress_not_check.png
new file mode 100755
index 0000000..0d68f56
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/icon_study_progress_not_check.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index bfa42f0..250a0ac 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/alipay_home_bg.jpg b/app/src/main/res/mipmap-xxhdpi/alipay_home_bg.jpg
new file mode 100644
index 0000000..50e6bcf
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/alipay_home_bg.jpg differ
diff --git a/app/src/main/res/mipmap-xxhdpi/banner_point_disabled.png b/app/src/main/res/mipmap-xxhdpi/banner_point_disabled.png
new file mode 100644
index 0000000..9399d3c
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/banner_point_disabled.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/banner_point_enabled.png b/app/src/main/res/mipmap-xxhdpi/banner_point_enabled.png
new file mode 100644
index 0000000..fcc9db8
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/banner_point_enabled.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/car.png b/app/src/main/res/mipmap-xxhdpi/car.png
new file mode 100644
index 0000000..2b1fc47
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/car.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_add.png b/app/src/main/res/mipmap-xxhdpi/ic_add.png
new file mode 100644
index 0000000..4d563a8
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_add.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_camera_3x.png b/app/src/main/res/mipmap-xxhdpi/ic_camera_3x.png
new file mode 100644
index 0000000..e5d04a1
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_camera_3x.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_card.png b/app/src/main/res/mipmap-xxhdpi/ic_card.png
new file mode 100644
index 0000000..626fa18
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_card.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_contact.png b/app/src/main/res/mipmap-xxhdpi/ic_contact.png
new file mode 100644
index 0000000..802747b
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_contact.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index 324e72c..8bcfa9d 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_payment.png b/app/src/main/res/mipmap-xxhdpi/ic_payment.png
new file mode 100644
index 0000000..7b6b4cc
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_payment.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_scan.png b/app/src/main/res/mipmap-xxhdpi/ic_scan.png
new file mode 100644
index 0000000..7be7b64
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_scan.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_search.png b/app/src/main/res/mipmap-xxhdpi/ic_search.png
new file mode 100644
index 0000000..02c7f68
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_search.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_transfer.png b/app/src/main/res/mipmap-xxhdpi/ic_transfer.png
new file mode 100644
index 0000000..db41da5
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_transfer.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/icon_img1.png b/app/src/main/res/mipmap-xxhdpi/icon_img1.png
new file mode 100644
index 0000000..64f74aa
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/icon_img1.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/icon_img2.jpg b/app/src/main/res/mipmap-xxhdpi/icon_img2.jpg
new file mode 100644
index 0000000..663ce32
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/icon_img2.jpg differ
diff --git a/app/src/main/res/mipmap-xxhdpi/icon_vote_check.png b/app/src/main/res/mipmap-xxhdpi/icon_vote_check.png
new file mode 100755
index 0000000..64d7c8a
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/icon_vote_check.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/icon_vote_unchecked.png b/app/src/main/res/mipmap-xxhdpi/icon_vote_unchecked.png
new file mode 100755
index 0000000..ee844a2
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/icon_vote_unchecked.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/live_bg.jpeg b/app/src/main/res/mipmap-xxhdpi/live_bg.jpeg
new file mode 100644
index 0000000..b29c7f6
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/live_bg.jpeg differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index aee44e1..a1fe6ba 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 9791d5b..a43a62b 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -10,7 +10,7 @@
-
+
@@ -29,8 +29,8 @@
-
-
+
+
@@ -59,4 +59,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index c677974..5a8852d 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -19,4 +19,21 @@
#80FF7E37
#80EB4438
+ #ffffff
+ #1983d1
+ #11222222
+
+
+ #e6e6e6
+ #1AFF7C05
+ #F3F3F3
+ #1a1a1a
+ #FF7C05
+ #333333
+ #FF7C05
+
+ #333333
+ #E2E2E2
+ #FFD500
+
diff --git a/build.gradle b/build.gradle
index 74b2ab0..270f05a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,15 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext.kotlin_version = '1.4.21'
repositories {
jcenter()
+ google()
+ mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.3'
+ classpath 'com.android.tools.build:gradle:4.1.2'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -15,6 +19,9 @@ buildscript {
allprojects {
repositories {
jcenter()
+ maven { url "https://jitpack.io" }
+ google()
+ mavenCentral()
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 04e285f..6533050 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Dec 28 10:00:20 PST 2015
+#Mon May 14 16:20:08 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
\ No newline at end of file