Android draws a five-pointed star and realizes the shining effect of the stars

Effect

Let’s take a look at the effect first:
insert image description here
the display speed of the five-pointed star gif is slower than the actual one, and the duration of the flashing can be customized in the code.

Custom View

ShiningStarViewCustom Kotlin code:

import android.animation.ValueAnimator
import android.animation.ValueAnimator.AnimatorUpdateListener
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.animation.Animation
import android.view.animation.LinearInterpolator

class ShiningStarView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr), AnimatorUpdateListener {
    
    
    private val mStarRadius: Float
    private val mStarPaint: Paint
    private var mStarPath: Path? = null
    private var mAlphaValue = 255

    init {
    
    
        val a = context.obtainStyledAttributes(attrs, R.styleable.ShiningStarView)
        val mStarColor = a.getColor(R.styleable.ShiningStarView_starColor, Color.YELLOW)
        mStarRadius = a.getDimension(R.styleable.ShiningStarView_starRadius, 90f)
        a.recycle()
        mStarPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        mStarPaint.color = mStarColor
        mStarPaint.maskFilter = BlurMaskFilter(mStarRadius / 3, BlurMaskFilter.Blur.SOLID) //外发光效果
        val circleAlphaValueAnimator = ValueAnimator.ofInt(0, 255)
        circleAlphaValueAnimator.duration = 1000
        circleAlphaValueAnimator.repeatCount = Animation.INFINITE
        circleAlphaValueAnimator.repeatMode = ValueAnimator.REVERSE
        circleAlphaValueAnimator.interpolator = LinearInterpolator()
        circleAlphaValueAnimator.addUpdateListener(this)
        circleAlphaValueAnimator.start()
    }

    override fun onDraw(canvas: Canvas) {
    
    
        super.onDraw(canvas)
        canvas.save()
        mStarPaint.alpha = mAlphaValue
        canvas.drawPath(mStarPath!!, mStarPaint)
        canvas.restore()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    
    
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        var width = MeasureSpec.getSize(widthMeasureSpec)
        var height = MeasureSpec.getSize(heightMeasureSpec)
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(widthMeasureSpec)
        if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
    
    
            width = mStarRadius.toInt() * 2
        }
        if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
    
    
            height = mStarRadius.toInt() * 2
        }
        mStarPath = calculateStarPath(mStarRadius)
        setMeasuredDimension(width, height)
    }

    private fun calculateStarPath(radius: Float): Path {
    
    
        val starPath = Path()
        //360度除以5个角, 比如5个角 每个角72度,利用cos 、sin计算位置 1度=π/180 以顶角尖作为坐标轴原点
        val outRadian = Math.PI / 180.0 * (90.0 - 360.0 / 5 / 2) //右角尖与x轴的弧度
        val pRadian = (1.0 - 0.5) * outRadian //钝角点与x轴的弧度
        val pRightRadian = 0.5 * outRadian //钝角点到原点与右角尖的弧度
        val centerLength = radius * Math.sin(Math.PI / 5) //右角尖到顶角间的中心点到原点的距离
        val pLength = centerLength / Math.cos(pRightRadian) //钝角点到原点距离
        val pX = pLength * Math.sin(pRadian)
        val pY = pLength * Math.cos(pRadian)
        starPath.moveTo(0f, 0f)
        for (i in 0..4) {
    
    
            val angle = Math.PI / 180 * 360 / 5 * i //每次旋转坐标轴角度 angle== π/180 * (360 / 5)
            val sinA = Math.sin(angle)
            val cosA = Math.cos(angle)
            val rX2 = (radius * sinA).toFloat()
            val rY2 = (-radius * cosA + radius).toFloat()
            val pX2 = (pX * cosA - (pY - radius) * sinA).toFloat()
            val pY2 = ((pY - radius) * cosA + pX * sinA + radius).toFloat()
            starPath.lineTo(rX2, rY2)
            starPath.lineTo(pX2, pY2)
        }
        starPath.close()
        val matrix = Matrix()
        //坐标系平移
        matrix.setTranslate(radius, 0f)
        starPath.transform(matrix)
        return starPath
    }

    override fun onAnimationUpdate(animation: ValueAnimator) {
    
    
        mAlphaValue = animation.animatedValue as Int
        invalidate()
    }
}

ShiningStarViewCustom Java code:

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;

import androidx.annotation.Nullable;

public class ShiningStarView extends View implements ValueAnimator.AnimatorUpdateListener {
    
    
    private final float mStarRadius;
    private final Paint mStarPaint;
    private Path mStarPath;
    private int mAlphaValue = 255;

    public ShiningStarView(Context context) {
    
    
        this(context, null);
    }

    public ShiningStarView(Context context, @Nullable AttributeSet attrs) {
    
    
        this(context, attrs, 0);
    }

    public ShiningStarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    
    
        super(context,attrs,defStyleAttr);
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ShiningStarView);
        int mStarColor = a.getColor(R.styleable.ShiningStarView_starColor, Color.YELLOW);
        mStarRadius = a.getDimension(R.styleable.ShiningStarView_starRadius, 90);
        a.recycle();

        mStarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mStarPaint.setColor(mStarColor);
        mStarPaint.setMaskFilter(new BlurMaskFilter(mStarRadius / 3, BlurMaskFilter.Blur.SOLID)); //外发光效果

        ValueAnimator circleAlphaValueAnimator = ValueAnimator.ofInt(0, 255);
        circleAlphaValueAnimator.setDuration(1000);
        circleAlphaValueAnimator.setRepeatCount(Animation.INFINITE);
        circleAlphaValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
        circleAlphaValueAnimator.setInterpolator(new LinearInterpolator());
        circleAlphaValueAnimator.addUpdateListener(this);
        circleAlphaValueAnimator.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
    
    
        super.onDraw(canvas);
        canvas.save();
        mStarPaint.setAlpha(mAlphaValue);
        canvas.drawPath(mStarPath, mStarPaint);
        canvas.restore();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(widthMeasureSpec);
        if(widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
    
    
            width = (int) mStarRadius * 2;
        }
        if(heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
    
    
            height = (int) mStarRadius * 2;
        }
        mStarPath = calculateStarPath(mStarRadius);

        setMeasuredDimension(width, height);
    }

    private Path calculateStarPath(float radius){
    
    
        Path starPath = new Path();
        //360度除以5个角, 比如5个角 每个角72度,利用cos 、sin计算位置 1度=π/180 以顶角尖作为坐标轴原点
        double outRadian = Math.PI / 180.0 * (90.0 - 360.0 / 5 / 2); //右角尖与x轴的弧度
        double pRadian = (1.0 - 0.5) * outRadian; //钝角点与x轴的弧度
        double pRightRadian = 0.5 * outRadian; //钝角点到原点与右角尖的弧度

        double centerLength = radius * Math.sin(Math.PI / 5); //右角尖到顶角间的中心点到原点的距离
        double pLength = centerLength / Math.cos(pRightRadian); //钝角点到原点距离
        double pX = pLength * Math.sin(pRadian);
        double pY = pLength * Math.cos(pRadian);
        starPath.moveTo(0,0);

        for (int i = 0;i < 5;i++){
    
    
            double angle = Math.PI / 180 * 360 / 5 * i; //每次旋转坐标轴角度 angle== π/180 * (360 / 5)
            double sinA = Math.sin(angle);
            double cosA = Math.cos(angle);
            float rX2 = (float) (radius * sinA);
            float rY2 = (float) (-radius * cosA + radius);
            float pX2 = (float) (pX * cosA - (pY - radius) * sinA);
            float pY2 = (float) ((pY - radius) * cosA + pX*sinA + radius);
            starPath.lineTo(rX2,rY2);
            starPath.lineTo(pX2,pY2);
        }
        starPath.close();
        Matrix matrix = new Matrix();
        //坐标系平移
        matrix.setTranslate(radius,0);
        starPath.transform(matrix);
        return starPath;
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
    
    
        mAlphaValue = (int) animation.getAnimatedValue();
        invalidate();
    }
}

By BlurMaskFilterrealizing the outer glow effect, the coordinate axis takes the vertex as the origin to perform mathematical calculations to obtain Paththe trajectory of the five-pointed star. Calculate the side length according to radians (1 angle = π/180 radians), and calculate the corresponding coordinates with the help of formulas such as sin and cos according to the symmetry principle of the five-pointed star and the principle of a right-angled triangle. Loop drawing through animation, drawing with different transparency to achieve sparkling effect. The attribute definition attrs.xml
of the custom View is as follows:

<resources>
    <declare-styleable name="ShiningStarView">
        <attr name="starRadius" format="dimension"/>
        <attr name="starColor" format="color"/>
    </declare-styleable>
</resources>

Among them, the radius of the five-pointed star is defined by starRadiusthe attribute (this is used as the size); starColorthe color of the five-pointed star is defined by the attribute.
Defined in the interface as follows:

    <com.example.customui.ShiningStarView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:starRadius="30dp"
        app:starColor="@color/yellow"
        />

Guess you like

Origin blog.csdn.net/CJohn1994/article/details/127974742