欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果

程序员文章站 2022-06-26 17:46:11
自定义View从实现到原理(七)已经到这一步了啊,这一篇写完基本上自定义View就不会写了,以后有可能的话,也许会写一下自定义ViewGroup或者是自定义View的仿真书籍翻页效果,不过那也是以后的事情了,今天就来实现以下水波纹加载效果,先看一下效果图:类似这种的效果,其实也就是一个自定义的View,接下来我们来一步步实现一下:定义属性首先还是一样,根据效果图,先定义这个View的属性,这个效果我觉得需要圆形的背景颜色,圆形的半径,显示的进度,显示文字的大小,显示文字的颜色,定义属性的代码:&...

自定义View从实现到原理(七)

已经到这一步了啊,这一篇写完基本上自定义View就不会写了,以后有可能的话,也许会写一下自定义ViewGroup或者是自定义View的仿真书籍翻页效果,不过那也是以后的事情了,今天就来实现以下水波纹加载效果,先看一下效果图:
自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
类似这种的效果,其实也就是一个自定义的View,接下来我们来一步步实现一下:

定义属性

首先还是一样,根据效果图,先定义这个View的属性,这个效果我觉得需要圆形的背景颜色,圆形的半径,显示的进度,显示文字的大小,显示文字的颜色,定义属性的代码:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="WaterView">
    
        <!-- 背景颜色,圆的半径,进度,进度字号,进度颜色 -->
        <attr name="backgroundColor" format="color" />
        <attr name="radius" format="dimension" />
        <attr name="text" format="string" />
        <attr name="textSize" format="dimension" />
        <attr name="textColor" format="color" />

    </declare-styleable>

</resources>

按照正常步骤,下一步在自定义的View中的构造函数获取属性:

 	private int backgroundColor, textColor;
    private float radius, textSize;
    private String text;
    private Paint backgroundPaint, textPaint;

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

    public WaterView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        TypedArray waterTypeArray = context.obtainStyledAttributes(attrs, R.styleable.WaterView);
        backgroundColor = waterTypeArray.getColor(R.styleable.WaterView_backgroundColor, 
                Color.BLACK);
        textColor = waterTypeArray.getColor(R.styleable.WaterView_textColor, Color.WHITE);
        radius = waterTypeArray.getDimension(R.styleable.WaterView_radius, 260f);
        textSize = waterTypeArray.getDimension(R.styleable.WaterView_textSize, 24f);
        text = waterTypeArray.getString(R.styleable.WaterView_text);
        //记得回收
        waterTypeArray.recycle();
    }

绘制进度文字

首先我们要初始化画笔,在自定义View中我们通过画笔可以绘制我们想要的所有效果:

/**
     * 初始化画笔
     */
    private void initPaint() {
        //初始化背景画笔
        backgroundPaint = new Paint();
        backgroundPaint.setColor(backgroundColor);
        //抗锯齿
        backgroundPaint.setAntiAlias(true);

        //初始化显示文字画笔
        textPaint = new Paint();
        textPaint.setTextSize(textSize);
        textPaint.setColor(textColor);
        textPaint.setAntiAlias(true);
        //字体为粗体
        textPaint.setFakeBoldText(true);
    }

定义完画笔之后,我们首先绘制出底层的圆形,有圆形才能在圆形中心绘制文字:

	canvas.drawCircle(getWidth()/2, getHeight()/2, radius, backgroundPaint);

别忘了在构造函数中调用initPaint()方法,要不然会有空指针异常的错误抛出的。

写到这里我们想看一下能不能画出圆形,所以在xml中简单引用一下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".Activity.WaterActivity">

    <com.example.day1.View.WaterView
        android:layout_width="260dp"
        android:layout_height="260dp"
        android:layout_centerInParent="true"
        />
    
</RelativeLayout>

效果图如下:
自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
可真是朴实无华的一个圆形,既然能画出来,那我们之前的工作就没错,在中心显示一下文字:

canvas.drawText(text, getWidth() / 2, getHeight() / 2, textPaint);

我第一次是这么写的,感觉很正常,在Width的中间与Height的中间绘制出显示的文字,结果运行出来出了问题:
自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
震惊,什么鬼?我仔细看了一下这个文字的位置,发现它的左下角,应该就是我们设置的中心位置,就离谱,遇到了问题就要解决,我就去搜了一下自定义View怎么将文字显示在中心,这里参考了这篇文章:

Android自定义View之文字居中

首先在initPaint()方法中设置居中,不过注意这只能做到水平居中:

textPaint.setTextAlign(Paint.Align.CENTER);

这一行代码就可以了,那么接下来我们处理竖直方向居中,按理来说上面这一行代码就足以解决了,但是为什么会有一点偏上呢,这就是因为在文字显示的时候,有一个基线,这个基线正是在我们设置的竖直居中位置,我们的文字在基线上部,因此就会比中心位置提高一点,对于这个问题有两种解决方法:

getTextBounds()方法

先来看一下这个方法的使用:

getTextBounds(String text, int start, int end, Rect bounds)

text是要显示的文本,start以及end是文本的开始显示位置与结束位置,bounds是存储文字显示位置的对象,最后的结果会写进bounds中。我们使用就是通过这个方法获得文字的边框bounds,由于基线在文字的下方,因此我们想要竖直居中的话就得向下平移文字高度的一半,就是 (bounds.top+bound.bottom)/2 这个值,但是要注意这个是一个负数,代表的是文字的高度一半的位置而不是真正意义的高度一半,getTextBounds()方法会有一个自己的坐标系:
自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
可以看得出来在这个坐标系下,top以及bottom都是负值,因此我们向下平移需要减去之前算出的文字高度中心位置,那么具体的用法就是下面的代码:

		Rect bounds = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), bounds);
        float offset = (bounds.bottom + bounds.top) / 2;

        canvas.drawText(text, getWidth() / 2, (getHeight() / 2) - offset, textPaint);

自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
可行,我们接下来看一下另外一种方法;

FontMetircs 方法

自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
和之前的方法类似,我们看一下这个图,有了之前的经验,我们可以看出需要的两个数据为ascent以及descent这两个,不过要注意的是,这几个值,是固定不会变的,也就是无论你绘制的内容怎么改变,这几个值都不会变,类比上面的代码,我们来看一下这个:

		Paint.FontMetrics fontMetrics= new Paint.FontMetrics();
        textPaint.getFontMetrics(fontMetrics);
        float offset = (fontMetrics.descent+fontMetrics.ascent)/2;
        canvas.drawText(text, getWidth() / 2, (getHeight() / 2) - offset , textPaint);

效果的话和上面是一样的,那么这两种方法,我们应该如何区别使用呢:

对于第一种,getTextBounds()方法,我们是获取到了文字的中间高度,那么随着内容的改变,我们的中间位置可能也会发生改变;

第二种,FontMetircs方法,这是固定的文字测量工具,不管内容如何改变,他的中间位置不会发生改变。

这样我们就可以总结出:

1.当我们绘制的内容会有动态改变操作时,使用FontMetircs()方法;

2.当绘制内容固定的时候,我们用哪个都可以,第一种看起来更加直观。

设置onMeasure

通常我们都会在xml布局中将控件设为wrapContent类型,按照我们之前写过的博客,我们也应该要重写onMeasure函数,这部分就不多说了,之前也介绍过:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        int width, height;

        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            height = width = (int) radius * 2;
            setMeasuredDimension(width, height);
        } else {
            setMeasuredDimension(widthSpecSize, heightSpecSize);
        }
    }

简简单单的一串代码,朴实无华,效果也没什么变化,就这样,下一步。

显示文字模拟下载

下载自然是从0%到100%,我们开启线程进行模拟,在规定时间内跑完,并动态更新文字内容的显示:

	private SingleTapThread singleTapThread;
    private GestureDetector detector;

    private int currentProgress = 0;
    private int maxProgress = 100;
	
	text = currentProgress + "%";

做好准备活动,接下来会开启线程模拟下载:

	@SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_UP) {
            startProgressAnimation();
        }
        return super.onTouchEvent(event);
    }


    private void startProgressAnimation() {
        if (singleTapThread == null) {
            singleTapThread = new SingleTapThread();
            getHandler().postDelayed(singleTapThread, 100);
        }
    }


    private class SingleTapThread implements Runnable {

        @Override
        public void run() {
            int maxProgress = 100;
            if (currentProgress < maxProgress) {
                invalidate();
                getHandler().postDelayed(singleTapThread, 100);
                currentProgress++;
            } else {
                getHandler().removeCallbacks(singleTapThread);
            }
        }
    }

我们从上到下看,首先设置了点击事件,只要有触碰抬起的事件发生,那么就启动startProgressAnimation()方法,在这个方法中我们首先检测了是否有线程在运行,如果没有的话开启线程,延迟100ms后启动SingleTapThread()线程,在这个线程中定义了最大值为100,如果当前值小于100就会刷新View并且100ms后再次重启,同时进度加一,如果已经完成则回收这个线程,效果图如下:
自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
文字效果已经实现,那么接下来才是主要的部分,实现水波纹波浪效果:

实现水波纹波浪效果

简单来说,水波纹波浪效果,就是二阶贝塞尔曲线的一个应用,来看一下二阶贝塞尔曲线效果:
自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
就是这样,我们这个效果可以看成是曲线在固定范围内的不断变换,我们这里就简要了解一下二阶贝塞尔曲线的应用就好了,如果需要深入在后面我再研究研究。

在Android SDK中提供了关于绘制贝塞尔曲线的方法:

public void rQuadTo(float dx1, float dy1, float dx2, float dy2) 

这四个参数都是相对值,我们来看一下上面的哪个动图,曲线起点是P0,终点P2,控制点P1,而相对的我们这四个参数的值,为:

1.dx1:控制点X坐标,表示相对上一个终点X坐标的位移坐标,可为负值,正值表示相加,负值表示相减;
2.dy1:控制点Y坐标,相对上一个终点Y坐标的位移坐标。同样可为负值,正值表示相加,负值表示相减;
3.dx2:终点X坐标,同样是一个相对坐标,相对上一个终点X坐标的位移值,可为负值,正值表示相加,负值表示相减;
4.dy2:终点Y坐标,同样是一个相对,相对上一个终点Y坐标的位移值。可为负值,正值表示相加,负值表示相减;

这四个参数都是传递的都是相对值,相对上一个终点的位移值。

接下来正式绘制波浪特效:

首先初始化波浪的画笔:

		//初始化波浪画笔
        progressPaint = new Paint();
        progressPaint.setAntiAlias(true);
        progressPaint.setColor(waterColor);
        //两层在一起我在上面
        progressPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

最后这行设置如果两层绘制在了同一个界面,这个绘制的会在上部显示,并且只显示交集,这是绘制圆形波浪的关键;

既然这个画布是有遮盖效果的,那么就应该设置在同一个bitmap上,我们定义一个bitmap,为了在这个bitmap上进行绘制,我们还要定义一个bitmapCanvas,来载入bitmap:

	if (bitmap == null) {
            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmapCanvas = new Canvas(bitmap);
        }

这是初始化bitmap以及bitmapCanvas部分,定义部分自己在开始写一下吧,既然已经有了bitmapCanvas,我们就来把之前的画圆以及绘制文字都载入一下:

		super.onDraw(canvas);

        width = getWidth();
        height = getHeight();

        if (bitmap == null) {
            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmapCanvas = new Canvas(bitmap);
        }
        bitmapCanvas.save();
        //移动坐标系
        bitmapCanvas.translate(0, height / 4);
        //绘制圆
        bitmapCanvas.drawCircle(radius, radius, radius, backgroundPaint);

        //绘制PATH
        //重置绘制路线
        path.reset();
        float percent = currentProgress * 1.0f / maxProgress;
        float y = (1 - percent) * radius * 2;
        //起点移动到右下
        path.moveTo(width, y);
        //移动到右下方
        path.lineTo(width, height);
        //移动到最左下边
        path.lineTo(0, height);
        //移动到左上边
        // path.lineTo(0, y);
        //实现左右波动,根据progress来平移
        path.lineTo(-(1 - percent) * radius * 2, y);
        if (currentProgress != 0.0f) {
            //根据直径计算绘制贝赛尔曲线的次数
            float count = radius * 2 / 30;
            //控制-控制点y的坐标
            float point = (1 - percent) * 15;
            for (int i = 0; i < count; i++) {
                path.rQuadTo(15, -point, 30, 0);
                path.rQuadTo(15, point, 30, 0);
            }
        }
        //闭合
        path.close();
        bitmapCanvas.drawPath(path, progressPaint);

        //绘制文字
        String text = currentProgress + "%";
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        float offset = (fontMetrics.ascent + fontMetrics.descent) / 2;
        bitmapCanvas.drawText(text, width / 2, radius - offset, textPaint);

        bitmapCanvas.restore();

        canvas.drawBitmap(bitmap, 0, 0, null);

这里我们将onDraw()的代码都写了上去,我们就着重来看一下绘制贝塞尔曲线这一部分,逐行分析:

1.首先我们reset了path,path是安卓中用来绘制图形,路径,曲线等多种图案的工具,有必要的时候我会写一篇博客学习一下;

2.定义了float类型变量,这个的值就是当前进度百分比,用 currentProgress * 1.0f / maxProgress 计算得到,这样计算可以保留精度;

3.在之前我们有一行代码需要关注一下:

bitmapCanvas.translate(0, height / 4);

这一行代码,我们移动了bitmapCanvas的坐标系,经过我的反复研究,我们绘制的画布的坐标系原点是在左上角的位置,也就是这里:
自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
原谅我画的图太辣鸡,但是想来应该都会懂得,红点的位置就是原点位置,x轴向右为正,y轴向下为正,在上面那串移动坐标系的代码中,我们可以看到横坐标没有移动,纵坐标移动 height / 4 ,y轴向下为正,所以整体绘制的View就会向下移动 height / 4 距离,呈现的效果就是这样:
自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
好,我们回归第三行代码,我们定义了float类型的y,也就是纵坐标,表达式为 (1 - percent) * radius * 2;,percent是当前的进度,那么y的值就是从radius*2一直变到0,由于y轴向下为正,那么也就是从View的最下端一直到最上部;

4.在这里我们开始对path进行操作,moveto的作用是设置起点,从代码可以看出,起点在(width,y)这个位置,根据上面说的,那也就是右下角位置;

5.lineto操作,代表移动连线,起点已经设置好,但是由于我们这个y是一直在变化的,因此为了保持绘制的完整,我们下一点还是要设置在右下角;

6.和5基本一样,第三个点绘制在左下角;

7.最后一个点,我们为了绘制出的水波浪效果是动态波动的,将最后一个点设置为根据当前进度进行调整,绘制出平滑的波浪我们的终点起点纵坐标要一致,这样才能在同一水平线,横坐标表达式:

-(1 - percent) * radius * 2

也很好理解,一开始位置是-2*radius,最后是0,这样保证了绘制的所有阶段,圆形区域内都是顺滑的,不会出现空白;

8.这个if我们整体说一下,如果当前不是刚开始的时候,就会进入if中,首先我们根据直径来判断要绘制多少次贝塞尔曲线,radius * 2 / 30 ,radius*2不用多说,是直径,至于这个三十,我们贴一张图:
自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
这部分借鉴了这篇文章:

android 自定义view-水波纹进度球

我们选择30为一个周期,这部分我们可以自定,也可以是60,这样计算出count这个值,作为计算的结果,在下一步我们计算控制点的y坐标,我们一开始拟定是15,根据当前进度,进度越大,波动越小;

9.根据8中计算的次数进行循环,根据8中计算的控制点y坐标进行波动,我们回头看一下刚说过的贝塞尔曲线的rQuadTo()方法,里面的参数都是相对起点的值,这里的起点,其实是我们刚说的path的终点,有点绕,自己理解一下;

10.在最后部份,我们使用path.close()方法,将path的起点和终点相连,构成闭合区域,最后绘制,

效果图如下:
自定义View从实现到原理(七)- 类似迅雷实现水波纹波浪加载效果
当然我这里更改了一下颜色,这样看着更加明显一点。

最后贴一个整体的自定义View代码,以供参考:

package com.example.day1.View;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

import com.example.day1.R;

public class WaterView extends View {
    //定义背景颜色,字体颜色;
    private int backgroundColor, textColor, waterColor;
    private float radius, textSize;
    private String text;
    private Paint backgroundPaint, textPaint, progressPaint;

    private SingleTapThread singleTapThread;

    private int currentProgress = 0;
    private int maxProgress = 100;

    private Path path = new Path();
    private Bitmap bitmap;
    private Canvas bitmapCanvas;

    private int width, height;

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

    public WaterView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        TypedArray waterTypeArray = context.obtainStyledAttributes(attrs, R.styleable.WaterView);
        backgroundColor = waterTypeArray.getColor(R.styleable.WaterView_backgroundColor, Color.WHITE);
        textColor = waterTypeArray.getColor(R.styleable.WaterView_textColor, Color.BLACK);
        radius = waterTypeArray.getDimension(R.styleable.WaterView_radius, 260f);
        textSize = waterTypeArray.getDimension(R.styleable.WaterView_textSize, 24f);
        text = waterTypeArray.getString(R.styleable.WaterView_text);
        waterColor = waterTypeArray.getColor(R.styleable.WaterView_waterColor, Color.GREEN);
        //记得回收
        waterTypeArray.recycle();
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        //初始化背景画笔
        backgroundPaint = new Paint();
        backgroundPaint.setColor(backgroundColor);
        //抗锯齿
        backgroundPaint.setAntiAlias(true);

        //初始化显示文字画笔
        textPaint = new Paint();
        textPaint.setTextSize(textSize);
        textPaint.setColor(textColor);
        textPaint.setAntiAlias(true);
        //字体为粗体
        textPaint.setFakeBoldText(true);
        textPaint.setTextAlign(Paint.Align.CENTER);

        //初始化波浪画笔
        progressPaint = new Paint();
        progressPaint.setAntiAlias(true);
        progressPaint.setColor(waterColor);
        //取两层绘制交集。显示上层
        progressPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        int width, height;

        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            height = width = (int) radius * 2;
            setMeasuredDimension(width, height);
        } else {
            setMeasuredDimension(widthSpecSize, heightSpecSize);
        }
    }

    /**
     * 绘制部份
     *
     * @param canvas 画布
     */
    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        width = getWidth();
        height = getHeight();

        if (bitmap == null) {
            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmapCanvas = new Canvas(bitmap);
        }
        bitmapCanvas.save();
        //移动坐标系
        bitmapCanvas.translate(0, height / 4);
        //绘制圆
        bitmapCanvas.drawCircle(radius, radius, radius, backgroundPaint);

        //绘制PATH
        //重置绘制路线
        path.reset();
        float percent = currentProgress * 1.0f / maxProgress;
        float y = (1 - percent) * radius * 2;
        //起点移动到右下
        path.moveTo(width, y);
        //移动到右下方
        path.lineTo(width, height);
        //移动到最左下边
        path.lineTo(0, height);
        //移动到左上边
        // path.lineTo(0, y);
        //实现左右波动,根据progress来平移
        path.lineTo(-(1 - percent) * radius * 2, y);
        if (currentProgress != 0.0f) {
            //根据直径计算绘制贝赛尔曲线的次数
            float count = radius * 2 / 30;
            //控制-控制点y的坐标
            float point = (1 - percent) * 15;
            for (int i = 0; i < count; i++) {
                path.rQuadTo(15, -point, 30, 0);
                path.rQuadTo(15, point, 30, 0);
            }
        }
        //闭合
        path.close();
        bitmapCanvas.drawPath(path, progressPaint);

        //绘制文字
        String text = currentProgress + "%";
        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        float offset = (fontMetrics.ascent + fontMetrics.descent) / 2;
        bitmapCanvas.drawText(text, width / 2, radius - offset, textPaint);

        bitmapCanvas.restore();

        canvas.drawBitmap(bitmap, 0, 0, null);

    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_UP) {
            startProgressAnimation();
        }
        return super.onTouchEvent(event);
    }


    private void startProgressAnimation() {
        if (singleTapThread == null) {
            singleTapThread = new SingleTapThread();
            getHandler().postDelayed(singleTapThread, 100);
        }
    }


    private class SingleTapThread implements Runnable {

        @Override
        public void run() {
            if (currentProgress < maxProgress) {
                invalidate();
                getHandler().postDelayed(singleTapThread, 100);
                currentProgress++;
            } else {
                getHandler().removeCallbacks(singleTapThread);
            }
        }
    }
}


就这样,撤退。

本文地址:https://blog.csdn.net/ly160507/article/details/111830673

相关标签: View android