Android 使用Kotlin来实现任务完成提醒效果
Posted Adan0520
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 使用Kotlin来实现任务完成提醒效果相关的知识,希望对你有一定的参考价值。
这篇文章比较简单,主要是记录一下任务完成提醒效果。
按照惯例,先来看看效果图
而、下面就是我们的代码实现
1、布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_full_complete_task"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CC000000"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/iv_bg_full_complete_task"
android:layout_width="200dp"
android:layout_height="200dp"
android:gravity="center"
android:src="@drawable/bg_full_complete_task"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.34" />
<ImageView
android:id="@+id/iv_badge_full_complete_task"
android:layout_width="150dp"
android:layout_height="116dp"
android:gravity="center"
android:src="@drawable/icon_full_complete_task_badge"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.34" />
<TextView
android:id="@+id/tv_full_complete_task_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:text="恭喜你"
android:textColor="#FFC53D"
android:textSize="24dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_bg_full_complete_task" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:text="又完成了一项任务"
android:textColor="#E7EaEF"
android:textSize="14dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_full_complete_task_tip" />
<TextView
android:id="@+id/tv_full_complete_task_iknow"
android:layout_width="168dp"
android:layout_height="44dp"
android:background="#ff2244"
android:gravity="center"
android:text="我知道了"
android:textColor="#E7EaEF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.94" />
</androidx.constraintlayout.widget.ConstraintLayout>
<co.per.celebratedome.TaskFullCompleteView
android:id="@+id/anim_full_complete_task"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="44dp"
android:foreground="?selectableItemBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
2、MainActivity:
package co.per.celebratedome
import android.os.Bundle
import android.view.View
import android.view.animation.TranslateAnimation
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
open class MainActivity : AppCompatActivity() {
private lateinit var animFullCompleteTask: TaskFullCompleteView
private lateinit var tvFullCompleteTaskIknow: TextView
private lateinit var clFullCompleteTask: ConstraintLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
animFullCompleteTask = findViewById(R.id.anim_full_complete_task)
tvFullCompleteTaskIknow = findViewById(R.id.tv_full_complete_task_iknow)
clFullCompleteTask = findViewById(R.id.cl_full_complete_task)
tvFullCompleteTaskIknow.setOnClickListener {
animFullCompleteTask.stopAnim()
}
show()
}
private fun show() {
val ctrlAnimation = TranslateAnimation(
TranslateAnimation.RELATIVE_TO_SELF, 0f, TranslateAnimation.RELATIVE_TO_SELF, 0f,
TranslateAnimation.RELATIVE_TO_SELF, 1f, TranslateAnimation.RELATIVE_TO_SELF, 0f
)
ctrlAnimation.duration = 1000 //设置动画的过渡时间
clFullCompleteTask.startAnimation(ctrlAnimation)
animFullCompleteTask.startAnim()
}
}
3、自定义View:TaskFullCompleteView
package co.per.celebratedome
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.TypeEvaluator
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.os.Handler
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.view.View
import android.view.WindowManager
import android.view.animation.*
import android.view.animation.Interpolator
import android.widget.FrameLayout
import androidx.annotation.AttrRes
import java.util.*
/**
* 任务完成提醒效果View
* Created by juan on 2021/07/23
*/
class TaskFullCompleteView(
private var mContext: Context,
attrs: AttributeSet?,
@AttrRes defStyleAttr: Int
) :
FrameLayout(mContext, attrs, defStyleAttr) {
private var mMaxWidth = 0
private var mMaxHeight = 0
private var rotateIndex = 0
private val mLinearInterpolator: Interpolator = LinearInterpolator() //线性
private val mAccelerateInterpolator: Interpolator = AccelerateInterpolator() //加速
private val mDecelerateInterpolator: Interpolator = DecelerateInterpolator() //减速
private val mAccelerateDecelerateInterpolator: Interpolator =
AccelerateDecelerateInterpolator() //先加速后减速
private var mInterpolatorArray: Array<Interpolator>? = null
private var mHandler: Handler? = Handler()
constructor(context: Context) : this(context, null) {
mContext = context
}
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {
mContext = context
}
/**
* 初始化测量
*/
private fun initMeasure() {
val windowManager = mContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
mMaxWidth = displayMetrics.widthPixels
mMaxHeight = displayMetrics.heightPixels
}
/**
* 初始化
*/
private fun initView() {
mInterpolatorArray = arrayOf(
mLinearInterpolator,
mAccelerateInterpolator,
mDecelerateInterpolator,
mAccelerateDecelerateInterpolator
)
}
/**
* 设置为和屏幕一样大
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(mMaxWidth, mMaxHeight)
}
/**
* 开始动画
*/
fun startAnim() {
mHandler!!.post(starStartRunnable)
}
/**
* 结束动画
*/
fun stopAnim() {
if (mHandler != null) {
mHandler!!.removeCallbacksAndMessages(null)
mHandler = null
}
}
/**
* 开始动画任务
*/
private val starStartRunnable: Runnable = object : Runnable {
override fun run() {
val anomalyView = AnomalyView(mContext)
addView(anomalyView)
bezierAnim(anomalyView)
mHandler!!.postDelayed(this, 200)
}
}
/**
* 动画实现
*/
private fun bezierAnim(view: View) {
rotateIndex++
if (rotateIndex % 5 == 0) {
val animationSet4 = AnimationSet(true)
//旋转动画
val rotateAnimation = RotateAnimation(
0f,
Random().nextInt(360).toFloat(), Animation.RELATIVE_TO_PARENT,
(-1 + Math.random() * 3).toFloat(), Animation.RELATIVE_TO_PARENT,
(-1 + Math.random() * 3).toFloat()
)
animationSet4.addAnimation(rotateAnimation)
//设置动画变化的持续时间
animationSet4.duration = 4000
view.startAnimation(animationSet4)
}
val startPointF = PointF(Random().nextInt(mMaxWidth).toFloat(), -50f) //起始点
val endPointF = PointF(
Random().nextInt(mMaxWidth).toFloat(),
mMaxHeight.toFloat()
) //结束点
val valueAnimator = ValueAnimator.ofObject(
BezierEvaluator(
getScreenRandomPointF(1),
getScreenRandomPointF(2)
), startPointF, endPointF
)
valueAnimator.addUpdateListener { animation ->
val pointF = animation.animatedValue as PointF
view.x = pointF.x
view.y = pointF.y
}
valueAnimator.interpolator = mInterpolatorArray!![Random().nextInt(4)]
valueAnimator.setTarget(view)
valueAnimator.duration = 4000
valueAnimator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
removeView(view)
}
})
valueAnimator.start()
}
/**
* 获取屏幕上的随机两个控制点
*
* @param number 第几个控制点
* @return
*/
private fun getScreenRandomPointF(number: Int): PointF {
val pointF = PointF()
pointF.x = (Random().nextInt(mMaxWidth * 2) - mMaxWidth / 2).toFloat() //尽量向左右两边扩散一点
pointF.y = Random().nextInt(mMaxHeight / 2 * number).toFloat()
return pointF
}
/**
* 贝塞尔路径实现
* 三阶公式 B(T) = p0((1-t)(1-t)(1-t))+3P1t((1-t)(1-t))+3p2(t*t)(1-t)+p3(t*t*t)
* 二阶公式 B(T) = (1-t)*(1-t)*P0+2t(1-t)P1+t*t*P2
* 一阶公式 B(T) = P0+(P1-P0)*t=(1-t)P0+t*P1
*/
private inner class BezierEvaluator(
private val mControlPointFOne: PointF,
private val mControlPointFTwo: PointF
) :
TypeEvaluator<PointF> {
override fun evaluate(fraction: Float, startValue: PointF, endValue: PointF): PointF {
val resultPointF = PointF() //结果值
val v = (1 - fraction) * (1 - fraction) * (1 - fraction)
resultPointF.x =
startValue //开始控制点
.x * v + 3 * mControlPointFOne.x * fraction * ((1 - fraction) * (1 - fraction)) + 3 * mControlPointFTwo.x * (fraction * fraction) * (1 - fraction) + endValue //结束控制点
.x * (fraction * fraction * fraction)
resultPointF.y =
startValue //开始控制点
.y * v + 3 * mControlPointFOne.y * fraction * ((1 - fraction) * (1 - fraction)) + 3 * mControlPointFTwo.y * (fraction * fraction) * (1 - fraction) + endValue //结束控制点
.y * (fraction * fraction * fraction)
return resultPointF
}
}
/**
* 不规则View
*/
private inner class AnomalyView @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) :
View(context, attrs, defStyleAttr) {
private val mMaxWidth = 25 //最大宽度
private val mMaxHeight = 50 //最大高度
private val mPadding = 10 //内边距
private var mPaint: Paint? = null
private var mRandomPoint: Array<PointF?>? = null //随机点 4个 顺序为 左上右下
private var mLumpColorArray: Array<String>? = null
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(mMaxWidth, mMaxHeight)
}
/**
* 初始化
*/
private fun init() {
mLumpColorArray = arrayOf("#3799FF", "#F52245", "#F6B56A", "#35B951") //蓝、红、黄、绿
mRandomPoint = arrayOfNulls(4)
mRandomPoint!![0] = PointF(
Random().nextInt(mPadding).toFloat(),
Random().nextInt(mMaxHeight - mPadding).toFloat()
) //左
mRandomPoint!![1] = PointF(
(Random().nextInt(mMaxWidth - mPadding) + mPadding).toFloat(),
Random().nextInt(mPadding).toFloat()
) //上
mRandomPoint!![2] = PointF(
(Random().nextInt(mPadding) + (mMaxWidth - mPadding)).toFloat(),
(Random().nextInt(mMaxHeight - mPadding) + mPadding).toFloat()
) //右
mRandomPoint!![3] = PointF(
(Random().nextInt(mMaxWidth) - mPadding).toFloat(),
(Random().nextInt(mPadding) + (mMaxHeight - mPadding)).toFloat()
) //下
mPaint = Paint()
mPaint!!.isDither = true
mPaint!!.isAntiAlias = true
mPaint!!.color = Color.parseColor(
mLumpColorArray!!以上是关于Android 使用Kotlin来实现任务完成提醒效果的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin + Flow 实现的 Android 应用初始化任务启动库
Android 在fragment中实现返回键单击提醒 双击退出