/** * https://blog.youkuaiyun.com/willway_wang/article/details/126698866 * @author wangzhichao * @since 2022/9/4 * */ private const val TAG = "FluidColorfulFrame" class FluidColorfulFrameDrawable : Drawable() { private val paint = Paint(Paint.ANTI_ALIAS_FLAG) private lateinit var bounds: RectF private val rectF = RectF() private val defaultRadius: Float = 10F private val defaultStrokeWidth: Float = 5F private val colors: IntArray private val positions: FloatArray private val mtx = Matrix() private var degree: Float = 0f set(value) { field = value Log.d(TAG, "degree setter called") invalidateSelf() } init { paint.style = Paint.Style.STROKE paint.strokeWidth = defaultStrokeWidth colors = intArrayOf( Color.parseColor("#FF0000FF"), // 蓝 0f Color.parseColor("#FF000000"), // 黑 0.02f Color.parseColor("#FF000000"), // 黑 0.25f Color.parseColor("#FFFF0000" ), // 红 0.27f Color.parseColor("#FFFF0000" ), // 红 0.37f Color.parseColor("#FF00FF00" ), // 绿 0.39f Color.parseColor("#FF0000FF" ), // 蓝 0.49f Color.parseColor("#FFFFFF00" ), // 黄 0.51f Color.parseColor("#FF000000" ), // 黑 0.53f Color.parseColor("#FF000000" ), // 黑 0.75f Color.parseColor("#FFFF0000" ), // 红 0.77f Color.parseColor("#FFFF0000" ), // 红 0.87f Color.parseColor("#FFFFFF00" ), // 黄 0.91f Color.parseColor("#FF0000FF" ), // 蓝 0.96f ) positions = floatArrayOf( 0f, 0.02f, 0.25f, 0.27f, 0.37f, 0.39f, 0.49f, 0.51f, 0.53f, 0.75f, 0.77f, 0.87f, 0.91f, 0.96f, ) } override fun setBounds(left: Int, top: Int, right: Int, bottom: Int) { super.setBounds(left, top, right, bottom) bounds = RectF(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat()) rectF.left = defaultStrokeWidth / 2 rectF.top = defaultStrokeWidth / 2 rectF.right = bounds.width() - defaultStrokeWidth / 2 rectF.bottom = bounds.height() - defaultStrokeWidth / 2 paint.shader = SweepGradient(bounds.centerX(), bounds.centerY(), colors, positions) } override fun draw(canvas: Canvas) { Log.d(TAG, "draw: ") mtx.reset() mtx.setRotate(degree, bounds.centerX(), bounds.centerY()) (paint.shader as SweepGradient).setLocalMatrix(mtx) canvas.drawRoundRect(rectF, defaultRadius, defaultRadius, paint) } override fun setAlpha(alpha: Int) { paint.alpha = alpha } override fun setColorFilter(colorFilter: ColorFilter?) { paint.colorFilter = colorFilter } override fun getOpacity(): Int { return PixelFormat.TRANSLUCENT } private var fluidAnim: ObjectAnimator? = null fun startFluid() { fluidAnim = ObjectAnimator.ofFloat(this, "degree", 0f, 360f).apply { duration = 2000L interpolator = LinearInterpolator() repeatCount = ValueAnimator.INFINITE start() } } fun cancelFluid() { fluidAnim?.cancel() fluidAnim = null; } }
public class MyImageView extends androidx.appcompat.widget.AppCompatImageView { private FluidColorfulFrameDrawable drawable = new FluidColorfulFrameDrawable(); public MyImageView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); drawable.setCallback(this); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 绘制 Drawable drawable.draw(canvas); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // 设置 Drawable 的范围 drawable.setBounds(0, 0, w, h); drawable.startFluid(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); drawable.cancelFluid(); } @Override public void invalidateDrawable(@NonNull Drawable dr) { super.invalidateDrawable(dr); if (dr == drawable) { invalidate(); } } }
xml中使用MyImageView查看动画效果