1.在values建立attrs.xml,写出你需要的属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="firstColor" format="color" />
<attr name="secondColor" format="color" />
<attr name="circleWidth" format="dimension" />
<attr name="dotCount" format="integer" />
<attr name="splitSize" format="integer" />
<attr name="bg" format="reference" /> <declare-styleable name="CustomVolumControlBar">
<attr name="firstColor" />
<attr name="secondColor" />
<attr name="circleWidth" />
<attr name="dotCount" />
<attr name="splitSize" />
<attr name="bg" />
</declare-styleable> </resources>

2.创建view类并实现所需要的业务,具体看代码,代码中写的很详细:

package com.zzw.Custom.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.media.AudioManager;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View; import com.zzw.Custom.R; /**
* Created by zzw on 2016/6/1.
* 描述:
*/
public class CustomVolumControlBar extends View { /**
* 第一圈的颜色
*/
private int mFirstColor; /**
* 第二圈的颜色
*/
private int mSecondColor;
/**
* 圈的宽度
*/
private int mCircleWidth;
/**
* 画笔
*/
private Paint mPaint;
/**
* 当前进度
*/
private int mCurrentCount = 3; /**
* 中间的图片
*/
private Bitmap mImage;
/**
* 每个块块间的间隙
*/
private int mSplitSize;
/**
* 个数
*/
private int mCount; /**
* 中间图片界限
*/
private Rect mRect; private AudioManager mAudioManager; private Handler mHandler = new Handler(); public CustomVolumControlBar(Context context) {
this(context, null);
} public CustomVolumControlBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public CustomVolumControlBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
} private void init(Context context, AttributeSet attrs, int defStyleAttr) { TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomVolumControlBar, defStyleAttr, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.CustomVolumControlBar_firstColor:
this.mFirstColor = a.getColor(attr, Color.BLACK);
break; case R.styleable.CustomVolumControlBar_secondColor:
this.mSecondColor = a.getColor(attr, Color.WHITE);
break; case R.styleable.CustomVolumControlBar_circleWidth:
this.mCircleWidth = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_PX, 20, getResources().getDisplayMetrics()));
break; case R.styleable.CustomVolumControlBar_dotCount:
this.mCount = a.getInt(attr, 20);// 默认20
break; case R.styleable.CustomVolumControlBar_splitSize:
this.mSplitSize = a.getInt(attr, 20);
break; case R.styleable.CustomVolumControlBar_bg:
this.mImage = BitmapFactory.decodeResource(getResources(), a.getResourceId(attr, 0));
break;
}
}
a.recycle();
mPaint = new Paint();
mRect = new Rect();
if (mAudioManager == null)
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); /**
* 获取到最大音量和当前音量
*/
mCount = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
mCurrentCount = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); Log.e("=====", "mCount:" + mCount + " mCurrentCount:" + mCurrentCount);
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas); mPaint.setAntiAlias(true);//消除锯齿
mPaint.setStrokeWidth(mCircleWidth);//设置圆圈宽度
mPaint.setStrokeCap(Paint.Cap.ROUND);//定义线段电形状圆头
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);//设置空心
int centre = getWidth() / 2;//得到圆心
int radius = centre - mCircleWidth / 2;//得到半径 drawOval(canvas, centre, radius); /**
* 计算内切正方形的位置
*/
int relRadius = radius - mCircleWidth / 2;// 获得内圆的半径
/**
* 内切正方形的距离顶部 = mCircleWidth + relRadius - √2 / 2
*/
mRect.left = (int) (relRadius - Math.sqrt(2) * 1.0f / 2 * relRadius) + mCircleWidth; /**
* 内切正方形的距离左边 = mCircleWidth + relRadius - √2 / 2
*/
mRect.top = (int) (relRadius - Math.sqrt(2) * 1.0f / 2 * relRadius) + mCircleWidth; mRect.bottom = (int) (mRect.left + Math.sqrt(2) * relRadius);
mRect.right = (int) (mRect.left + Math.sqrt(2) * relRadius); /**
* 如果图片比较小,那么根据图片的尺寸放置到正中心
*/
if (mImage.getWidth() < Math.sqrt(2) * relRadius) {
mRect.left = (int) (mRect.left + Math.sqrt(2) * relRadius * 1.0f / 2 - mImage.getWidth() * 1.0f / 2);
mRect.top = (int) (mRect.top + Math.sqrt(2) * relRadius * 1.0f / 2 - mImage.getHeight() * 1.0f / 2);
mRect.right = (int) (mRect.left + mImage.getWidth());
mRect.bottom = (int) (mRect.top + mImage.getHeight());
}
// 绘图
canvas.drawBitmap(mImage, null, mRect, mPaint);
} /**
* 画块块去
*/
private void drawOval(Canvas canvas, int centre, int radius) { /**
* 根据需要画的个数以及间隙计算每个块块所占的比例*360
*/
float itemSize = (360 * 1.0f - mCount * mSplitSize) / mCount;
/**
* 用于定义的圆弧的形状和大小的界限
*/
RectF oval = new RectF(centre - radius, centre - radius, centre + radius, centre + radius);
mPaint.setColor(mFirstColor);// 设置圆环的颜色
for (int i = 0; i < mCount; i++) {
canvas.drawArc(oval, i * (itemSize + mSplitSize), itemSize, false, mPaint);// 根据进度画圆弧
}
mPaint.setColor(mSecondColor); // 设置圆环的颜色
for (int i = 0; i < mCurrentCount; i++) {
canvas.drawArc(oval, i * (itemSize + mSplitSize), itemSize, false, mPaint); // 根据进度画圆弧
}
} /**
* 当前数量+1
*/
public synchronized void up() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mCurrentCount++;
if (mCurrentCount > mCount)
mCurrentCount = mCount; postInvalidate();
setVolume(mCurrentCount);
}
}, 100);
} /**
* 设置音量
*
* @param index
*/
private void setVolume(int index) {
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 1);
} /**
* 当前数量-1
*/
public synchronized void down() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mCurrentCount--;
if (mCurrentCount < 0)
mCurrentCount = 0;
postInvalidate();
setVolume(mCurrentCount);
}
}, 100);
} private int lastY, nowY; @Override
public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastY = (int) event.getY();
break; case MotionEvent.ACTION_MOVE:
nowY = (int) event.getY();
int updateYCount = (nowY - lastY) / 30;
if (updateYCount > 0) {
for (int i = 0; i < updateYCount; i++) {
down();
}
} else if (updateYCount < 0) {
for (int i = updateYCount; i < 0; i++) {
up();
}
}
lastY = nowY;
break;
}
return true;
}
}

3.在xml中引用:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context="com.zzw.Custom.MainActivity"> <com.zzw.Custom.widget.CustomVolumControlBar
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerInParent="true"
app:bg="@mipmap/ic_horn"
app:circleWidth="5dp"
app:firstColor="@color/colorPrimary"
app:secondColor="@color/colorAccent"
app:splitSize="10" />
</RelativeLayout>

当然,在实际操作中OnTouch事件一般是放在activity或者Fragment里面的,只需在代码中复制出去即可

最后十分感谢鸿洋大神,让我们学到了很多,该篇与鸿洋大神相关的的博客地址:http://blog.csdn.net/lmj623565791/article/details/24529807

CustomVolumControlBar

基于鸿洋博客自定于View实现的android音量调节控件的更多相关文章

  1. 基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  2. 基于.NetCore开发博客项目 StarBlog - (7) 页面开发之文章详情页面

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  3. 基于.NetCore开发博客项目 StarBlog - (8) 分类层级结构展示

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  4. 基于.NetCore开发博客项目 StarBlog - (12) Razor页面动态编译

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  5. 基于.NetCore开发博客项目 StarBlog - (16) 一些新功能 (监控/统计/配置/初始化)

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  6. 基于.NetCore开发博客项目 StarBlog - (17) 自动下载文章里的外部图片

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  7. 象写程序一样写博客:搭建基于github的博客

    象写程序一样写博客:搭建基于github的博客   前言 github 真是无所不能.其 Pages 功能 支持上传 html,并且在页面中显示.于是有好事者做了一个基于 github 的博客管理工具 ...

  8. Lucene5.5.4入门以及基于Lucene实现博客搜索功能

    前言 一直以来个人博客的搜索功能很蹩脚,只是自己简单用数据库的like %keyword%来实现的,所以导致经常搜不到想要找的内容,而且高亮显示.摘要截取等也不好实现,所以决定采用Lucene改写博客 ...

  9. 基于Hexo搭建博客并部署到Github Pages

    基于Hexo搭建博客并部署到Github Pages 之前在简书上写东西,觉得自己还是太浮躁.本来打算用Flask自己写一个,以为是微框架就比较简单,naive.HTML.CSS.JS等都要学啊,我几 ...

随机推荐

  1. 数据性能调校——查看最耗资源的各种SQL

    从计划高速缓存中清除查询计划 DBCC FREEPROCCACHE 清除缓存中的过程 DBCC DROPCLEANBUFFERS清除内存中的数据 SELECT DB_ID('你的数据库名') tota ...

  2. brew和brew cask安装

    brew 是从下载源码解压然后 ./configure && make install ,同时会包含相关依存库.并自动配置好各种环境变量,而且易于卸载. 这个对程序员来说简直是福音,简 ...

  3. when you are old

    When you are old william Butler Yeats When you are old and grey and full of sleep And nodding by the ...

  4. Git处理 行结束符

    Dealing with line endings (Windows) 如果你正在使用Git在GitHub上和别人协作的话,确保Git处理行结束符的配置已经正确配置了. 每次在键盘上按下return键 ...

  5. 利用onresize使得div可以随着屏幕大小而自适应的代码

    原文:http://www.jb51.net/article/21831.htm 当我们让div居中时候,一般有两种方法,一种是固定左右宽度,也就是使用像素绝对定位:另一种是用百分比来相对定位,在这种 ...

  6. Loadrunder脚本篇——Run-time Settings之Preferences

    打开Preferences设置对话框,这里提供了对运行时的参数选择设置 Enable Image and Text Check 开启图片和文本检查.允许用户在回放期间通过web_find(文本检测)或 ...

  7. iOS 什么是函数式编程

    前言:当前只做理解性的常规背书,根据不断深入学习会不断丰富解读内容,欢迎评论提意见 函数式编程:Functional Programming 1 基本解释: 函数式编程 是一种思维模式,一种编程思想, ...

  8. Python学习进程(8)字符串內建函数

        Python字符串內建函数实现了string模块的大部分方法,并包括了对Unicode编码方式的支持.     (1)capitalize(): 将字符串的第一个字母变成大写,其他字母变小写. ...

  9. 配置NFS作为HDFS高可用的共享存储系统

    所有命令或步骤: 首先,在各个节点上安装nfs服务 yum install -y nfs service rpcbind start service nfs start 配置开机自启动服务 chkco ...

  10. Shell编程之case条件

    一.case条件语句 1.语法 case "变量" in 值 1) 指令 1... ;; 值 2) 指令 2... ;; *) 指令 3... esac case条件语句的执行流程 ...