Android简单实现汉字笔顺动画——Java版

在掘金上看到一篇实现汉字笔顺动画的文章很感兴趣,其代码采用kotlin实现,阅读不便,把相关实现类提出来翻译成了Java,有兴趣的可以看下。

先上原文链接:

Android修炼系列(41),简单实现个汉字笔顺动画icon-default.png?t=M4ADhttps://juejin.cn/post/7103192601515425823

改写后,相关实现类只保留一个Activity和一个自定义View,方便阅读。直接贴源码和效果:

1.主Activity

package com.example.myapplication;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class StrokeOrderActivityNew extends Activity {
    String svgSix = null;
    String svgOne = null;
    StrokeOrderView strokeOrderView1;
    StrokeOrderView strokeOrderView2;
    Button btnSix;
    Button btnOne;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stroke_order_layout);

        strokeOrderView1 = (StrokeOrderView)findViewById(R.id.stroke_order_view1);
        strokeOrderView2 = (StrokeOrderView)findViewById(R.id.stroke_order_view2);
        btnSix = (Button)findViewById(R.id.btn_load_svg_six);
        btnSix.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String name = "六.json"; // 需要将 svg.json 放在 assets 或特定路径下
                svgSix = loadSvgJson(name);
                strokeOrderView1.setStrokesBySvg(svgSix);
            }
        });

        btnOne = (Button)findViewById(R.id.btn_load_svg_one);
        btnOne.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String name = "一.json";
                svgOne = loadSvgJson(name);
                strokeOrderView2.setStrokesBySvg(svgOne);
            }
        });
    }

    private String loadSvgJson(String file) {
        BufferedReader reader = null;
        InputStreamReader inputStreamReader = null;
        try {
            InputStream inputStream = getAssets().open(file);
            inputStreamReader = new InputStreamReader(inputStream);
            reader = new BufferedReader(inputStreamReader);
            String line;
            StringBuilder entity = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                entity.append(line);
            }
            return entity.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                inputStreamReader.close();
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

2.自定义View,负责实现动画绘制效果

package com.example.myapplication;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.*;
import android.graphics.PathMeasure;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.core.graphics.PathParser;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;

public class StrokeOrderView extends View {

    static float SVG_STROKE_WIDTH = 1024F;
    static float SVG_STROKE_HEIGHT = 1024F;

    private ArrayList strokePaths = new ArrayList();
    private ArrayList medians = new ArrayList();
    private Paint strokePaint = new Paint();
    private Paint medianPaint = new Paint();
    private ArrayList medianMeasures = new ArrayList();
    private Path tempPath = new Path();
    private float progress = 0F;
    private int currIndex = 0;
    private ArrayList points = new ArrayList();

    Bitmap srcBmp = null;
    Canvas srcCanvas = null;
    Paint srcPaint = new Paint();

    Bitmap dstBmp = null;
    Canvas dstCanvas = null;
    Paint dstPaint = new Paint();

    PorterDuffXfermode clearMode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
    PorterDuffXfermode srcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
    PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);

    public StrokeOrderView(Context context) {
        super(context);
    }

    public StrokeOrderView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        strokePaint.setAntiAlias(true);
        strokePaint.setStyle(Paint.Style.FILL);
        strokePaint.setColor(Color.RED);

        medianPaint.setAntiAlias(true);
        medianPaint.setStyle(Paint.Style.FILL);
        medianPaint.setColor(Color.BLACK);

        srcPaint.setAntiAlias(true);
        srcPaint.setStrokeWidth(100f);
        srcPaint.setStyle(Paint.Style.STROKE);
        srcPaint.setColor(Color.BLACK);

        dstPaint.setAntiAlias(true);
        dstPaint.setStyle(Paint.Style.FILL);
        dstPaint.setColor(Color.BLACK);
    }

    public StrokeOrderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public StrokeOrderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    void setStrokesBySvg(String svgJson){
        strokePaths.clear();
        medians.clear();
        medianMeasures.clear();
        points.clear();
        ArrayList strokes = new ArrayList();
        parseSvgJson(svgJson, strokes, medians, points);
        for(int i=0; i=0; i--){
            if(i <= currIndex && currIndex < strokePaths.size()){
                dstCanvas.drawPath(strokePaths.get(i), dstPaint);
            }
        }

        dstCanvas.restoreToCount(c1);
        dstPaint.setXfermode(null);
        canvas.drawBitmap(dstBmp, 0F, 0F, medianPaint);

        // 在两者相交的地方绘制源图像
        medianPaint.setXfermode(porterDuffXfermode);
        // src bitmap
        if (srcBmp == null) {
            srcBmp = Bitmap.createBitmap((int)w, (int)h, Bitmap.Config.ARGB_8888);
            srcCanvas = new Canvas(srcBmp);
        }
        srcPaint.setXfermode(srcMode);// 只保留 srcBmp 的 alpha 和 color ,所以绘制出来只有源图

        int c2 = srcCanvas.save();
        srcCanvas.scale(1F, -1F);
        srcCanvas.translate(0F, -SVG_STROKE_HEIGHT * 7 / 8);
        srcCanvas.scale(xTmp, yTmp, 0F, SVG_STROKE_HEIGHT * 7 / 8);
        if (!medianMeasures.isEmpty()) { // 绘制当前进度下的笔画
            // 起笔落笔 都添加一个圆,防止不完整
            drawBackbonePointCircle(currIndex * 2, 20F);
            if (progress > 0.99) {
                drawBackbonePointCircle(currIndex * 2 + 1, 30F);
            }

            tempPath.reset();
            PathMeasure m = medianMeasures.get(currIndex);
            m.getSegment(0F, m.getLength() * progress, tempPath, true);
            srcCanvas.drawPath(tempPath, srcPaint);
        }
        srcCanvas.restoreToCount(c2);
        srcPaint.setXfermode(null);
        canvas.drawBitmap(srcBmp, 0F, 0F, medianPaint);
        medianPaint.setXfermode(null);

        canvas.restoreToCount(layer);
    }

    void parseSvgJson(String json, ArrayList list, ArrayList paths, ArrayList points){
        try {
            JSONObject obj = new JSONObject(json);
            JSONArray array = obj.getJSONArray("strokes");
            for(int i=0; i animators = new ArrayList();
        for(int i=0; i

3.补全布局文件:




    
        
            

4.注意Assert下放置汉字的json文件,json文件不方便贴,大家可以从github上获取。

hanzi-writer-dataicon-default.png?t=M4ADhttps://github.com/chanind/hanzi-writer-data

 

你可能感兴趣的:(Android开发,android,动画,kotlin)