这篇文章比较简单,主要是记录一下任务完成提醒效果。 按照惯例,先来看看效果图  而、下面就是我们的代码实现 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.*
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
)
}
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()
}
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
}
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
}
}
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
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!![Random().nextInt(
mLumpColorArray!!.size
)]
)
mPaint!!.style = Paint.Style.FILL_AND_STROKE
mPaint!!.strokeWidth = 60f
}
@SuppressLint("DrawAllocation")
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val path = Path()
path.moveTo(mRandomPoint!![0]!!.x, mRandomPoint!![0]!!.y)
path.lineTo(mRandomPoint!![1]!!.x, mRandomPoint!![1]!!.y)
path.lineTo(mRandomPoint!![2]!!.x, mRandomPoint!![2]!!.y)
path.lineTo(mRandomPoint!![3]!!.x, mRandomPoint!![3]!!.y)
path.close()
canvas.drawPath(path, mPaint!!)
}
init {
init()
}
}
init {
initMeasure()
initView()
startAnim()
}
}
源码下载
|