自定义View篇Android控件Android自定义View

Android自定义View实例教程~自定义可拖拽评价进度条「完

2019-01-11  本文已影响73人  唐_夏影

Android自定义评View实例教程~自定义可拖拽进度条「完全自定义」

实际效果,我们可以通过拖拽来设置进度,也可以通过点击按钮来设置进度

最终效果.gif

视频转Git会有卡顿感,实际运行并不会,项目源代码已经上传到仓库的AProgress文件夹,

要看懂本篇文章,需要你掌握Paint类,Canvas类的基本使用,可以查看以下文章学习

触摸改变进度条的进度

我们创建EasyProgress,继承于View,纯手工的来绘制Progress。

灰色.png

声明两个Paint变量,我们需要一个画笔来绘制一条总长度的线段,颜色为灰色。另外一个画笔来绘制实际选中长度的线段,颜色为绿色。

当我们的进度改变时,只需要将实际进度(绿色)的线段长度扩大,去覆盖灰色(背景)的线段就可以了

声明几个必要的属性,将两个画笔基础的属性设置好

import com.example.tonjies.abase.app.App;
import com.example.tonjies.abase.util.ADensity;

/**
 * Created by 舍长 on 2019/1/10
 * describe:
 */
public class EasyProgress extends View {

     //灰色背景线段的画笔
    private Paint bgPaint;

    //实际进度绿色线段的画笔
    private Paint progressPaint;

    //进度条的最大宽度
    private int maxProgress;

    //进度条当前的宽度
    private int currentProgress;

    //当前View的宽度
    private int width;

    //当前View的高度
    private int height;

    //距离左边的内边距
    private int paddingLeft;

    //距离右边的内边距
    private int paddingRight;


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

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

    public EasyProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();//初始化画笔
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        //进度条背景画笔
        bgPaint = new Paint();
        bgPaint.setColor(Color.parseColor("#F0F0F0"));//灰色
        bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);//填充且描边
        bgPaint.setAntiAlias(true);//抗锯齿
        bgPaint.setStrokeCap(Paint.Cap.ROUND);//线冒的头是圆的
        bgPaint.setStrokeWidth(ADensity.dip2px(3));//大小为3dp转px

        //设置进度画笔
        progressPaint = new Paint();
        progressPaint.setColor(Color.parseColor("#0DE6C2"));//绿色
        progressPaint.setStyle(Paint.Style.FILL_AND_STROKE);//填充且描边
        progressPaint.setAntiAlias(true);//抗锯齿
        progressPaint.setStrokeCap(Paint.Cap.ROUND);//线冒的头是圆的
        progressPaint.setStrokeWidth(ADensity.dip2px(3));//大小为3dp转px
        
   
    }
}

其中ADensity的作用是将我们的dp单位转换为px单位,因为不同设备的px值又是不同的,所以必须要经过转换,工具类ADensity

/**
 * Created by 舍长 on 2019/1/10
 * describe:
 */
public class ADensity {
    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(float dpValue) {
        float scale = 1;
        scale = App.getContext().getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    public static int px2dip(float pxValue) {
        final float scale = App.getContext().getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }


    // 将px值转换为sp值
    public static int px2sp(float pxValue) {
        final float fontScale = App.getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }
}

App是我自定义的Application

/**
 * Created by 舍长 on 2019/1/8
 * describe:
 */
public class App extends Application{
    private static Context context;
    @Override
    public void onCreate() {
        super.onCreate();
        context=this;
    }

    public static Context getContext() {
        return context;
    }
}

记得去权限菜单引用

android:name=".app.App"

在onLayout方法里面去初始化我们几个距离的参数,onLayout方法在View加载中只能加载一次,不过这几个参数都是不会改变的,因此在这里初始化没有问题滴

  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
      super.onLayout(changed, left, top, right, bottom);
      width = getWidth();//view的宽度
      height = getHeight();//view的高度
      paddLeft = getPaddingLeft();//距离左边的距离
      paddRight = getPaddingRight();//距离右边的距离
      //最大进度长度等于View的宽度-(左边的内边距+右边的内边距)
       maxProgress = width - getPaddingLeft() - getPaddingRight();
   }

接着写我们的onDraw方法和onTouchEvent方法


/**
 * Created by 舍长 on 2019/1/10
 * describe:一个简单的进度条
 */
public class EasyProgress extends View {

    //灰色背景线段的画笔
    private Paint bgPaint;

    //实际进度绿色线段的画笔
    private Paint progressPaint;

    //灰色背景线段画笔的路径
    private Path bgPath;

    //实际进度绿色线段画笔的路径
    private Path progressPath;

    //进度条的最大宽度
    private int maxProgress;

    //进度条当前的宽度
    private int currentProgress;

    //当前View的宽度
    private int width;

    //当前View的高度
    private int height;

    //距离左边的内边距
    private int paddingLeft;

    //距离右边的内边距
    private int paddingRight;


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

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

    public EasyProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();//初始化画笔
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        //进度条背景画笔
        bgPaint = new Paint();
        bgPaint.setColor(Color.parseColor("#F0F0F0"));//灰色
        bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);//填充且描边
        bgPaint.setAntiAlias(true);//抗锯齿
        bgPaint.setStrokeCap(Paint.Cap.ROUND);//线冒的头是圆的
        bgPaint.setStrokeWidth(ADensity.dip2px(3));//大小为3dp转px

        //设置进度画笔
        progressPaint = new Paint();
        progressPaint.setColor(Color.parseColor("#0DE6C2"));//绿色
        progressPaint.setStyle(Paint.Style.FILL_AND_STROKE);//填充且描边
        progressPaint.setAntiAlias(true);//抗锯齿
        progressPaint.setStrokeCap(Paint.Cap.ROUND);//线冒的头是圆的
        progressPaint.setStrokeWidth(ADensity.dip2px(3));//大小为3dp转px
    }


    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        width = getWidth();//view的宽度
        height = getHeight();//view的高度
        paddingLeft = getPaddingLeft();//距离左边的距离
        paddingRight = getPaddingRight();//距离右边的距离
        //最大进度长度等于View的宽度-(左边的内边距+右边的内边距)
        maxProgress = width - paddingLeft - paddingRight;
    }

    //绘制控件
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        L.d("maxProgress:" + maxProgress);
        //绘制背景线段
        canvas.drawLine(paddingLeft, height / 2, width -paddingRight , height / 2, bgPaint);
        //绘制实际线段
        canvas.drawLine(paddingLeft, height / 2, currentProgress, height / 2, progressPaint);
    }

    //触摸
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            //按住
            case MotionEvent.ACTION_DOWN:
                //设置进度值
                setMotionProgress(event);
                return true;
            //移动
            case MotionEvent.ACTION_MOVE:
                //设置进度值
                setMotionProgress(event);
                return true;
        }
        return super.onTouchEvent(event);
    }
     //设置进度值
    private void setMotionProgress(MotionEvent event) {
        //获取当前触摸点,赋值给当前进度
        currentProgress = (int) event.getX();
        //如果当前进度小于左边距
        if (currentProgress < paddingLeft) {
            currentProgress = paddingLeft;
        }
        //如果当前进度大于宽度-右边距
        else if (currentProgress > width - paddingRight) {
            currentProgress = width - paddingRight;
        }
        invalidate();
    }
}

因为我们要保证绿色线段的长度永远在实际线段长度(实际线段长度不等于View的长度,因为还要考虑View的内边距问题)之内,所以我么要在setProgress方法里面对获取到的触摸距离进行处理

这里写了一个触摸方法,就是触摸之后更新当前的进度值,并刷新UI,到这里我们可以去去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:orientation="vertical"
    tools:context=".module.easy.EasyActivity">


    <!--当前进度条的百分比进度值-->
    <TextView
        android:id="@+id/tvCurrentProgress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right|top"
        android:layout_marginRight="19dp"
        android:layout_marginTop="19dp"
        android:text="0/100"
        android:textColor="@color/colorPrimary"
        android:textSize="30sp"
        android:visibility="gone" />


    <com.example.tonjies.abase.view.EasyProgress
        android:id="@+id/easyProgress"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <!--点击增加进度的按钮-->
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/faButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right|bottom"
        android:layout_marginBottom="29dp"
        android:layout_marginRight="29dp"
        android:elevation="8dp"
        android:src="@drawable/add"
        android:visibility="gone"
        app:backgroundTint="#0DE6C2"
        />

</LinearLayout>

其中TextView会在后面显示进度条当前的进度值,而FloatingActionButton则是让我们点击后进度增加,该按钮需要添加design依赖,使用普通按钮代替也是可以的,两个组件暂时不需要,隐藏起来

//design组件
api 'com.android.support:design:27.1.1'

然后运行一下程序看看样子了

效果1.gif

但是我们现在的进度,都是屏幕上距离单位,这样的东西怎么上得了台面,所以我们接下来将距离转换为我们熟悉的百分比,并给进度条添加相应的圆点指示器

并且这里其实还是会有问题的,就是控件的高度。当我们将控件的高度设置为明确的数值或者Match时,看不出来,但是假如是设置成wrap_content,它就会把下面的所有控件的控件都“顶掉”

因为这里还有没添加圆点指示器,所以我们暂时不对它进行处理,在后面添加了圆点指示器后,我们会通过重写onMeasure方法。来使得控件设置为wrap_content时,让控件的高度等于圆点指示器的高度

百分比,圆点指示器

我们的线段距离单位有以下几个

他们之间的关系可以用下面的公式来表示

进度条比例.png

其中MaxProgress对应我们的总量100,而我们的currentProgress的数字实际上是在View中的宽度,不是仅仅在maxProgress的宽度,所以还要减去左内边距,而右类边距同样会在方法中会处理,所以不必担心

//如果当前进度大于宽度-右边距
else if (currentProgress > width - paddingRight) {
    currentProgress = width - paddingRight;
}

有了思路我们就可以直接上手了,我们声明相应的接口回调变量,用来向我们的Activity回调数据

   //当前选中进度的回调
    private OnProgressListener onProgressListener;

    public interface OnProgressListener {
        void onSelect(int progress);
    }

    public void setOnProgressListener(OnProgressListener onProgressListener) {
        onProgressListener = onProgressListener;
    }

修改setMotionProgress方法

   @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            //按住
            case MotionEvent.ACTION_DOWN:
                //设置进度值
                setMotionProgress(event);
                return true;
            //移动
            case MotionEvent.ACTION_MOVE:
                //获取当前触摸点,赋值给当前进度
                setMotionProgress(event);
                return true;
        }
        return super.onTouchEvent(event);
    }

    //设置进度值
    private void setMotionProgress(MotionEvent event) {
        //获取当前触摸点,赋值给当前进度
        currentProgress = (int) event.getX();
        //如果当前进度小于左边距
        setCurrentProgress();
        //看数学公式就可以了,实际百分比进度数值
        int result = ((currentProgress - paddingLeft) * 100) / maxProgress;
        onProgressListener.onSelect(result);
        invalidate();
    }

最后我们回到我们的Activity,写我们的回调方法


/**
 * Created by 舍长
 * describe:最简单的进度条
 */
public class EasyActivity extends AppCompatActivity {

    //实
    @BindView(R.id.easyProgress)
    EasyProgress easyProgress;

    //当前进度条的百分比文本
    @BindView(R.id.tvCurrentProgress)
    TextView tvCurrentProgress;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_easy);
        ButterKnife.bind(this);
        tvCurrentProgress.setVisibility(View.VISIBLE);
        easyProgress.setOnProgressListener(new EasyProgress.OnProgressListener() {
            @Override
            public void onSelect(int progress) {
                tvCurrentProgress.setText(progress + "/100");
                L.d("progress:"+progress);
            }
        });
    }

}

L是日志封装类,你使用普通Log也可以。效果图和上面其实没什么区别,就是多了个显示百分比进度

接着我们来给进度值添加一个圆点指示器,声明Paint变量,指示点的半径,初始化画笔属性

//圆点指示器的画笔
private Paint circlePaint;

//圆点指示器的半径
private int mCircleRadius = ADensity.dip2px(12);

init方法里初始化属性

//圆点指示器
circlePaint = new Paint();
circlePaint.setAntiAlias(true);//设置抗锯齿
circlePaint.setColor(Color.parseColor("#fafafa"));//颜色
circlePaint.setShadowLayer(ADensity.dip2px(2), 0, 0, Color.parseColor("#38000000"));//外阴影颜色
circlePaint.setStyle(Paint.Style.FILL);//填充

在onDraw方法里面使用

//要支持阴影下过必须关闭硬件加速
setLayerType(LAYER_TYPE_SOFTWARE, null);//发光效果不支持硬件加速
//绘制圆点
canvas.drawCircle(currentProgress,getHeight()/2,mCircleRadius,circlePaint);

运行效果,转换的git图有点卡顿,实际效果并不会,这个放心

效果2.gif

到这里我们看似是完成了,但是假如我们在xml文件中不设置padd属性呢?

圆点指示器.png

圆点指示器有一半被顶出去了,因为当没有设置内边距的时候,我们进度条是以画布的起点开始算的,而我们的圆点指示器的圆中心点永远是当前进度的点

所以我们还需要对线段的起始距离做一下处理,让它永远距离最左边和最右边有半个圆点指示器的距离。这里我们只要修改padd属性就好,这时候也体现出我们当时将几个常用的距离单位在onLayout中提取出来是一件多么明智的事情

   //初始化几个距离参数
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        width = getWidth();//view的宽度
        height = getHeight();//view的高度

        //让左边距至少为半个圆点指示器的距离
        paddingLeft = getPaddingLeft();//距离左边的距离
        if (getPaddingLeft() < mCircleRadius) {
            L.d("onLayout");
            paddingLeft = mCircleRadius;
        }
        //让右边距至少为半个圆点指示器的距离
        paddingRight = getPaddingRight();//距离右边的距离
        if (getPaddingRight() < mCircleRadius) {
            paddingRight = mCircleRadius;
        }

        //如果当前进度小于左边距
        if (currentProgress < paddingLeft) {
            currentProgress = paddingLeft;
        }
        //如果当前进度大于宽度-右边距
        else if (currentProgress > width - paddingRight) {
            currentProgress = width - paddingRight;
        }
        //最大进度长度等于View的宽度-(左边的内边距+右边的内边距)
        maxProgress = width - paddingLeft - paddingRight;
    }

上面说到的控件高度问题我们现在就可以解决了

    //重新计算控件的宽,高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    //返回高度值
    private int measureHeight(int heightMeasureSpec) {
        int result;
        int mode = MeasureSpec.getMode(heightMeasureSpec);//获取高度类型
        int size = MeasureSpec.getSize(heightMeasureSpec);//获取高度数值
        //如果用户设定了指定大小
        if (mode == MeasureSpec.EXACTLY) {
            L.d("EXACTLY");
            result = size;
        }
        //如果用户没有设定明确的值
        else {
            //设定高度为圆点指示器的直径
            result = mCircleRadius * 2;
        }
        return result;
    }

当控件的高度为明确的数值时我们直接用,当不是明确的数值时我们就让它的高度是圆点指示器的高度

如如果你不希望让进度条是通过拖动增加进度的,而是自己设定数值,我们就提供一个方法,通过代码设置数值的方式来改变进度

//设置当前进度条进度,从1到100
public void setProgress(int progress) {
    if (progress > 100 || progress < 0) {
        Toast.makeText(App.getContext(), "输入的进度值不符合规范", Toast.LENGTH_SHORT).show();
    }
    if (currentProgress < paddingLeft) {
        currentProgress = paddingLeft;
    }
    //如果当前进度大于宽度-右边距
    else if (currentProgress > width - paddingRight) {
        currentProgress = width - paddingRight;
    }
    //设置当前进度的宽度
    currentProgress = ((progress * maxProgress) / 100) + paddingLeft;
    onProgressListener.onSelect(progress);
    invalidate();
}

到这里,你可能会觉得我们太多个地方都需要对currentProgress进行内边距的处理了,我们可以将这一段d代码抽取出来,这里我们只要用鼠标圈住这一段代码,使用快捷键Ctrl+Alt+M,AndroidStuio就会自动帮我们把所有相同的代码抽取成一个方法啦


    private void setCurrentProgress() {
        if (currentProgress < paddingLeft) {
            currentProgress = paddingLeft;
        }
        //如果当前进度大于宽度-右边距
        else if (currentProgress > width - paddingRight) {
            currentProgress = width - paddingRight;
        }
    }

完整代码

/**
 * Created by 舍长 on 2019/1/10
 * describe:一个简单的进度条
 */
public class EasyProgress extends View {

    //灰色背景线段的画笔
    private Paint bgPaint;

    //实际进度绿色线段的画笔
    private Paint progressPaint;

    //圆点指示器的画笔
    private Paint circlePaint;

    //圆点指示器的半径
    private int mCircleRadius = ADensity.dip2px(12);

    //进度条的最大宽度
    private int maxProgress;

    //进度条当前的宽度
    private int currentProgress;

    //当前View的宽度
    private int width;

    //当前View的高度
    private int height;

    //距离左边的内边距
    private int paddingLeft;

    //距离右边的内边距
    private int paddingRight;


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

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

    public EasyProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();//初始化画笔
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        //进度条背景画笔
        bgPaint = new Paint();
        bgPaint.setColor(Color.parseColor("#F0F0F0"));//灰色
        bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);//填充且描边
        bgPaint.setAntiAlias(true);//抗锯齿
        bgPaint.setStrokeCap(Paint.Cap.ROUND);//线冒的头是圆的
        bgPaint.setStrokeWidth(ADensity.dip2px(3));//大小为3dp转px

        //设置进度画笔
        progressPaint = new Paint();
        progressPaint.setColor(Color.parseColor("#0DE6C2"));//绿色
        progressPaint.setStyle(Paint.Style.FILL_AND_STROKE);//填充且描边
        progressPaint.setAntiAlias(true);//抗锯齿
        progressPaint.setStrokeCap(Paint.Cap.ROUND);//线冒的头圆原的
        progressPaint.setStrokeWidth(ADensity.dip2px(3));//大小为3dp转px

        //圆点指示器
        circlePaint = new Paint();
        circlePaint.setAntiAlias(true);//设置抗锯齿
        circlePaint.setColor(Color.parseColor("#fafafa"));//颜色
        circlePaint.setShadowLayer(ADensity.dip2px(2), 0, 0, Color.parseColor("#38000000"));//外阴影颜色
        circlePaint.setStyle(Paint.Style.FILL);//填充
    }

    //重新计算控件的宽,高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    //返回高度值
    private int measureHeight(int heightMeasureSpec) {
        int result;
        int mode = MeasureSpec.getMode(heightMeasureSpec);//获取高度类型
        int size = MeasureSpec.getSize(heightMeasureSpec);//获取高度数值
        //如果用户设定了指定大小
        if (mode == MeasureSpec.EXACTLY) {
            L.d("EXACTLY");
            result = size;
        }
        //如果用户没有设定明确的值
        else {
            //设定高度为圆点指示器的直径
            result = mCircleRadius * 2;
        }
        return result;
    }

    //初始化几个距离参数
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        width = getWidth();//view的宽度
        height = getHeight();//view的高度

        //让左边距至少为半个圆点指示器的距离
        paddingLeft = getPaddingLeft();//距离左边的距离
        if (getPaddingLeft() < mCircleRadius) {
            L.d("onLayout");
            paddingLeft = mCircleRadius;
        }
        //让右边距至少为半个圆点指示器的距离
        paddingRight = getPaddingRight();//距离右边的距离
        if (getPaddingRight() < mCircleRadius) {
            paddingRight = mCircleRadius;
        }

        //如果当前进度小于左边距
        setCurrentProgress();
        //最大进度长度等于View的宽度-(左边的内边距+右边的内边距)
        maxProgress = width - paddingLeft - paddingRight;
    }

    //绘制控件
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
//        L.d("onDraw");
        //绘制背景线段
        canvas.drawLine(paddingLeft, height / 2, width - paddingRight, height / 2, bgPaint);
        //绘制实际进度线段
        canvas.drawLine(paddingLeft, height / 2, currentProgress, height / 2, progressPaint);
        //要支持阴影下过必须关闭硬件加速
        setLayerType(LAYER_TYPE_SOFTWARE, null);//发光效果不支持硬件加速
        //绘制圆点
        canvas.drawCircle(currentProgress, getHeight() / 2, mCircleRadius, circlePaint);
    }

    //触摸
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            //按住
            case MotionEvent.ACTION_DOWN:
                //设置进度值
                setMotionProgress(event);
                return true;
            //移动
            case MotionEvent.ACTION_MOVE:
                //获取当前触摸点,赋值给当前进度
                setMotionProgress(event);
                return true;
        }
        return super.onTouchEvent(event);
    }

    //设置进度值
    private void setMotionProgress(MotionEvent event) {
        //获取当前触摸点,赋值给当前进度
        currentProgress = (int) event.getX();
        //如果当前进度小于左边距
        setCurrentProgress();
        //看数学公式就可以了,实际百分比进度数值
        int result = ((currentProgress - paddingLeft) * 100) / maxProgress;
        onProgressListener.onSelect(result);
        invalidate();
    }


    //设置当前进度条进度,从1到100
    public void setProgress(int progress) {
        if (progress > 100 || progress < 0) {
            Toast.makeText(App.getContext(), "输入的进度值不符合规范", Toast.LENGTH_SHORT).show();
        }
        setCurrentProgress();
        //设置当前进度的宽度
        currentProgress = ((progress * maxProgress) / 100) + paddingLeft;
        onProgressListener.onSelect(progress);
        invalidate();
    }

    private void setCurrentProgress() {
        if (currentProgress < paddingLeft) {
            currentProgress = paddingLeft;
        }
        //如果当前进度大于宽度-右边距
        else if (currentProgress > width - paddingRight) {
            currentProgress = width - paddingRight;
        }
    }

    //当前选中进度的回调
    private OnProgressListener onProgressListener;

    public interface OnProgressListener {
        void onSelect(int progress);
    }

    public void setOnProgressListener(OnProgressListener onProgressListener) {
        this.onProgressListener = onProgressListener;
    }
}


在Actiivty中去使用

/**
 * Created by 舍长
 * describe:最简单的进度条
 */
public class EasyActivity extends AppCompatActivity {

    //进度条
    @BindView(R.id.easyProgress)
    EasyProgress easyProgress;

    //当前进度条的百分比文本
    @BindView(R.id.tvCurrentProgress)
    TextView tvCurrentProgress;

    //增加进度按钮
    @BindView(R.id.faButton)
    FloatingActionButton faButton;

    //每次累加的进度
    private int progress = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_easy);
        ButterKnife.bind(this);
        tvCurrentProgress.setVisibility(View.VISIBLE);
        faButton.setVisibility(View.VISIBLE);
        //监听数值的变化
        easyProgress.setOnProgressListener(new EasyProgress.OnProgressListener() {
            @Override
            public void onSelect(int progress) {
                tvCurrentProgress.setText(progress + "/100");
//                L.d("progress:"+progress);
            }
        });
        //设置进度值
        faButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (progress < 100) {
                    progress += 10;
                    easyProgress.setProgress(progress);
                } else {
                    progress = 0;
                }
            }
        });
    }

}

到这里你可能会说,就这么一点效果,弄得这么麻烦,我继承个SeekBar,重写几个属性不是美滋滋?确实,就当前的效果来说继承个Seekbar就可以了,但是现在你的UI表示,我想要你在进度条下面给我来个同步移动的文字!如下

进度条下方的文字.png

加入你是使用的Seekbar,那么你已经凉了,没法判断圆点坐标的位置啊!(或者是我不知道?!),而这个效果也是我下一篇想要写的效果

如果本文对你有所帮助,希望能给我一个喜欢,这对我的帮助非常的大,谢谢你

上一篇下一篇

猜你喜欢

热点阅读