diff --git a/app/src/main/java/com/wanglu/wlikeview/MainActivity.kt b/app/src/main/java/com/wanglu/wlikeview/MainActivity.kt index dc1993e..1323bd7 100644 --- a/app/src/main/java/com/wanglu/wlikeview/MainActivity.kt +++ b/app/src/main/java/com/wanglu/wlikeview/MainActivity.kt @@ -2,6 +2,8 @@ package com.wanglu.wlikeview import android.os.Bundle import android.support.v7.app.AppCompatActivity +import com.wanglu.lib.juejin.WJueJinLikeAnim +import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { @@ -9,5 +11,18 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + var liked = false + val likeAnim = WJueJinLikeAnim.Builder(iv, R.mipmap.fd_zan_press).create() + iv.setOnClickListener { + if(liked){ + iv.setImageResource(R.mipmap.fd_zan) + liked = false + }else{ + iv.setImageResource(R.mipmap.fd_zan_press) + liked = true + likeAnim.show() + } + } + } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c63d8cd..8c153a1 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,12 +5,12 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="#fff" tools:context=".MainActivity"> - - + android:src="@mipmap/fd_zan"/> \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/fd_zan.png b/app/src/main/res/mipmap-hdpi/fd_zan.png new file mode 100755 index 0000000..885450a Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/fd_zan.png differ diff --git a/app/src/main/res/mipmap-hdpi/fd_zan_press.png b/app/src/main/res/mipmap-hdpi/fd_zan_press.png new file mode 100755 index 0000000..70621a2 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/fd_zan_press.png differ diff --git a/lib/src/main/AndroidManifest.xml b/lib/src/main/AndroidManifest.xml index 5305769..bc3d732 100644 --- a/lib/src/main/AndroidManifest.xml +++ b/lib/src/main/AndroidManifest.xml @@ -1,2 +1,5 @@ + package="com.wanglu.lib"> + + + diff --git a/lib/src/main/java/com/wanglu/lib/juejin/CircleView.kt b/lib/src/main/java/com/wanglu/lib/juejin/CircleView.kt index c7e28cd..771d09c 100644 --- a/lib/src/main/java/com/wanglu/lib/juejin/CircleView.kt +++ b/lib/src/main/java/com/wanglu/lib/juejin/CircleView.kt @@ -1,5 +1,7 @@ package com.wanglu.lib.juejin +import android.animation.Animator +import android.animation.AnimatorSet import android.animation.ValueAnimator import android.content.Context import android.graphics.Canvas @@ -13,17 +15,21 @@ class CircleView : View { private val outerCirclePaint = Paint() private val innerCirclePaint = Paint() - private var viewWidth = 174 - private var viewHeight = 174 + private var viewWidth = 0 + private var viewHeight = 0 private var outerCircleColor = Color.parseColor("#5BA2E9") private var innerCircleColor = Color.parseColor("#48CFC2") - + private var outerCircleRadius = 0f private var innerCircleRadius = 0f - private var outerCircleMaxRadius = viewWidth / 2f - 30 - private var innerCircleMaxRadius = viewWidth / 2f - 35 + private var outerStrokeWidth = 10f + private var outerCircleMaxRadius = 0f + private var innerCircleMaxRadius = 0f + + private var animSet = AnimatorSet() + private lateinit var dv: DotsView constructor(context: Context?) : this(context, null) constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0) @@ -33,32 +39,25 @@ class CircleView : View { outerCirclePaint.color = outerCircleColor outerCirclePaint.isAntiAlias = true outerCirclePaint.style = Paint.Style.STROKE - outerCirclePaint.strokeWidth = 5f + outerCirclePaint.strokeWidth = outerStrokeWidth innerCirclePaint.color = innerCircleColor innerCirclePaint.isAntiAlias = true innerCirclePaint.style = Paint.Style.FILL - - - val outerRadiusAnim = ValueAnimator.ofFloat(outerCircleRadius, outerCircleMaxRadius) - outerRadiusAnim.duration = 400 - outerRadiusAnim.addUpdateListener{ - outerCircleRadius = it.animatedValue as Float - invalidate() - } - - val innerRadiusAnim = ValueAnimator.ofFloat(outerCircleRadius, innerCircleMaxRadius) - innerRadiusAnim.duration = 400 - innerRadiusAnim.addUpdateListener{ - outerCircleRadius = it.animatedValue as Float - invalidate() - } + + } + + + fun startAnim() { + animSet.start() } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) - setMeasuredDimension(viewWidth, viewHeight) + if (viewWidth != 0 && viewHeight != 0) { + setMeasuredDimension(viewWidth, viewHeight) + } } override fun onDraw(canvas: Canvas?) { @@ -74,6 +73,100 @@ class CircleView : View { fun setSize(width: Int, height: Int) { this.viewWidth = width this.viewHeight = height + outerStrokeWidth = (viewWidth / 2 * 0.1).toFloat() + outerCircleMaxRadius = viewWidth / 2f - 30 + innerCircleMaxRadius = outerCircleMaxRadius - outerStrokeWidth / 2 + + + val outerRadiusAnim = ValueAnimator.ofFloat(outerCircleRadius, outerCircleMaxRadius) + outerRadiusAnim.duration = 200 + outerRadiusAnim.addUpdateListener { + outerCircleRadius = it.animatedValue as Float + invalidate() + } + + val innerRadiusAnim = ValueAnimator.ofFloat(innerCircleRadius, innerCircleMaxRadius) + innerRadiusAnim.duration = 200 + innerRadiusAnim.addUpdateListener { + innerCircleRadius = it.animatedValue as Float + invalidate() + } + + val outerStrokeWidthAnim = ValueAnimator.ofFloat(outerStrokeWidth, 0f) + outerStrokeWidthAnim.duration = 100 + outerStrokeWidthAnim.addUpdateListener { + outerStrokeWidth = it.animatedValue as Float + outerCirclePaint.strokeWidth = outerStrokeWidth + invalidate() + } + + innerRadiusAnim.addListener(object : Animator.AnimatorListener { + override fun onAnimationRepeat(animation: Animator?) { + + } + + override fun onAnimationCancel(animation: Animator?) { + } + + override fun onAnimationStart(animation: Animator?) { + } + + override fun onAnimationEnd(animation: Animator?) { + innerCirclePaint.color = Color.TRANSPARENT + invalidate() + } + + }) + + outerRadiusAnim.addListener(object : Animator.AnimatorListener { + override fun onAnimationRepeat(animation: Animator?) { + + } + + override fun onAnimationCancel(animation: Animator?) { + } + + override fun onAnimationStart(animation: Animator?) { + } + + override fun onAnimationEnd(animation: Animator?) { + innerCirclePaint.color = Color.TRANSPARENT + invalidate() + } + + }) + + + + animSet.play(outerRadiusAnim).with(innerRadiusAnim).before(outerStrokeWidthAnim) + animSet.addListener(object : Animator.AnimatorListener { + override fun onAnimationRepeat(animation: Animator?) { + + } + + override fun onAnimationCancel(animation: Animator?) { + outerCircleRadius = 0f + innerCircleRadius = 0f + outerStrokeWidth = (viewWidth / 2 * 0.1).toFloat() + outerCirclePaint.strokeWidth = outerStrokeWidth + innerCirclePaint.color = innerCircleColor + dv.cancelAnim() + } + + override fun onAnimationStart(animation: Animator?) { + dv.show() + } + + override fun onAnimationEnd(animation: Animator?) { + outerCircleRadius = 0f + innerCircleRadius = 0f + outerStrokeWidth = (viewWidth / 2 * 0.1).toFloat() + outerCirclePaint.strokeWidth = outerStrokeWidth + innerCirclePaint.color = innerCircleColor + dv.dismiss() + } + + }) invalidate() } @@ -86,4 +179,15 @@ class CircleView : View { invalidate() } + fun cancelAnim() { + animSet.cancel() + } + + fun isAnimRunning(): Boolean { + return animSet.isRunning + } + + fun setDv(dv: DotsView){ + this.dv = dv + } } \ No newline at end of file diff --git a/lib/src/main/java/com/wanglu/lib/juejin/DotsView.kt b/lib/src/main/java/com/wanglu/lib/juejin/DotsView.kt index d258ea2..a27026b 100644 --- a/lib/src/main/java/com/wanglu/lib/juejin/DotsView.kt +++ b/lib/src/main/java/com/wanglu/lib/juejin/DotsView.kt @@ -1,5 +1,7 @@ package com.wanglu.lib.juejin +import android.animation.Animator +import android.animation.AnimatorSet import android.animation.ValueAnimator import android.content.Context import android.graphics.Canvas @@ -14,11 +16,11 @@ class DotsView : View { private val smallDotPaint = Paint() - private var viewWidth = 174 - private var viewHeight = 174 + private var viewWidth = 0 + private var viewHeight = 0 private var centerX = 0 private var centerY = 0 - private var bitDotColor = Color.parseColor("#48CFC2") + private var bigDotColor = Color.parseColor("#48CFC2") private var smallDotColor = Color.parseColor("#5BA2E9") private var bigDotRadius = MAX_BIG_DOT_RADIUS // 大点的半径 @@ -38,11 +40,11 @@ class DotsView : View { init { - bigDotPaint.color = bitDotColor + bigDotPaint.color = Color.TRANSPARENT bigDotPaint.isAntiAlias = true bigDotPaint.style = Paint.Style.FILL - smallDotPaint.color = smallDotColor + smallDotPaint.color = Color.TRANSPARENT smallDotPaint.isAntiAlias = true smallDotPaint.style = Paint.Style.FILL } @@ -52,7 +54,7 @@ class DotsView : View { * 设置颜色 */ fun setColor(color1: Int, color2: Int){ - bitDotColor = color1 + bigDotColor = color1 smallDotColor = color2 invalidate() } @@ -67,6 +69,14 @@ class DotsView : View { invalidate() } + fun show(){ + bigDotPaint.color = bigDotColor + smallDotPaint.color = smallDotColor + invalidate() + } + + + private var animSet = AnimatorSet() /** * 消失 @@ -74,8 +84,8 @@ class DotsView : View { fun dismiss(){ val bigDotAnim = ValueAnimator.ofFloat(bigDotRadius, 0f) val smallDotAnim = ValueAnimator.ofFloat(smallDotRadius, 0f) - bigDotAnim.duration = 400 - smallDotAnim.duration = 400 + bigDotAnim.duration = 300 + smallDotAnim.duration = 300 bigDotAnim.addUpdateListener { bigDotRadius = it.animatedValue as Float invalidate() @@ -86,8 +96,35 @@ class DotsView : View { invalidate() } - bigDotAnim.start() - smallDotAnim.start() + animSet.playTogether(bigDotAnim, smallDotAnim) + animSet.addListener(object : Animator.AnimatorListener{ + override fun onAnimationRepeat(animation: Animator?) { + + } + + override fun onAnimationCancel(animation: Animator?) { + bigDotPaint.color = Color.TRANSPARENT + smallDotPaint.color = Color.TRANSPARENT + + bigDotRadius = MAX_BIG_DOT_RADIUS + smallDotRadius = MAX_SMALL_DOT_RADIUS + } + + override fun onAnimationStart(animation: Animator?) { + } + + override fun onAnimationEnd(animation: Animator?) { + // 动画结束后还原 + bigDotPaint.color = Color.TRANSPARENT + smallDotPaint.color = Color.TRANSPARENT + + bigDotRadius = MAX_BIG_DOT_RADIUS + smallDotRadius = MAX_SMALL_DOT_RADIUS + invalidate() + } + + }) + animSet.start() } @@ -95,10 +132,11 @@ class DotsView : View { super.onMeasure(widthMeasureSpec, heightMeasureSpec) // 测量的时候如果宽高都不为0再设置 -// if (width != 0 && height != 0) - setMeasuredDimension(viewWidth, viewHeight) - centerX = viewWidth / 2 - centerY = viewHeight / 2 + if (viewWidth != 0 && viewHeight != 0) { + setMeasuredDimension(viewWidth, viewHeight) + centerX = viewWidth / 2 + centerY = viewHeight / 2 + } } @@ -121,5 +159,8 @@ class DotsView : View { } + fun cancelAnim(){ + animSet.cancel() + } } \ No newline at end of file diff --git a/lib/src/main/java/com/wanglu/lib/juejin/WJueJinAnim.kt b/lib/src/main/java/com/wanglu/lib/juejin/WJueJinAnim.kt deleted file mode 100644 index 47e0e30..0000000 --- a/lib/src/main/java/com/wanglu/lib/juejin/WJueJinAnim.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.wanglu.lib.juejin - -class WJueJinAnim{ - - companion object { - var defaultColor1 = "#48CFC2" - var defaultColor2 = "#5BA2E9" - } -} \ No newline at end of file diff --git a/lib/src/main/java/com/wanglu/lib/juejin/WJueJinLikeAnim.kt b/lib/src/main/java/com/wanglu/lib/juejin/WJueJinLikeAnim.kt new file mode 100644 index 0000000..98a7bbb --- /dev/null +++ b/lib/src/main/java/com/wanglu/lib/juejin/WJueJinLikeAnim.kt @@ -0,0 +1,138 @@ +package com.wanglu.lib.juejin + +import android.animation.Animator +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.view.Gravity +import android.view.LayoutInflater +import android.view.animation.BounceInterpolator +import android.widget.ImageView +import android.widget.PopupWindow +import com.wanglu.lib.R + +class WJueJinLikeAnim(context: Context?, private val builder: Builder) : PopupWindow(context) { + + private val imgScaleXAnim: ObjectAnimator + private val imgScaleYAnim: ObjectAnimator + private val animSet = AnimatorSet() + private val iv: ImageView + private val cv: CircleView + private val dv: DotsView + + private val SCALE_FACTOR = 3 + + init { + contentView = LayoutInflater.from(context).inflate(R.layout.view_jue_jin, null) + iv = contentView.findViewById(R.id.iv) + cv = contentView.findViewById(R.id.cv) + dv = contentView.findViewById(R.id.dv) + cv.setDv(dv) + +// isTouchable = false +// setTouchInterceptor { v, event -> +// +// cv.cancelAnim() +// dismiss() +// false +// } + imgScaleXAnim = ObjectAnimator.ofFloat(iv, "scaleX", 1.5f, 1f) + imgScaleYAnim = ObjectAnimator.ofFloat(iv, "scaleY", 1.5f, 1f) + setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + builder.view.post { + + // 如果没有设置padding,则把iv的宽高设置为view的宽高 + val params = iv.layoutParams + if (builder.view.paddingBottom == 0 && builder.view.paddingTop == 0 && builder.view.paddingLeft == 0 && builder.view.paddingRight == 0) { + params.width = builder.width + params.height = builder.height + } else { + // 如果设置了padding,则获取图片原始的大小 + params.width = builder.view.drawable.bounds.width() + params.height = builder.view.drawable.bounds.height() + } + + iv.setImageResource(builder.imgRes) + iv.layoutParams = params + + + + width = params.width * SCALE_FACTOR + height = params.height * SCALE_FACTOR + + cv.setColor(builder.color1, builder.color2) + dv.setColor(builder.color1, builder.color2) + + cv.setSize(width, height) + dv.setSize(width, height) + + animSet.duration = 600 + animSet.interpolator = BounceInterpolator() // 设置插值器 + animSet.playTogether(imgScaleXAnim, imgScaleYAnim) + animSet.addListener(object : Animator.AnimatorListener { + override fun onAnimationRepeat(animation: Animator?) { + + } + + override fun onAnimationCancel(animation: Animator?) { + } + + override fun onAnimationStart(animation: Animator?) { + + } + + override fun onAnimationEnd(animation: Animator?) { + dismiss() + } + + }) + } + + } + + fun show() { +// if(cv.isAnimRunning()){ +// cv.cancelAnim() +// animSet.cancel() +// dismiss() +// } + val viewLocation = IntArray(2) + builder.view.getLocationInWindow(viewLocation) + val x = viewLocation[0] + builder.width / 2 - width / 2 + val y = viewLocation[1] + builder.height / 2 - height / 2 + showAtLocation(iv, Gravity.NO_GRAVITY, x, y) + cv.startAnim() + animSet.start() + } + + class Builder(val view: ImageView, val imgRes: Int) { + + internal var color1 = Color.parseColor("#48CFC2") + internal var color2 = Color.parseColor("#5BA2E9") + + internal var width: Int = 0 + internal var height: Int = 0 + + init { + view.post { + width = view.width + height = view.height + } + } + + /** + * 设置颜色 + */ + fun setColor(color1: Int, color2: Int) { + this.color1 = color1 + this.color2 = color2 + } + + + fun create(): WJueJinLikeAnim { + return WJueJinLikeAnim(view.context, this) + } + } +} \ No newline at end of file diff --git a/lib/src/main/res/layout/view_jue_jin.xml b/lib/src/main/res/layout/view_jue_jin.xml new file mode 100644 index 0000000..4856d9a --- /dev/null +++ b/lib/src/main/res/layout/view_jue_jin.xml @@ -0,0 +1,25 @@ + + + + + + + + + + \ No newline at end of file diff --git a/lib/src/main/res/values/attr.xml b/lib/src/main/res/values/attr.xml index bf6ffc4..b86edc8 100644 --- a/lib/src/main/res/values/attr.xml +++ b/lib/src/main/res/values/attr.xml @@ -1,6 +1,6 @@ - +