一、效果图

Android自定义滑动进度条_java

二、实现过程

实现过程很简单,只要自定义一个TextView,在onTouchEvent中的移动事件中不断重新绘制即可。为了方便使用,首先自定义几个属性。

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

   <declare-styleable name="SlideView">
       <attr name="selectColor" format="color"></attr>
       <attr name="radius" format="dimension"></attr>
       <attr name="maxValue" format="integer"></attr>
   </declare-styleable>
</resources>

初始化代码,主要用到TypedArray来获取设置的值。

public class SlideView extends CenterTextView {
   private  float mCurrentValue;
   private Paint mSelectPaint;
   private int mRadius=100;
   private int mSelectColor;

   private  int mAnimatorDuration=500;

   private  int mMaxValue =20;

   public SlideView(Context context) {
       this(context,null);

   }

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

   }

   public SlideView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
       super(context, attrs, defStyleAttr);
       TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlideView);
       mRadius =(int) ta.getDimension(R.styleable.SlideView_radius,100);
       mSelectColor=ta.getColor(R.styleable.SlideView_selectColor,Color.parseColor("#F5F5F5"));
       mMaxValue=ta.getInt(R.styleable.SlideView_maxValue,100);
       ta.recycle();
       init();
   }

   @Override
   public void init() {
       super.init();
       mSelectPaint = new Paint();
       mSelectPaint.setColor(mSelectColor);

   }
}

触摸事件,当手指在范围时,记录x值,会在onDraw中绘制。

  @Override
 public boolean onTouchEvent(MotionEvent event) {
     if (event.getAction() == MotionEvent.ACTION_MOVE || event.getAction()==MotionEvent.ACTION_UP) {
         if (event.getX()>0 && event.getX()<=getWidth())
         mCurrentValue = ((int) event.getX());
     }
         invalidate();
     return true;
 }

视图绘制,由于是继承TextView,可直接借助background属性来设置背景,就不需要做额外的工作。然后通过drawRect来绘制滑动的大小。也就是鼠标移到哪里,绘制到哪里。

  @Override
 protected void onDraw(Canvas canvas) {
     canvas.drawRect(0, 0, mCurrentValue, getMeasuredHeight(),  mSelectPaint);
     setText(converXToValue(mCurrentValue)+"");
     super.onDraw(canvas);
 }
  private  int   converXToValue(float x){
      return  Math.round(x/getWidth()*mMaxValue) ;
 }

当通过setCurrentValue设置当前值的时候,增加一个动画,显的不那么死板。要注意不能超出最大值。给ValueAnimator添加AnimatorUpdateListener监听,不断调用invalidate()重新绘制即可。

public void  setCurrentValue(int value){
   if (value>mMaxValue){return;}
   float oldX =mCurrentValue;
   float newX =value*getWidth()/mMaxValue;
   ValueAnimator animator =ValueAnimator.ofFloat(oldX,newX);
   animator.setDuration(mAnimatorDuration);
   animator.setInterpolator(new OvershootInterpolator());
   animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
       @Override
       public void onAnimationUpdate(ValueAnimator animation) {
           float v =(float)animation.getAnimatedValue();
           mCurrentValue= v;
           invalidate();
       }
   });
   animator.start();
}

当然还有圆角问题,可以通过以下代码轻松实现。

@Override
public void draw(Canvas canvas) {
   Path path = new Path();
   path.addRoundRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mRadius, mRadius, Path.Direction.CW);
   canvas.clipPath(path);
   super.draw(canvas);
}

使用方法。

<com.hxl.course.widget.SlideView
   android:background="#E76A6A"
   app:selectColor="@color/colorPrimary"
   app:radius="100dp"
   app:maxValue="200"
   android:id="@+id/slideview"
   android:layout_width="match_parent"
   android:layout_height="40dp">
</com.hxl.course.widget.SlideView>

完整代码。

package com.hxl.course.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.OvershootInterpolator;

import androidx.annotation.Nullable;

import com.hxl.course.R;

public class SlideView extends CenterTextView {
   private  float mCurrentValue;
   private Paint mSelectPaint;
   private int mRadius=100;
   private int mSelectColor;

   private  int mAnimatorDuration=500;

   private  int mMaxValue =20;

   public SlideView(Context context) {
       this(context,null);

   }

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

   }



   public SlideView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
       super(context, attrs, defStyleAttr);
       TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlideView);
       mRadius =(int) ta.getDimension(R.styleable.SlideView_radius,100);
       mSelectColor=ta.getColor(R.styleable.SlideView_selectColor,Color.parseColor("#F5F5F5"));
       mMaxValue=ta.getInt(R.styleable.SlideView_maxValue,100);
       ta.recycle();
       init();
   }

   @Override
   public void init() {
       super.init();
       mSelectPaint = new Paint();
       mSelectPaint.setColor(mSelectColor);

   }

   @Override
   public boolean onTouchEvent(MotionEvent event) {
       if (event.getAction() == MotionEvent.ACTION_MOVE || event.getAction()==MotionEvent.ACTION_UP) {
           if (event.getX()>0 && event.getX()<=getWidth())
           mCurrentValue = ((int) event.getX());

       }
           invalidate();
       return true;
   }

   @Override
   public void draw(Canvas canvas) {

       Path path = new Path();
       path.addRoundRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mRadius, mRadius, Path.Direction.CW);
       canvas.clipPath(path);
       super.draw(canvas);

   }

   public  int getCurrentValue() {
       return Math.round(mCurrentValue/getWidth()*mMaxValue);
   }

   @Override
   protected void onDraw(Canvas canvas) {
       canvas.drawRect(0, 0, mCurrentValue, getMeasuredHeight(),  mSelectPaint);
       setText(converXToValue(mCurrentValue)+"");
       super.onDraw(canvas);

   }

   private  int   converXToValue(float x){
       return  Math.round(x/getWidth()*mMaxValue) ;
   }
   public void  setCurrentValue(int value){
       if (value>mMaxValue){return;}
       float oldX =mCurrentValue;
       float newX =value*getWidth()/mMaxValue;
       ValueAnimator animator =ValueAnimator.ofFloat(oldX,newX);
       animator.setDuration(mAnimatorDuration);
       animator.setInterpolator(new OvershootInterpolator());
       animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
           @Override
           public void onAnimationUpdate(ValueAnimator animation) {
               float v =(float)animation.getAnimatedValue();
               mCurrentValue= v;
               invalidate();
           }
       });
       animator.start();

   }

   public void setRadius(int radius) {
       mRadius = radius;
       invalidate();
   }

   public void setAnimatorDuration(int animatorDuration) {
       mAnimatorDuration = animatorDuration;
   }
}