转载至:http://blog.csdn.net/lmj623565791/article/details/24529807
自己重新实现了一遍,并在其中增加了详细的注释,以及优解决了音量可以无限加载的问题。
首先还是先看看效果图:
今天没事逛eoe,看见有人求助要做一个下面的效果,我看下面一哥们说要用12张图片,这尼玛逆天的麻烦,仔细看了一下感觉自定义控件木有问题,就花点时间写了一个。
好了,进入正题,继续我们的自定义View四部曲。
- 1、自定义View的属性
- 2、在View的构造方法中获得我们自定义的属性
- [ 3、重写onMesure ]
- 4、重写onDraw
1、先分许需要的属性,两个小块的颜色、一张中间的图片、间隙大小、一个多少个块块。分析完毕,开始写attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--CustomVolumControlBar-->
<attr name="vFirstColor" format="color" />
<attr name="vSecondColor" format="color" />
<attr name="vCircleWidth" format="dimension" />
<attr name="vDotCount" format="integer" />
<attr name="vSplitSize" format="integer" />
<attr name="vBg" format="reference" /><!--注意格式-->
<declare-styleable name="CustomVolumControlBar">
<attr name="vFirstColor" />
<attr name="vSecondColor" />
<attr name="vCircleWidth" />
<attr name="vDotCount" />
<attr name="vSplitSize" />
<attr name="vBg" />
</declare-styleable>
</resources>
2、在构造中获取这些属性:
/ 第一个圆的颜色
private int mFirstColor;
// 第二个圆的颜色
private int mSecondColor;
// 圆弧的宽度
private int mCircleWidth;
// 个数
private int mDotCount;
// 每个块块之间的间隙
private int mSplitSize;
// 中间的图片
private Bitmap mBg;
// 当前进度
private int mCurrentCount = 1;
// 画笔
private Paint mPaint;
private Rect mRect;
public CustomVolumControlBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomVolumControlBar, defStyleAttr, 0);
int indexCount = typedArray.getIndexCount();
for (int i = 0; i < indexCount; i++) {
int arr = typedArray.getIndex(i);
switch (arr) {
case R.styleable.CustomVolumControlBar_vFirstColor:
mFirstColor = typedArray.getColor(arr, Color.BLACK);
break;
case R.styleable.CustomVolumControlBar_vSecondColor:
mSecondColor = typedArray.getColor(arr, Color.WHITE);
break;
case R.styleable.CustomVolumControlBar_vCircleWidth:
mCircleWidth = typedArray.getDimensionPixelSize(arr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, 20, getResources().getDisplayMetrics()));
break;
case R.styleable.CustomVolumControlBar_vDotCount:
mDotCount = typedArray.getInt(arr, 20);
break;
case R.styleable.CustomVolumControlBar_vSplitSize:
mSplitSize = typedArray.getInt(arr, 20);
break;
case R.styleable.CustomVolumControlBar_vBg:
mBg = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(arr, 0));
break;
}
}
typedArray.recycle();
mPaint = new Paint();
mRect = new Rect();
}
3、重写onDraw
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mCircleWidth);
mPaint.setStrokeCap(Paint.Cap.ROUND);//定义线段断电形状为圆头
mPaint.setStyle(Paint.Style.STROKE);// 设置空心
int center = getWidth() / 2;// 获取圆心x坐标
int radius = center - mCircleWidth / 2;// 半径(***圆边在圆环的中间***)
/** * 椭圆 */
drawOval(canvas, center, radius);
/** * 计算内切正方形的位置 */
int relRadius = radius - mCircleWidth / 2;// 获得内圆的半径
/** * 内切正方形距离左边的距离(或顶部): * (内圆半径 - (更2 / 2) * 内圆半径) + 圆弧的宽度 */
mRect.left = (int) (relRadius - Math.sqrt(2) / 2 * relRadius) + mCircleWidth;
mRect.top = (int) (relRadius - Math.sqrt(2) / 2 * relRadius) + mCircleWidth;
/** * 内切正方形距离左边的距离 + 正方形的边长(Math.sqrt(2) * relRadius) */
mRect.right = (int) (mRect.left + Math.sqrt(2) * relRadius);
mRect.bottom = (int) (mRect.left + Math.sqrt(2) * relRadius);
/** * 如果图片比较小,那么根据图片的尺寸放置到正中心 */
if (mBg.getWidth() < Math.sqrt(2) * relRadius) {
mRect.left = mCircleWidth + (relRadius - mBg.getWidth() / 2);
mRect.top = mCircleWidth + (relRadius - mBg.getWidth() / 2);
mRect.right = mCircleWidth + (relRadius + mBg.getWidth() / 2);
mRect.bottom = mCircleWidth + (relRadius + mBg.getWidth() / 2);
}
canvas.drawBitmap(mBg, null, mRect, mPaint);
}
/** * 根据参数画出每个小块 */
private void drawOval(Canvas canvas, int center, int radius) {
/** * 根据需要的个数以及间隙计算每个块块所占的比例 * 360 ---> 每个块的大小 * ( 360 - 块个数 * 间距 ) / 块的个数 */
float itemSize = (360 * 1.0f - mDotCount * mSplitSize) / mDotCount;
// 用于定义的圆的形状和大小的界限
RectF oval = new RectF(center - radius, center - radius, center + radius, center + radius);
/** * 画圆环的第一种颜色 */
mPaint.setColor(mFirstColor);// 设置圆环的颜色
for (int i = 0; i < mDotCount; i++) {
// 根据进度画圆弧
canvas.drawArc(oval, (i * (itemSize + mSplitSize)), itemSize, false, mPaint);
}
/** * 画圆环的第二种颜色 mCurrentCount */
mPaint.setColor(mSecondColor);// 设置圆环的颜色
for (int i = 0; i < mCurrentCount; i++) {
// canvas.drawArc(rectF, -90, mProgress, false, mPaint);// 根据进度画圆弧
// 根据进度画圆弧
canvas.drawArc(oval, i * (itemSize + mSplitSize), itemSize, false, mPaint);
}
}
这里需要注意下:
画块:首先根据块数量和间隙计算,每个块所占的比例。
画图:当图比较大时,直接使用该环内切正方形大小进行约束,当图片比较小时,在正中心的位置绘制。有些数学运算过程,楼主在草稿上画了一会,不复杂,大家自己画画,我就不贴草稿了。
4、添加触摸监听:
/** * 添加触摸监听 * 当前数量+1 */
public void up() {
if (mCurrentCount < mDotCount) {
mCurrentCount++;
}
postInvalidate();
}
public void down() {
if (mCurrentCount > 0) {
mCurrentCount--;
}
postInvalidate();
}
private int xDown, xUp;
@Override
public boolean onTouchEvent(MotionEvent event) {
// return super.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
xDown = (int) event.getY();
break;
case MotionEvent.ACTION_UP:
xUp = (int) event.getY();
if (xDown < xUp) {
down();
} else {
up();
}
break;
}
return true;
}
触摸监听也得很简单哈,基本能实现,大家也可以加个最小距离加速度什么的,都行。
最后,效果图:
源码点击此处查看
嘿嘿,留个言,顶一个哈~