+ * @author : Allen + * date : 2018年09月30日 + * desc : 支付宝首页 来源 https://mp.weixin.qq.com/s/GegMt7GDBCFVoUgFQWG3Sw + * version : 1.0 + *+ */ +public class AliPayHomeActivity extends AppCompatActivity implements AppBarLayout.OnOffsetChangedListener { + + + private AppBarLayout mAppBarLayout; + private View mToolbarOpenBgView; + private View mToolbarCloseBgView; + private View mToolbarOpenLayout; + private View mToolbarCloseLayout; + private View contentBgView; + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_alipay_home); + initView(); + mAppBarLayout.addOnOffsetChangedListener(this); + } + + private void initView() { + + mToolbarOpenBgView = findViewById(R.id.toolbar_open_bg_view); + mToolbarOpenLayout = findViewById(R.id.include_toolbar_open); + + mToolbarCloseBgView = findViewById(R.id.bg_toolbar_close); + mToolbarCloseLayout = findViewById(R.id.include_toolbar_close); + contentBgView = findViewById(R.id.content_bg_view); + + mAppBarLayout = findViewById(R.id.app_bar_layout); + } + + @Override + public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) { + + //垂直方向偏移量 + int offset = Math.abs(verticalOffset); + //最大偏移距离 + int scrollRange = appBarLayout.getTotalScrollRange(); + if (offset <= scrollRange / 2) {//当滑动没超过一半,展开状态下toolbar显示内容,根据收缩位置,改变透明值 + mToolbarOpenLayout.setVisibility(View.VISIBLE); + mToolbarCloseLayout.setVisibility(View.GONE); + //根据偏移百分比 计算透明值 + float scale2 = (float) offset / (scrollRange / 2); + int alpha2 = (int) (255 * scale2); + mToolbarOpenBgView.setBackgroundColor(Color.argb(alpha2, 25, 131, 209)); + } else {//当滑动超过一半,收缩状态下toolbar显示内容,根据收缩位置,改变透明值 + mToolbarOpenLayout.setVisibility(View.GONE); + mToolbarCloseLayout.setVisibility(View.VISIBLE); + float scale3 = (float) (scrollRange - offset) / (scrollRange / 2); + int alpha3 = (int) (255 * scale3); + mToolbarCloseBgView.setBackgroundColor(Color.argb(alpha3, 25, 131, 209)); + } + //根据偏移百分比计算扫一扫布局的透明度值 + float scale = (float) offset / scrollRange; + int alpha = (int) (255 * scale); + contentBgView.setBackgroundColor(Color.argb(alpha, 25, 131, 209)); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mAppBarLayout.removeOnOffsetChangedListener(this); + } +} diff --git a/app/src/main/java/com/allen/androidcustomview/activity/ClearScreenActivity.kt b/app/src/main/java/com/allen/androidcustomview/activity/ClearScreenActivity.kt new file mode 100644 index 0000000..f6091f0 --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/activity/ClearScreenActivity.kt @@ -0,0 +1,39 @@ +package com.allen.androidcustomview.activity + +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.widget.Toast +import com.allen.androidcustomview.R +import com.allen.androidcustomview.widget.ClearScreenMode +import com.allen.androidcustomview.widget.ClearScreenView +import kotlinx.android.synthetic.main.activity_clear_screen.* + +/** + *
+ * author : Allen + * date : 2021年1月28日 + * desc : + *+ */ +class ClearScreenActivity : AppCompatActivity(), ClearScreenView.OnClearScreenListener { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_clear_screen) + + clear_screen_container.addClearView(iv_clear_content) + + clear_screen_container.setOnClearScreenListener(this) + + btn_quick_clear.setOnClickListener { clear_screen_container.clearScreenMode = ClearScreenMode.QUICK_SCROLL } + btn_slow_clear.setOnClickListener { clear_screen_container.clearScreenMode = ClearScreenMode.SLOW_SCROLL } + } + + override fun onCleared() { + Toast.makeText(this, "清屏了", Toast.LENGTH_SHORT).show() + } + + override fun onRestored() { + Toast.makeText(this, "恢复了", Toast.LENGTH_SHORT).show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/activity/HoverItemActivity.java b/app/src/main/java/com/allen/androidcustomview/activity/HoverItemActivity.java index 52c9a97..04b814e 100644 --- a/app/src/main/java/com/allen/androidcustomview/activity/HoverItemActivity.java +++ b/app/src/main/java/com/allen/androidcustomview/activity/HoverItemActivity.java @@ -5,7 +5,6 @@ import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.util.Log; import android.widget.TextView; import com.allen.androidcustomview.R; @@ -39,7 +38,7 @@ public class HoverItemActivity extends AppCompatActivity { private List
+ * @author : Allen + * date : 2019年07月23日 + * desc : + *+ */ +class PathActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_path) + start_btn.setOnClickListener { + car_anim_view.startAnim() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/allen/androidcustomview/activity/ProgressBarActivity.java b/app/src/main/java/com/allen/androidcustomview/activity/ProgressBarActivity.java index 761895a..09c91c2 100644 --- a/app/src/main/java/com/allen/androidcustomview/activity/ProgressBarActivity.java +++ b/app/src/main/java/com/allen/androidcustomview/activity/ProgressBarActivity.java @@ -13,6 +13,10 @@ import com.allen.androidcustomview.widget.LoadingLineView; import com.allen.androidcustomview.widget.LoadingView; import com.allen.androidcustomview.widget.ProductProgressBar; +import com.allen.androidcustomview.widget.StudyPlanProgressView; + +import java.util.ArrayList; +import java.util.List; public class ProgressBarActivity extends AppCompatActivity { @@ -24,6 +28,7 @@ public class ProgressBarActivity extends AppCompatActivity { LoadingLineView loadingLineView; Button button; + StudyPlanProgressView studyPlanProgressView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -39,6 +44,7 @@ protected void onCreate(Bundle savedInstanceState) { textView = (TextView) findViewById(R.id.progress_tv); button = (Button) findViewById(R.id.startAnimationBtn); + studyPlanProgressView = findViewById(R.id.study_plan_progress_view); circleProgressBarView.setProgressWithAnimation(60); circleProgressBarView.setProgressListener(new CircleProgressBarView.ProgressListener() { @@ -68,7 +74,7 @@ public void currentProgressListener(float currentProgress) { button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - loadingLineView.stopLoading(); + loadingLineView.startLoading(); loadingView.startAnimation(); horizontalProgressBar.setProgressWithAnimation(100); productProgressBar.setProgress(100); @@ -79,8 +85,25 @@ public void currentProgressListener(float currentProgress) { textView.setText("当前进度:" + currentProgress); } }); + studyPlanProgressView.setData(getPlanData(true)); } }); + studyPlanProgressView.setData(getPlanData(false)); + + } + + private List
+ * @author : Allen + * e-mail : lygttpod@163.com + * date : 2019年05月11日 + * desc : + *+ */ +class RecyclerViewItemAnimActivity : AppCompatActivity() { + + var adapter: ItemAnimAdapter? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_recycler_view_item_anim) + initListener() + initView() + initData() + } + + private fun initView() { + adapter = ItemAnimAdapter(arrayListOf()) + recycler_view.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false) + recycler_view.adapter = adapter + recycler_view.itemAnimator = DefaultItemAnimator() + } + + private fun initListener() { + normal_btn.setOnClickListener { setReverseLayout(false) } + reverse_btn.setOnClickListener { setReverseLayout(true) } + scale_btn.setOnClickListener { setItemAnimation(ScaleItemAnimation()) } + slide_btn.setOnClickListener { setItemAnimation(SlideItemAnimation()) } + rotate_x_btn.setOnClickListener { setItemAnimation(RotateXItemAnimation()) } + rotate_y_btn.setOnClickListener { setItemAnimation(RotateYItemAnimation()) } + + add_btn.setOnClickListener { + adapter?.addData(0, getItemData()) + (recycler_view.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(0, 0) + } + + remove_btn.setOnClickListener { + if (adapter?.data?.size ?: 0> 0) { + adapter?.remove(0) + (recycler_view.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(0, 0) + } + } + } + + private fun setReverseLayout(reverseLayout: Boolean) { + recycler_view.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, reverseLayout) + adapter?.data?.clear() + adapter?.notifyDataSetChanged() + } + + private fun setItemAnimation(itemAnimation: RecyclerView.ItemAnimator) { + adapter?.data?.clear() + adapter?.notifyDataSetChanged() + recycler_view.itemAnimator = itemAnimation + } + + private fun initData(): ArrayList
+ * @author : xiaoyao + * e-mail : xiaoyao@51vest.com + * date : 2018年08月24日 + * desc : 揭露动画 + * version : 1.0 + *+ */ +public class RevealAnimationActivity extends AppCompatActivity { + + private ImageView imageView; + private Button startBtn; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_reveal_animation); + + imageView = findViewById(R.id.img_iv); + startBtn = findViewById(R.id.start_btn); + + + final int centerX = 0; + final int centerY = 0; + startBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final float radius = (float) Math.hypot(imageView.getWidth(), imageView.getHeight()); + + if (imageView.getVisibility() == View.VISIBLE) { + if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.LOLLIPOP) { + Animator animator = ViewAnimationUtils.createCircularReveal(imageView, centerX, centerY, radius, 0); + animator.setDuration(3000); + animator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + imageView.setVisibility(View.GONE); + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + animator.start(); + } + + } else { + if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.LOLLIPOP) { + Animator animator = ViewAnimationUtils.createCircularReveal(imageView, centerX, centerY, 0, radius); + animator.setDuration(3000); + imageView.setVisibility(View.VISIBLE); + animator.start(); + } + } + } + }); + + } +} diff --git a/app/src/main/java/com/allen/androidcustomview/activity/SinaVoteActivity.kt b/app/src/main/java/com/allen/androidcustomview/activity/SinaVoteActivity.kt new file mode 100644 index 0000000..2fee8ab --- /dev/null +++ b/app/src/main/java/com/allen/androidcustomview/activity/SinaVoteActivity.kt @@ -0,0 +1,43 @@ +package com.allen.androidcustomview.activity + +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import com.allen.androidcustomview.R +import com.allen.androidcustomview.bean.VoteBean +import com.allen.androidcustomview.bean.VoteOption +import com.allen.androidcustomview.data.getMockData +import com.allen.androidcustomview.widget.vote.VoteLayoutAdapter +import kotlinx.android.synthetic.main.activity_sina_vote.* + +/** + *
+ * @author : Allen + * date : 2019年08月06日 + * desc : + *+ */ +class SinaVoteActivity : AppCompatActivity(), VoteLayoutAdapter.OnVoteClickListener { + + var voteLayoutAdapter: VoteLayoutAdapter? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_sina_vote) + voteLayoutAdapter = VoteLayoutAdapter(vote_ll) + voteLayoutAdapter?.setData(getMockData()) + voteLayoutAdapter?.onVoteClickListener = this + } + + override fun onDestroy() { + super.onDestroy() + voteLayoutAdapter?.onDestroy() + } + + override fun onVoteCommitBtnClick(mainVote: VoteBean?, optionIds: ArrayList
+ * @author : Allen + * e-mail :lygttpod@163.com + * date : 2019年05月11日 + * desc : + *+ */ +class ItemAnimAdapter(list: ArrayList
+ * @author : xiaoyao + * e-mail : xiaoyao@51vest.com + * date : 2018年05月14日 + * desc : + * version : 1.0 + *+ */ + +public class MainAdapter extends BaseQuickAdapter
+ * @author : Allen + * e-mail : lygttpod@163.com + * date : 2019年05月11日 + * desc : 基于DefaultItemAnimator扩展的BaseItemAnimation,暴露常用方法 + *+ */ + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.support.annotation.NonNull; +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView.ViewHolder; +import android.support.v7.widget.SimpleItemAnimator; +import android.view.View; +import android.view.ViewPropertyAnimator; + +import java.util.ArrayList; +import java.util.List; + +public abstract class BaseItemAnimation extends SimpleItemAnimator { + private static final boolean DEBUG = false; + + private static TimeInterpolator sDefaultInterpolator; + + private ArrayList
+ * If the payload list is not empty, DefaultItemAnimator returns true.
+ * When this is the case:
+ *
+ * @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/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
+ * @author : Allen + * date : 2019年08月06日 + * desc : + *+ */ + +fun getMockData(): ArrayList
+ * @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/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
+ * @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 index 1497702..46570b2 100644 --- a/app/src/main/java/com/allen/androidcustomview/widget/HoverItemDecoration.java +++ b/app/src/main/java/com/allen/androidcustomview/widget/HoverItemDecoration.java @@ -152,6 +152,8 @@ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, Recycle //如果是分组第一个就留出绘制item的高度 if (isFirstInGroup(position)) { outRect.top = itemHeight; + }else { + outRect.top = itemDivideHeight; } } 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
+ * @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/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
+ * @author : Allen + * date : 2019年08月03日 + * desc : + *+ */ +class VoteLayoutAdapter(private val viewGroup: ViewGroup) { + + private var viewHolders = mutableListOf
+ * @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 @@ + +