Android 自定义View 之 圆环进度条

文章目录
  •   很多时候我们会使用进度条,而Android默认的进度条是长条的,从左至右。而在日常开发中,有时候UI为了让页面更美观,就需要用到圆环进度条,那么本文就是通过自定义写一个圆环进度条,首先看一下效果图:
  •   关于自定义View的基础知识就不再做过多的讲解了,我们直接进入正题,这一次我们不需要再去创建项目了,就用我之前创建的EasyView。
  •   根据上面的效果图,我们首先来确定XML中的属性样式,修改attrs.xml的代码如下所示: <?xml version="1.0" encoding="utf-8"?> <resources> <!--文字颜色--> <attr name="textColor" format="color|reference" /> <!--文字大小--> <attr name="textSize" format="dimension" /> <!--蓝牙地址输入控件--> <declare-styleable name="MacAddressEditText"> <!-- 方框大小,宽高一致 --> <attr name="boxWidth" format="dimension" /> <!-- 方框背景颜色 --> <attr name="boxBackgroundColor" format="color|reference" /> <!-- 方框描边颜色 --> <attr name="boxStrokeColor" format="color|reference" /> <!-- 方框描边宽度 --> <attr name="boxStrokeWidth" format="dimension" /> <!--文字颜色--> <attr name="textColor" /> <!--文字大小--> <attr name="textSize" /> <!--分隔符,: 、- --> <attr name="separator" format="string|reference" /> </declare-styleable> <!--圆形进度条控件--> <declare-styleable name="CircularProgressBar"> <!--半径--> <attr name="radius" format="dimension" /> <!--进度条宽度--> <attr name="strokeWidth" format="dimension" /> <!--进度条背景颜色--> <attr name="progressbarBackgroundColor" format="color|reference" /> <!--进度条进度颜色--> <attr name="progressbarColor" format="color|reference" /> <!--最大进度--> <attr name="maxProgress" format="integer" /> <!--当前进度--> <attr name="progress" format="integer" /> <!--文字--> <attr name="text" format="string" /> <!--文字颜色--> <attr name="textColor" /> <!--文字大小--> <attr name="textSize" /> </declare-styleable> </resources>   这里你会发现一个改变,那就是文字颜色和文字大小的属性从之前的declare-styleable中抽出来了,因为我们可能多个自定义控件会用到同样的属性,那么根据属性不可重名的原则,我们需要抽离出来,然后在declare-styleable引用。
  •   现在属性样式已经有了,下一步就是写自定义View的构造方法了,在com.llw.easyview包下新建一个CircularProgressBar类,里面的代码如下所示: public class CircularProgressBar extends View { /** * 半径 */ private int mRadius; /** * 进度条宽度 */ private int mStrokeWidth; /** * 进度条背景颜色 */ private int mProgressbarBgColor; /** * 进度条进度颜色 */ private int mProgressColor; /** * 开始角度 */ private int mStartAngle = 0; /** * 当前角度 */ private float mCurrentAngle = 0; /** * 结束角度 */ private int mEndAngle = 360; /** * 最大进度 */ private float mMaxProgress; /** * 当前进度 */ private float mCurrentProgress; /** * 文字 */ private String mText; /** * 文字颜色 */ private int mTextColor; /** * 文字大小 */ private float mTextSize; /** * 动画的执行时长 */ private long mDuration = 1000; /** * 是否执行动画 */ private boolean isAnimation = false; public CircularProgressBar(Context context) { this(context, null); } public CircularProgressBar(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public CircularProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircularProgressBar); mRadius = array.getDimensionPixelSize(R.styleable.CircularProgressBar_radius, 80); mStrokeWidth = array.getDimensionPixelSize(R.styleable.CircularProgressBar_strokeWidth, 8); mProgressbarBgColor = array.getColor(R.styleable.CircularProgressBar_progressbarBackgroundColor, ContextCompat.getColor(context, R.color.teal_700)); mProgressColor = array.getColor(R.styleable.CircularProgressBar_progressbarColor, ContextCompat.getColor(context, R.color.teal_200)); mMaxProgress = array.getInt(R.styleable.CircularProgressBar_maxProgress, 100); mCurrentProgress = array.getInt(R.styleable.CircularProgressBar_progress, 0); String text = array.getString(R.styleable.CircularProgressBar_text); mText = text == null ? "" : text; mTextColor = array.getColor(R.styleable.CircularProgressBar_textColor, ContextCompat.getColor(context, R.color.black)); mTextSize = array.getDimensionPixelSize(R.styleable.CircularProgressBar_textSize, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics())); array.recycle(); } }   这里声明了一些变量,然后写了3个构造方法,在第三个构造方法中进行属性的赋值。
  •   这里测量就比较简单了,当然这是相对于之前的那个Mac地址输入框来说的,代码如下所示: @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = 0; switch (MeasureSpec.getMode(widthMeasureSpec)) { case MeasureSpec.UNSPECIFIED: case MeasureSpec.AT_MOST: //wrap_content width = mRadius * 2; break; case MeasureSpec.EXACTLY: //match_parent width = MeasureSpec.getSize(widthMeasureSpec); break; } //Set the measured width and height setMeasuredDimension(width, width); }   因为不需要进行子控件处理,所以我们只要一个圆环就可以了,文字在圆环中间绘制,下面再看绘制的方法。
  •   绘制这里稍微代码量多一些,因为需要绘制的内容有进度条背景、进度条、中间文字三个,绘制的代码如下所示: @Override protected void onDraw(Canvas canvas) { int centerX = getWidth() / 2; RectF rectF = new RectF(); rectF.left = mStrokeWidth; rectF.top = mStrokeWidth; rectF.right = centerX * 2 - mStrokeWidth; rectF.bottom = centerX * 2 - mStrokeWidth; //绘制进度条背景 drawProgressbarBg(canvas, rectF); //绘制进度 drawProgress(canvas, rectF); //绘制中心文本 drawCenterText(canvas, centerX); }   在绘制之前首先要确定中心点,因为我们是一个圆环,实际上也是一个圆,圆的宽高一样,所以中心点的x、y轴的位置就是一样的,然后是确定一个矩形的左上和右下两个位置的坐标点,通过这两个点就能绘制一个矩形,接下来就是绘制进度条背景。
  •   还需要提供一些方法在代码中调用,下面是这些方法的代码: /** * 设置当前进度 */ public void setProgress(float progress) { if (progress < 0) { throw new IllegalArgumentException("Progress value can not be less than 0"); } if (progress > mMaxProgress) { progress = mMaxProgress; } mCurrentProgress = progress; mCurrentAngle = 360 * (mCurrentProgress / mMaxProgress); setAnimator(0, mCurrentAngle); } /** * 设置文本 */ public void setText(String text) { mText = text; } /** * 设置文本的颜色 */ public void setTextColor(int color) { if (color <= 0) { throw new IllegalArgumentException("Color value can not be less than 0"); } mTextColor = color; } /** * 设置文本的大小 */ public void setTextSize(float textSize) { if (textSize <= 0) { throw new IllegalArgumentException("textSize can not be less than 0"); } mTextSize = textSize; } /** * 设置动画 * * @param start 开始位置 * @param target 结束位置 */ private void setAnimator(float start, float target) { isAnimation = true; ValueAnimator animator = ValueAnimator.ofFloat(start, target); animator.setDuration(mDuration); animator.setTarget(mCurrentAngle); //动画更新监听 animator.addUpdateListener(valueAnimator -> { mCurrentAngle = (float) valueAnimator.getAnimatedValue(); invalidate(); }); animator.start(); }   那么到此为止这个自定义View就完成了,下面我们可以在MainActivity中使用了。
  •   先修改activity_main.xml的代码,如下所示: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:padding="16dp" tools:context=".MainActivity"> <com.easy.view.MacAddressEditText android:id="@+id/mac_et" android:layout_width="wrap_content" android:layout_height="wrap_content" app:boxBackgroundColor="@color/white" app:boxStrokeColor="@color/black" app:boxStrokeWidth="2dp" app:boxWidth="48dp" app:separator=":" app:textColor="@color/black" app:textSize="16sp" /> <Button android:id="@+id/btn_mac" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:text="获取地址" /> <com.easy.view.CircularProgressBar android:id="@+id/cpb_test" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" app:maxProgress="100" app:progress="10" app:progressbarBackgroundColor="@color/purple_500" app:progressbarColor="@color/purple_200" app:radius="80dp" app:strokeWidth="16dp" app:text="10%" app:textColor="@color/teal_200" app:textSize="28sp" /> <Button android:id="@+id/btn_set_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:text="随机设置进度" /> </LinearLayout> 首先要注意看是否能够预览,我这里是可以预览的,如下图所示: 在MainActivity中使用,修改onCreate()方法中的代码,如下所示: //圆形进度条操作 CircularProgressBar cpbTest = findViewById(R.id.cpb_test); Button btnSetProgress = findViewById(R.id.btn_set_progress); btnSetProgress.setOnClickListener(v -> { int progress = Math.abs(new Random().nextInt() % 100); Toast.makeText(this, "" + progress, Toast.LENGTH_SHORT).show(); cpbTest.setText(progress + "%"); cpbTest.setProgress(progress); }); 运行效果如下图所示:
  •   顺便说一下,我打算把这个项目做成一个开源仓库,提交到mavenCentral()中,后面使用的话就可以通过这个引入依赖的方式进行调用,会很方便,后面会单独出一篇文章讲述这个仓库。 如果对你有所帮助的话,不妨 Star 或 Fork,山高水长,后会有期~ 源码地址:EasyView
    • 前言
    • 正文
    • 一、XML样式
    • 二、构造方法
    • 三、测量
    • 四、绘制
    • ① 绘制进度条背景
    • ② 绘制进度
    • ③ 绘制文字
    • 五、API方法
    • 六、使用
    • 七、源码

      很多时候我们会使用进度条,而Android默认的进度条是长条的,从左至右。而在日常开发中,有时候UI为了让页面更美观,就需要用到圆环进度条,那么本文就是通过自定义写一个圆环进度条,首先看一下效果图:

    Android 自定义View 之 圆环进度条

      关于自定义View的基础知识就不再做过多的讲解了,我们直接进入正题,这一次我们不需要再去创建项目了,就用我之前创建的EasyView。

      根据上面的效果图,我们首先来确定XML中的属性样式,修改attrs.xml的代码如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
     <!--文字颜色-->
     <attr name="textColor" format="color|reference" />
     <!--文字大小-->
     <attr name="textSize" format="dimension" />
     <!--蓝牙地址输入控件-->
     <declare-styleable name="MacAddressEditText">
     <!-- 方框大小,宽高一致 -->
     <attr name="boxWidth" format="dimension" />
     <!-- 方框背景颜色 -->
     <attr name="boxBackgroundColor" format="color|reference" />
     <!-- 方框描边颜色 -->
     <attr name="boxStrokeColor" format="color|reference" />
     <!-- 方框描边宽度 -->
     <attr name="boxStrokeWidth" format="dimension" />
     <!--文字颜色-->
     <attr name="textColor" />
     <!--文字大小-->
     <attr name="textSize" />
     <!--分隔符,: 、- -->
     <attr name="separator" format="string|reference" />
     </declare-styleable>
     <!--圆形进度条控件-->
     <declare-styleable name="CircularProgressBar">
     <!--半径-->
     <attr name="radius" format="dimension" />
     <!--进度条宽度-->
     <attr name="strokeWidth" format="dimension" />
     <!--进度条背景颜色-->
     <attr name="progressbarBackgroundColor" format="color|reference" />
     <!--进度条进度颜色-->
     <attr name="progressbarColor" format="color|reference" />
     <!--最大进度-->
     <attr name="maxProgress" format="integer" />
     <!--当前进度-->
     <attr name="progress" format="integer" />
     <!--文字-->
     <attr name="text" format="string" />
     <!--文字颜色-->
     <attr name="textColor" />
     <!--文字大小-->
     <attr name="textSize" />
     </declare-styleable>
    </resources>

      这里你会发现一个改变,那就是文字颜色和文字大小的属性从之前的declare-styleable中抽出来了,因为我们可能多个自定义控件会用到同样的属性,那么根据属性不可重名的原则,我们需要抽离出来,然后在declare-styleable引用。

    Android 自定义View 之 圆环进度条

      现在属性样式已经有了,下一步就是写自定义View的构造方法了,在com.llw.easyview包下新建一个CircularProgressBar类,里面的代码如下所示:

    public class CircularProgressBar extends View {
    
     /**
     * 半径
     */
     private int mRadius;
     /**
     * 进度条宽度
     */
     private int mStrokeWidth;
    
     /**
     * 进度条背景颜色
     */
     private int mProgressbarBgColor;
    
     /**
     * 进度条进度颜色
     */
     private int mProgressColor;
    
     /**
     * 开始角度
     */
     private int mStartAngle = 0;
    
     /**
     * 当前角度
     */
     private float mCurrentAngle = 0;
    
     /**
     * 结束角度
     */
     private int mEndAngle = 360;
     /**
     * 最大进度
     */
     private float mMaxProgress;
     /**
     * 当前进度
     */
     private float mCurrentProgress;
     /**
     * 文字
     */
     private String mText;
     /**
     * 文字颜色
     */
     private int mTextColor;
     /**
     * 文字大小
     */
     private float mTextSize;
     /**
     * 动画的执行时长
     */
     private long mDuration = 1000;
     /**
     * 是否执行动画
     */
     private boolean isAnimation = false;
     
     public CircularProgressBar(Context context) {
     this(context, null);
     }
    
     public CircularProgressBar(Context context, @Nullable AttributeSet attrs) {
     this(context, attrs, 0);
     }
    
     public CircularProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
     super(context, attrs, defStyleAttr);
     TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircularProgressBar);
     mRadius = array.getDimensionPixelSize(R.styleable.CircularProgressBar_radius, 80);
     mStrokeWidth = array.getDimensionPixelSize(R.styleable.CircularProgressBar_strokeWidth, 8);
     mProgressbarBgColor = array.getColor(R.styleable.CircularProgressBar_progressbarBackgroundColor, ContextCompat.getColor(context, R.color.teal_700));
     mProgressColor = array.getColor(R.styleable.CircularProgressBar_progressbarColor, ContextCompat.getColor(context, R.color.teal_200));
     mMaxProgress = array.getInt(R.styleable.CircularProgressBar_maxProgress, 100);
     mCurrentProgress = array.getInt(R.styleable.CircularProgressBar_progress, 0);
     String text = array.getString(R.styleable.CircularProgressBar_text);
     mText = text == null ? "" : text;
     mTextColor = array.getColor(R.styleable.CircularProgressBar_textColor, ContextCompat.getColor(context, R.color.black));
     mTextSize = array.getDimensionPixelSize(R.styleable.CircularProgressBar_textSize, (int) TypedValue
     .applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));
     array.recycle();
     }
    }

      这里声明了一些变量,然后写了3个构造方法,在第三个构造方法中进行属性的赋值。

      这里测量就比较简单了,当然这是相对于之前的那个Mac地址输入框来说的,代码如下所示:

    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     int width = 0;
     switch (MeasureSpec.getMode(widthMeasureSpec)) {
     case MeasureSpec.UNSPECIFIED:
     case MeasureSpec.AT_MOST: //wrap_content
     width = mRadius * 2;
     break;
     case MeasureSpec.EXACTLY: //match_parent
     width = MeasureSpec.getSize(widthMeasureSpec);
     break;
     }
     //Set the measured width and height
     setMeasuredDimension(width, width);
     }

      因为不需要进行子控件处理,所以我们只要一个圆环就可以了,文字在圆环中间绘制,下面再看绘制的方法。

      绘制这里稍微代码量多一些,因为需要绘制的内容有进度条背景、进度条、中间文字三个,绘制的代码如下所示:

    @Override
     protected void onDraw(Canvas canvas) {
     int centerX = getWidth() / 2;
     RectF rectF = new RectF();
     rectF.left = mStrokeWidth;
     rectF.top = mStrokeWidth;
     rectF.right = centerX * 2 - mStrokeWidth;
     rectF.bottom = centerX * 2 - mStrokeWidth;
    
     //绘制进度条背景
     drawProgressbarBg(canvas, rectF);
     //绘制进度
     drawProgress(canvas, rectF);
     //绘制中心文本
     drawCenterText(canvas, centerX);
     }

      在绘制之前首先要确定中心点,因为我们是一个圆环,实际上也是一个圆,圆的宽高一样,所以中心点的x、y轴的位置就是一样的,然后是确定一个矩形的左上和右下两个位置的坐标点,通过这两个点就能绘制一个矩形,接下来就是绘制进度条背景。

    /**
     * 绘制进度条背景
     */
     private void drawProgressbarBg(Canvas canvas, RectF rectF) {
     Paint mPaint = new Paint();
     //画笔的填充样式,Paint.Style.STROKE 描边
     mPaint.setStyle(Paint.Style.STROKE);
     //圆弧的宽度
     mPaint.setStrokeWidth(mStrokeWidth);
     //抗锯齿
     mPaint.setAntiAlias(true);
     //画笔的颜色
     mPaint.setColor(mProgressbarBgColor);
     //画笔的样式 Paint.Cap.Round 圆形
     mPaint.setStrokeCap(Paint.Cap.ROUND);
     //开始画圆弧
     canvas.drawArc(rectF, mStartAngle, mEndAngle, false, mPaint);
     }

      因为背景是一个圆环,所以这里的画笔设置就比较注意一些,看一下就会了,这里最重要的是drawArc,用于绘制圆弧,像下图这样,画了4/1的背景。

    Android 自定义View 之 圆环进度条

    下面绘制进度

    /**
     * 绘制进度
     */
     private void drawProgress(Canvas canvas, RectF rectF) {
     Paint paint = new Paint();
     paint.setStyle(Paint.Style.STROKE);
     paint.setStrokeWidth(mStrokeWidth);
     paint.setColor(mProgressColor);
     paint.setAntiAlias(true);
     paint.setStrokeCap(Paint.Cap.ROUND);
     if (!isAnimation) {
     mCurrentAngle = 360 * (mCurrentProgress / mMaxProgress);
     }
     canvas.drawArc(rectF, mStartAngle, mCurrentAngle, false, paint);
     }

      这里的进度值就要根据当前的参数进行处理了,这里有一个变量进行判断处理,主要作用就是是否进行动画绘制。

    /**
     * 绘制中心文字
     */
     private void drawCenterText(Canvas canvas, int centerX) {
     Paint paint = new Paint();
     paint.setAntiAlias(true);
     paint.setColor(mTextColor);
     paint.setTextAlign(Paint.Align.CENTER);
     paint.setTextSize(mTextSize);
     Rect textBounds = new Rect();
     paint.getTextBounds(mText, 0, mText.length(), textBounds);
     canvas.drawText(mText, centerX, textBounds.height() / 2 + getHeight() / 2, paint);
     }

    绘制文字的规则还是和之前一样。

      还需要提供一些方法在代码中调用,下面是这些方法的代码:

    /**
     * 设置当前进度
     */
     public void setProgress(float progress) {
     if (progress < 0) {
     throw new IllegalArgumentException("Progress value can not be less than 0");
     }
     if (progress > mMaxProgress) {
     progress = mMaxProgress;
     }
     mCurrentProgress = progress;
     mCurrentAngle = 360 * (mCurrentProgress / mMaxProgress);
     setAnimator(0, mCurrentAngle);
     }
    
     /**
     * 设置文本
     */
     public void setText(String text) {
     mText = text;
     }
    
     /**
     * 设置文本的颜色
     */
     public void setTextColor(int color) {
     if (color <= 0) {
     throw new IllegalArgumentException("Color value can not be less than 0");
     }
     mTextColor = color;
     }
    
     /**
     * 设置文本的大小
     */
     public void setTextSize(float textSize) {
     if (textSize <= 0) {
     throw new IllegalArgumentException("textSize can not be less than 0");
     }
     mTextSize = textSize;
     }
    
    
     /**
     * 设置动画
     *
     * @param start 开始位置
     * @param target 结束位置
     */
     private void setAnimator(float start, float target) {
     isAnimation = true;
     ValueAnimator animator = ValueAnimator.ofFloat(start, target);
     animator.setDuration(mDuration);
     animator.setTarget(mCurrentAngle);
     //动画更新监听
     animator.addUpdateListener(valueAnimator -> {
     mCurrentAngle = (float) valueAnimator.getAnimatedValue();
     invalidate();
     });
     animator.start();
     }

      那么到此为止这个自定义View就完成了,下面我们可以在MainActivity中使用了。

      先修改activity_main.xml的代码,如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:gravity="center"
     android:orientation="vertical"
     android:padding="16dp"
     tools:context=".MainActivity">
    
     <com.easy.view.MacAddressEditText
     android:id="@+id/mac_et"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     app:boxBackgroundColor="@color/white"
     app:boxStrokeColor="@color/black"
     app:boxStrokeWidth="2dp"
     app:boxWidth="48dp"
     app:separator=":"
     app:textColor="@color/black"
     app:textSize="16sp" />
    
     <Button
     android:id="@+id/btn_mac"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_marginTop="30dp"
     android:text="获取地址" />
    
     <com.easy.view.CircularProgressBar
     android:id="@+id/cpb_test"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_marginTop="30dp"
     app:maxProgress="100"
     app:progress="10"
     app:progressbarBackgroundColor="@color/purple_500"
     app:progressbarColor="@color/purple_200"
     app:radius="80dp"
     app:strokeWidth="16dp"
     app:text="10%"
     app:textColor="@color/teal_200"
     app:textSize="28sp" />
    
     <Button
     android:id="@+id/btn_set_progress"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_marginTop="30dp"
     android:text="随机设置进度" />
    </LinearLayout>

    首先要注意看是否能够预览,我这里是可以预览的,如下图所示:

    Android 自定义View 之 圆环进度条

    在MainActivity中使用,修改onCreate()方法中的代码,如下所示:

    //圆形进度条操作
     CircularProgressBar cpbTest = findViewById(R.id.cpb_test);
     Button btnSetProgress = findViewById(R.id.btn_set_progress);
     btnSetProgress.setOnClickListener(v -> {
     int progress = Math.abs(new Random().nextInt() % 100);
     Toast.makeText(this, "" + progress, Toast.LENGTH_SHORT).show();
     cpbTest.setText(progress + "%");
     cpbTest.setProgress(progress);
     });

    运行效果如下图所示:

    Android 自定义View 之 圆环进度条

      顺便说一下,我打算把这个项目做成一个开源仓库,提交到mavenCentral()中,后面使用的话就可以通过这个引入依赖的方式进行调用,会很方便,后面会单独出一篇文章讲述这个仓库。

    如果对你有所帮助的话,不妨 Star 或 Fork,山高水长,后会有期~

    源码地址:EasyView

    © 版权声明

    相关文章