随笔 - 245  文章 - 0  评论 - 11  阅读 - 50万

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

如图:

 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!-- 仪表盘自定义属性 -->
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
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、布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

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

  

 

 完成

posted on   巫山老妖  阅读(1305)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
历史上的今天:
2016-08-07 XML的Pull解析
2016-08-07 HttpURLConnection请求网络数据
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示