一、需求简述
1、定义音量调节View(如图示)
2、通过监听供调用者使用
二、实现
1、由于seekbar定义给开发者开放的只能定义规则一级、二级背景或滑块,如实现如图所示不规则的我们可以自己在onDraw()方法内根据progress值绘制灰色背景和上面二级背景
2、绘制的图形拆分:先绘制半个圆、在绘制一个梯形、在绘制另外一个圆
三、具体代码实现
1、定义VolumeSeekbar所需要的属性,如背景、进度条背景、梯形最小高度及最大高度
<declare-styleable name="VolumeSeekbar">
<attr name="seekBarBgColor" format="color" />
<attr name="seekBarProgressColor" format="color" />
<attr name="seekBarMaxHeight" format="dimension" />
<attr name="seekBarMinHeight" format="dimension" />
</declare-styleable>
2、重写SeekBar内onDraw()方法,向画布绘制背景及根据进度绘制半圆及梯形
package com.chiyue.qiye.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.SeekBar;
import com.chiyue.qiye.R;
import com.chiyue.qiye.service.AudioServiceManager;
import com.chiyue.qiye.utils.DensityUtil;
/**
* 音量seekbar
*
* @date 2018/12/12 下午3:21
* @author: kaily_zhou
*/
public class VolumeSeekbar extends android.support.v7.widget.AppCompatSeekBar implements SeekBar.OnSeekBarChangeListener {
private OnSeekBarChangeListener onSeekBarChangeListener;
private Paint paint;
private Paint progressPaint;
public final static String TAG = "VolumeSeekbar";
public final static int BG_DEFAULT_COLOR = 0XFFF1F1F1;
public final static int PROGRESS_DEFAULT_COLOR = 0XFFF5B92E;
public final static int MAX_HEIGHT = 16;
public final static int MIN_HEIGHT = 8;
private int maxHeight;
private int minHeight;
private int maxProgress;
private int bgColor;
private int progressColor;
public VolumeSeekbar(Context context) {
super(context);
init(context, null);
}
public VolumeSeekbar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public VolumeSeekbar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attributeSet) {
initAttr(context, attributeSet);
initPaint(context);
initListener();
setPadding(getPaddingLeft(), getPaddingTop(), maxHeight / 2, getPaddingBottom());
}
private void initAttr(Context context, AttributeSet attrs) {
if (attrs != null) {
try {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.VolumeSeekbar);
if (typedArray != null) {
bgColor = typedArray.getColor(R.styleable.VolumeSeekbar_seekBarBgColor, BG_DEFAULT_COLOR);
progressColor = typedArray.getColor(R.styleable.VolumeSeekbar_seekBarProgressColor, PROGRESS_DEFAULT_COLOR);
maxHeight = typedArray.getDimensionPixelSize(R.styleable.VolumeSeekbar_seekBarMaxHeight, DensityUtil.dip2px(context, MAX_HEIGHT));
minHeight = typedArray.getDimensionPixelSize(R.styleable.VolumeSeekbar_seekBarMinHeight, DensityUtil.dip2px(context, MIN_HEIGHT));
typedArray.recycle();
} else {
initDefaultAttr(context);
}
} catch (Exception e) {
initDefaultAttr(context);
}
} else {
initDefaultAttr(context);
}
}
private void initDefaultAttr(Context context) {
bgColor = BG_DEFAULT_COLOR;
progressColor = PROGRESS_DEFAULT_COLOR;
maxHeight = DensityUtil.dip2px(context, MAX_HEIGHT);
minHeight = DensityUtil.dip2px(context, MIN_HEIGHT);
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
if (width > 0) {
int height = Math.max(maxHeight, minHeight);
setMeasuredDimension(width, height);
}
}
@Override
protected synchronized void onDraw(Canvas canvas) {
int width = getMeasuredWidth();
//画背景
drawSeekBarBg(width, canvas);
width = width - (maxHeight / 2);
maxProgress = getMax();
if (maxProgress <= 0) {
return;
}
double progressWidth = width * ((float) getProgress() / (float) maxProgress);
if (progressWidth < minHeight / 2) {
drawSeekBarProgressHeader(progressWidth, canvas);
} else {
drawSeekBarProgressHeader((double) minHeight / (double) 2, canvas);
drawSeekBarProgress(width, progressWidth, canvas);
}
}
private void drawSeekBarBg(int width, Canvas canvas) {
//中间梯形
Path path = new Path();
path.moveTo(minHeight / 2, (maxHeight - minHeight) / 2);
path.lineTo(width - (maxHeight / 2), 0);
path.lineTo(width - (maxHeight / 2), maxHeight);
path.lineTo(minHeight / 2, (maxHeight + minHeight) / 2);
path.close();
canvas.drawPath(path, paint);
//画左边半圆
Path pathLeft = new Path();
RectF mRectF = new RectF(0, (maxHeight - minHeight) / 2, minHeight, (maxHeight + minHeight) / 2);
pathLeft.arcTo(mRectF, 90, 180);
pathLeft.close();
canvas.drawPath(pathLeft, paint);
//画右边半圆
Path pathRight = new Path();
RectF rectF = new RectF(width - maxHeight, 0, width, maxHeight);
pathRight.arcTo(rectF, 270, 180);
pathRight.close();
canvas.drawPath(pathRight, paint);
}
private void drawSeekBarProgressHeader(double progressWidth, Canvas canvas) {
float ratio = (float) progressWidth / ((float) minHeight / (float) 2);
float angle = (180 - ratio * 180) / (float) 2;
Path pathLeft = new Path();
RectF mRectF = new RectF(0, (maxHeight - minHeight) / 2, minHeight, maxHeight / 2 + minHeight / 2);
pathLeft.arcTo(mRectF, 90 + angle, 180 - angle * 2);
pathLeft.close();
canvas.drawPath(pathLeft, progressPaint);
}
private void drawSeekBarProgress(int width, double progressWidth, Canvas canvas) {
int w = width - (minHeight / 2);
int h = (maxHeight - minHeight) / 2;
double z = Math.sqrt(Math.pow(w, 2) + Math.pow(h, 2));
double pw = progressWidth - minHeight / 2;
double pz = pw * z / (double) w;
double ph = (int) Math.sqrt(Math.pow(pz, 2) - Math.pow(pw, 2));
double top = (maxHeight - minHeight) / 2 - ph;
Path progressPath = new Path();
progressPath.moveTo(minHeight / 2, (maxHeight - minHeight) / 2);
progressPath.lineTo((int) progressWidth, (int) top);
progressPath.lineTo((int) progressWidth, (int) (maxHeight - top));
progressPath.lineTo(minHeight / 2, (maxHeight + minHeight) / 2);
progressPath.close();
canvas.drawPath(progressPath, progressPaint);
//画右边半圆
double radius = (maxHeight - top * 2) / (double) 2;
Path pathRight = new Path();
RectF rectF = new RectF((int) (progressWidth - radius), (int) top, (int) (progressWidth + radius), (int) (top + radius * 2));
pathRight.arcTo(rectF, 270, 180);
pathRight.close();
canvas.drawPath(pathRight, progressPaint);
}
@Override
public void setOnSeekBarChangeListener(OnSeekBarChangeListener onSeekBarChangeListener) {
this.onSeekBarChangeListener = onSeekBarChangeListener;
}
private void initListener() {
super.setOnSeekBarChangeListener(this);
}
private void initPaint(Context context) {
if (context == null) {
return;
}
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setDither(true);
paint.setAntiAlias(true);
paint.setColor(bgColor);
progressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
progressPaint.setStyle(Paint.Style.FILL);
progressPaint.setDither(true);
progressPaint.setAntiAlias(true);
progressPaint.setColor(progressColor);
}
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (onSeekBarChangeListener != null) {
onSeekBarChangeListener.onProgressChanged(seekBar, i, b);
postInvalidate();
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (onSeekBarChangeListener != null) {
onSeekBarChangeListener.onStartTrackingTouch(seekBar);
}
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (onSeekBarChangeListener != null) {
onSeekBarChangeListener.onStopTrackingTouch(seekBar);
}
}
}
3、VolumeSeekbar使用
xml定义
<com.chiyue.qiye.view.VolumeSeekbar
android:id="@+id/seekBarVoice"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13dp"
android:background="@null"
android:thumb="@null"
app:seekBarBgColor="#F1F1F1"
app:seekBarMaxHeight="16dip"
app:seekBarMinHeight="8dip"
app:seekBarProgressColor="#F5B92E" />
代码监听
seekBarVoice.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
AudioServiceManager.get().getMusicService().setVolume(i);
int currentVolume = AudioServiceManager.get().getMusicService().getCurrentVolume();
int maxVolume = AudioServiceManager.get().getMusicService().getMaxVolume();
seekBar.setProgress(currentVolume);
tvVoice.setText(currentVolume * 100 / maxVolume + "%");
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});