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>
完成