Android自定义竖直拖动条(VerticalSeekBar)

如图:

 

 1、自定义属性 res->values下创建attrs.xml文件

    <!-- 仪表盘自定义属性 -->
    <declare-styleable name="MySeekBar">
        <!--背景颜色-->
        <attr name="bgColor" format="color"/>
        <!--进度颜色-->
        <attr name="progressColor" format="color"/>
        <!--当前进度-->
        <attr name="progress" format="float"/>
        <!--总进度-->
        <attr name="maxProgress" format="float"/>
        <!--是否显示小太阳-->
        <attr name="showSun" format="boolean"/>
        <!--是否缩放小太阳-->
        <attr name="zoomSun" format="boolean"/>
        <!--小太阳颜色-->
        <attr name="sunColor" format="color"/>
        <!--小太阳半径-->
        <attr name="circleRadius" format="dimension"/>
        <!--矩形圆角-->
        <attr name="radiusXY" format="dimension"/>
        <attr name="showText" format="boolean"/>
        <attr name="setTextColor" format="color"/>
        <attr name="setTextHeight" format="integer|dimension"/>
        <attr name="setTextSize" format="dimension"/>
    </declare-styleable>

  2、自定义View

package com.example.customseekbar;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;

import com.example.customseekbar.utils.MyColorUtils;

public class MySeekBar extends View {

    //背景颜色
    private int bgColor;
    //进度颜色
    private int progressColor;
    //当前进度
    private float progress = 10;
    //总进度
    private float maxProgress = 100;
    //当前UI高度与view高度的比例
    private double progressRate = 0;
    //记录按压时手指相对于组件view的高度
    private float downY;
    //手指移动的距离,视为亮度调整
    private float moveDistance;

    private Paint mPaint;//画笔

    //是否画亮度路标
    private boolean showSun = true;
    //太阳颜色
    private int sunColor;
    //小太阳半径
    private float circleRadius = 15;
    //矩形圆角
    private float radiusXY = 40;
    //设置是否画亮度文字
    private boolean showText = true;
    //文字位置
    private int textHeight = -1;
    //字体大写
    private float textSize = 15;
    //字体颜色
    private int textColor = Color.BLACK;
    //显示内容
    private String textContent = "";
    //是否缩放小太阳
    private boolean isSunZoom = true;

    //亮度图标margin
    private int margin = 10;

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

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

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

    private void initAttrs(Context context, AttributeSet attrs) {
        textSize = sp2px(context,textSize);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MySeekBar);
        bgColor = typedArray.getColor(R.styleable.MySeekBar_bgColor, ContextCompat.getColor(getContext(),R.color.bg_color));
        progressColor = typedArray.getColor(R.styleable.MySeekBar_progressColor,ContextCompat.getColor(getContext(),R.color.progress_color));
        progress = typedArray.getFloat(R.styleable.MySeekBar_progress, 10);
        maxProgress = typedArray.getFloat(R.styleable.MySeekBar_maxProgress, 100);
        showSun = typedArray.getBoolean(R.styleable.MySeekBar_showSun,showSun);
        isSunZoom = typedArray.getBoolean(R.styleable.MySeekBar_zoomSun,isSunZoom);
        sunColor = typedArray.getColor(R.styleable.MySeekBar_sunColor,ContextCompat.getColor(getContext(),R.color.sun_color));
        circleRadius = typedArray.getDimension(R.styleable.MySeekBar_circleRadius, circleRadius);
        radiusXY = typedArray.getDimension(R.styleable.MySeekBar_radiusXY, radiusXY);
        showText = typedArray.getBoolean(R.styleable.MySeekBar_showText,showText);
        textColor = typedArray.getColor(R.styleable.MySeekBar_setTextColor,textColor);
        textHeight = typedArray.getInt(R.styleable.MySeekBar_setTextHeight,textHeight);
        textSize = typedArray.getDimension(R.styleable.MySeekBar_setTextSize,textSize);
        typedArray.recycle();

        initPaint();
    }

    private void initPaint() {
        //获取当前百分比率
        progressRate = getProgressRate();
        //背景画笔
        mPaint = new Paint();
        mPaint.setAntiAlias(true);//抗锯齿
        mPaint.setDither(true);//设置防抖动
        mPaint.setColor(bgColor);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(0);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setTextSize(textSize);
    }

    public int getProgress(){
       return (int)progress;
    }
    public void setProgress(int mProgress){
        progress = mProgress;
        progressRate = getProgressRate();
        invalidate();
    }

    /**
     * 计算亮度比例
     */
    private double getProgressRate(){
        return (double) progress / maxProgress;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //保存
        int layerId = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), null, Canvas.ALL_SAVE_FLAG);
        onDrawBackground(canvas); //画背景
        onDrawProgress(canvas); //画进度
        onDrawText(canvas); //画文字
        onDrawSunCircle(canvas);//画小太郎
        //恢复到特定的保存点
        canvas.restoreToCount(layerId);
    }

    /**
     * 画圆弧背景
     * @param canvas
     */
    private void onDrawBackground(Canvas canvas){
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(bgColor);
        int with = getWidth();
        int height = getHeight();

//        int  colors [] = new int[2];
//        //colors[0] = Color.parseColor("#FFB660");
//        //colors[1] = Color.parseColor("#FFA757");
//        colors[0] = setColorTempToColor("6500K");
//        colors[1] = setColorTempToColor("2700K");
//        //线性变色
//        float heightkkk = (canvas.getHeight()-(int)(canvas.getHeight() * progressRate));
//        LinearGradient linearGradient = new LinearGradient((canvas.getWidth()/2),heightkkk,(canvas.getWidth()/2),0,colors,null, Shader.TileMode.CLAMP);
//        //new float[]{},中的数据表示相对位置,将150,50,150,300,划分10个单位,.3,.6,.9表示它的绝对位置。300到400,将直接画出rgb(0,232,210)
//        mPaint.setShader(linearGradient);

        RectF rectF = new RectF(0,0,with,height);
        canvas.drawRoundRect(rectF,radiusXY,radiusXY,mPaint);
    }

    /**
     * 画亮度背景-方形-随手势上下滑动而变化用来显示亮度大小
     * @param canvas
     */
    private void onDrawProgress(Canvas canvas){
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));

        int with = getWidth();
        int height = getHeight();

//        int  colors [] = new int[2];
//        //colors[0] = Color.parseColor("#FFB660");
//        //colors[1] = Color.parseColor("#FFA757");
//        colors[0] = setColorTempToColor("6500K");
//        colors[1] = setColorTempToColor("2700K");
//        //线性变色
//        float heightkkk = (canvas.getHeight()-(int)(canvas.getHeight() * progressRate));
//        LinearGradient linearGradient = new LinearGradient((canvas.getWidth()/2),heightkkk,(canvas.getWidth()/2),height,colors,null, Shader.TileMode.CLAMP);
//        //new float[]{},中的数据表示相对位置,将150,50,150,300,划分10个单位,.3,.6,.9表示它的绝对位置。300到400,将直接画出rgb(0,232,210)
//        mPaint.setShader(linearGradient);

        mPaint.setColor(progressColor);

        Log.i("打印比率:","progressRate = "+progressRate);
        float progressHeight = (canvas.getHeight()-(int)(canvas.getHeight() * progressRate));
        canvas.drawRect(0,progressHeight,with,height,mPaint);
        mPaint.setXfermode(null);
    }
    /**
     * 画文字-展示当前大小
     * @param canvas
     */
    private void onDrawText(Canvas canvas){
        if(showText) { //如果开启了则开始绘制
            mPaint.setStyle(Paint.Style.FILL);
            textContent = "" + (int) (progressRate * 100);
            mPaint.setColor(textColor);
            canvas.drawText(textContent, (canvas.getWidth() / 2 - mPaint.measureText(textContent) / 2), textHeight >= 0 ? textHeight : getHeight() / 6, mPaint);
        }
    }
    /**
     * 画亮度图标-太阳圆心
     */
    private void onDrawSunCircle(Canvas canvas){
        if(showSun){ //如果开启了则开始绘制
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setStrokeWidth(4);
            mPaint.setColor(sunColor);
            if (isSunZoom){//是否缩放太阳圆点
                float circleMaxRadius = (float) (Math.sqrt(canvas.getWidth()) * 1.5);
                float circleMinRadius = (float) (Math.sqrt(canvas.getWidth()) * 1);
                //当前圆半径
                circleRadius = (float) progressRate * (circleMaxRadius - circleMinRadius) + circleMinRadius;
                canvas.drawCircle(canvas.getWidth()/2,(float) (canvas.getHeight() * 0.85 - margin), circleRadius,mPaint);
                onDrawSunRays(canvas,canvas.getWidth()/2,(float) (canvas.getHeight() * 0.85 - margin));
            }else {
                canvas.drawCircle(canvas.getWidth()/2,(float) (canvas.getHeight() * 0.85), circleRadius,mPaint);
                onDrawSunRays(canvas,canvas.getWidth()/2,(float) (canvas.getHeight() * 0.85));
            }

        }
    }

    /**
     * 画亮度图标-太阳光芒
     */
    private void onDrawSunRays(Canvas canvas,float cx,float cy){
        mPaint.setStrokeCap(Paint.Cap.ROUND); // 定义线段断电形状为圆头
        //绘制时刻度
        canvas.translate(cx,cy);
        for (int i = 0; i < 10; i++) {
            if (isSunZoom){//是否缩放
                canvas.drawLine(circleRadius, circleRadius, (float)(circleRadius + 5 * progressRate),(float)( circleRadius + 5* progressRate), mPaint);
            }else {
                canvas.drawLine(circleRadius, circleRadius, (float)(circleRadius + 5),(float)( circleRadius + 5), mPaint);
            }
            canvas.rotate(36);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                moveDistance = downY - event.getY();
                //计算手指移动后亮度UI占比大小
                calculateLoudRate();
                downY = event.getY();
                if (listener != null) {
                    listener.onProgressChange((int)(progressRate*100));
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        invalidate();
        return true;
    }

    /**
     * 计算手指移动后亮度UI占比大小,视其为亮度大小
     */
    private void calculateLoudRate(){
        progressRate = ( getHeight() * progressRate + moveDistance) /  getHeight();
        if(progressRate >= 1){
            progressRate = 1;
        }
        if(progressRate <= 0){
            progressRate = 0;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        margin = MeasureSpec.getSize(widthMeasureSpec)/10;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
    }

    //附加
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
    }

    //分离,拆卸
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
    }

    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dp2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
    /**
     * 将sp值转换为px值,保证文字大小不变
     */
    public int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

    //进度发生变化的回调接口
    interface OnProgressChangedListener {
        void onProgressChange(int progress);
    }

    public void setOnProgressChangedListener(OnProgressChangedListener listener) {
        this.listener = listener;
    }

    //进度移动监听
    private OnProgressChangedListener listener  = null;

    private int setColorTempToColor(String color){

        String zhi = color.replace("K", "");
        Log.i("打印色温值:",""+zhi);
        int rgb [] = new int[3];
        rgb = MyColorUtils.getRgbFromTemperature(Double.valueOf(zhi),false);
        Log.i("打印色温值转颜色:","R=${rgb[0]} ,G=${rgb[1]} ,B=${rgb[2]}");
        int c = Color.argb(255,rgb[0], rgb[1], rgb[2]);
        Log.i("打印选择的值3","c=${c}");
        //返回颜色
        return c;
    }
}

  3、布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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:background="#00FF00"
    tools:context=".MySeekBarActivity">

    <com.example.customseekbar.MySeekBar
        android:id="@+id/my_seekbar"
        android:layout_width="100dp"
        android:layout_height="300dp"
        app:progress="60"
        app:maxProgress="100"
        app:bgColor="@color/bg_color"
        app:progressColor="@color/progress_color"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"></com.example.customseekbar.MySeekBar>
    <TextView
        android:id="@+id/tv_value"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text=""
        android:textSize="16sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/my_seekbar"></TextView>

</androidx.constraintlayout.widget.ConstraintLayout>

  4、activity

public class MySeekBarActivity extends AppCompatActivity {

    private MySeekBar mySeekBar;
    private TextView tvValue;

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

        tvValue = findViewById(R.id.tv_value);
        mySeekBar = findViewById(R.id.my_seekbar);
        mySeekBar.setOnProgressChangedListener(new MySeekBar.OnProgressChangedListener() {
            @Override
            public void onProgressChange(int progress) {
                tvValue.setText(""+progress);
                //tvValue.setText(""+(2700+(6500-2700)/100*progress));
            }
        });
        //设置默认
        mySeekBar.setProgress(90);
        tvValue.setText(""+mySeekBar.getProgress());
    }
}

 color

<color name="bg_color">#FFF4ED</color>
    <color name="progress_color">#FFE5C5</color>
    <color name="sun_color">#F6B160</color>

  

 

 完成

posted on 2022-08-07 17:20  巫山老妖  阅读(1269)  评论(0编辑  收藏  举报