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. 免费 SSL 安全证书

    为了保证网上传输信息的安全而在自己的 Linode VPS 上部署 SSL 加密服务.商业 CA 较贵,所以使用了自己签发的 CA.网友神爱的留言提到了 StartSSL 的免费 CA,稍做了一些调查 ...

  2. Java的变量命名

    Java的变量命名 1.首字母是英文字母.$和下划线,由字母.数字和下划线组成.  [很常规] 2.变量的命名遵循见名知义的原则.  [很重要,比如名字就用 name ,而不是用a.b.c这样的命名, ...

  3. re模块(Python中的正则表达式)

    re模块 正则表达式本身是一种小型的.高度专业化的编程语言,而在python中,通过内嵌集成re模块,程序媛们可以直接调用来实现正则匹配.正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎 ...

  4. windows下python调用c文件流程

    1.新建fun.c文件和fun.h文件 #include <stdio.h> #include <stdlib.h> #include <string.h> int ...

  5. ACM解题之(ZOJ 1094) Matrix Chain Multiplication

    题目来源: 点击打开链接 题目翻译: 矩阵乘法问题是动态规划的典型例子. 假设你必须评估一个表达式,如A * B * C * D * E,其中A,B,C,D和E是矩阵.由于矩阵乘法是关联的,乘法运算的 ...

  6. mysq数据库的安装和基本操作

    一.数据库的简介 1.什么是数据库? 数据库(database,DB)是指长期存储在计算机内的,有组织,可共享的数据的集合.数据库中的数据按一定的数学模型组织.描述和存储,具有较小的冗余,较高的数据独 ...

  7. Summaries On Java

    @1:== 和 equals(): ==用于比较引用和比较基本数据类型时具有不同的功能: 比较基本数据类型:如果两个值相同,则结果为true. 比较引用:如果引用指向内存中的同一对象,结果为true( ...

  8. 软件工作考核项(zcl)——

    注意:这里没有对代码风格做要求,因为要代码走查! 考核项 考核标准 分数等级   需求规格说明书编写 主要用例图缺失 -1   主要软件界面设计图缺失 -1   主要功能清单项目缺失 -1   主要复 ...

  9. HTML5(。。。。不完整)

    <!DOCTYPE html>  不区分大小写 <header>.<nav>.<article>.<section>.<sidebar ...

  10. WebSocket 初体验

    其实老早就觊觎 Socket 这碗美食了,在 WebSocket 发出后更是心潮澎湃... 奈何这需要后端同志的帮助,使得至今才得以品尝.(当然本文也只涉及前端部分) 以前想监听其他设备变化,大屏幕交 ...