通过android自定义View之(一)------基本篇,我们学习了如何画一些基本的图形,android自定义View之(二)------简单进度条显示样例篇,我们明白了如何自定义一个view。这个文章,我打算参考《Android 自定义View (四) 视频音量调控》,明白一个音量调控控件是如何实现的。
1.onMeasure方法的说明:
在做这个之前,我们还要讲一个自定义view中的重要的一个方法(onMeasure)方法:
默认onMeasure方法:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
当我们阅读《Android 自定义View (一)》和《Android 自定义View (二) 进阶》,我们会知道在重写onMeasure方法时,对于view的高和宽有三种情况:match_parent, wrap_content,xxx(详细值)。这对应了MeasureSpec的specMode三种类型:
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMeasure方法”。
重写onMeasure方法,有几个方法比较重要:参考下面的方法,我们可以
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height ;
if (widthMode == MeasureSpec.EXACTLY)
{
width = widthSize;
}
确定view的宽和高:
setMeasuredDimension(width, height);
2.postInvalidate()和invalidate()
view的更新界面有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。
3.视频音量调控样例
3.1 效果图
3.2 \res\values\attrs_custom_volum_control_bar.xml
<resources>
<attr name="firstColor" format="color" />
<attr name="secondColor" format="color" />
<attr name="circleWidth" format="dimension" />
<attr name="dotCount" format="integer" />
<attr name="currentCount" format="integer" />
<attr name="splitSize" format="integer" />
<attr name="bg" format="reference"></attr>
<declare-styleable name="CustomVolumControlBar">
<attr name="firstColor" />
<attr name="secondColor" />
<attr name="circleWidth" />
<attr name="dotCount" />
<attr name="currentCount" />
<attr name="splitSize" />
<attr name="bg" />
</declare-styleable>
</resources>
3.3 CustomVolumControlBar.java
package com.example.administrator.customview.customview03;
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.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import com.example.administrator.customview.R;
public class CustomVolumControlBar extends View {
public static final String TAG = "CustomVolumControlBar";
private int mFirstColor;
private int mSecondColor;
private int mCircleWidth;
private int mCount;
private int mCurrentCount;
private int mSplitSize;
private Bitmap mImage;
private Paint mPaint;
private Rect mRect;
private int xDown, xUp;
public CustomVolumControlBar(Context context) {
super(context);
init(null, 0);
}
public CustomVolumControlBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public CustomVolumControlBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
// Load attributes
final TypedArray a = getContext().obtainStyledAttributes(
attrs, R.styleable.CustomVolumControlBar, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++)
{
int attr = a.getIndex(i);
switch (attr)
{
case R.styleable.CustomVolumControlBar_firstColor:
mFirstColor = a.getColor(attr, Color.GREEN);
break;
case R.styleable.CustomVolumControlBar_secondColor:
mSecondColor = a.getColor(attr, Color.CYAN);
break;
case R.styleable.CustomVolumControlBar_bg:
mImage = BitmapFactory.decodeResource(getResources(), a.getResourceId(attr, 0));
break;
case R.styleable.CustomVolumControlBar_circleWidth:
mCircleWidth = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_PX, 20, getResources().getDisplayMetrics()));
break;
case R.styleable.CustomVolumControlBar_dotCount:
mCount = a.getInt(attr, 10);
break;
case R.styleable.CustomVolumControlBar_currentCount:
mCurrentCount = a.getInt(attr, 5);
break;
case R.styleable.CustomVolumControlBar_splitSize:
mSplitSize = a.getInt(attr, 20);
break;
}
}
a.recycle();
mPaint = new Paint();
mRect = new Rect();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mCircleWidth);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
int centre = getWidth() / 2;
int radius = centre - mCircleWidth / 2;
drawOval(canvas, centre, radius);
int relRadius = radius - mCircleWidth / 2;
mRect.left = (int) (relRadius - Math.sqrt(2) * 1.0f / 2 * relRadius) + mCircleWidth;
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) {
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);
}
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
xDown = (int) event.getY();
break;
case MotionEvent.ACTION_UP:
xUp = (int) event.getY();
if (xUp > xDown)
{
down();
} else
{
up();
}
break;
}
return true;
}
public void up()
{
if(mCurrentCount < mCount){
mCurrentCount++;
postInvalidate();
}
}
public void down()
{
if(mCurrentCount > 0){
mCurrentCount--;
postInvalidate();
}
}
public int getCurrentCount() {
return mCurrentCount;
}
public void setCurrentCount(int mCurrentCount) {
if(mCurrentCount > this.mCount){
this.mCurrentCount = mCount;
}else if(mCurrentCount < 0){
this.mCurrentCount = 0;
}else{
this.mCurrentCount = mCurrentCount;
}
postInvalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
3.4 \res\layout\activity_custom_view_activity3.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.example.administrator.customview.customview03.CustomViewActivity3">
<com.example.administrator.customview.customview03.CustomVolumControlBar
android:id="@+id/customVolumControlBar01"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_centerHorizontal="true"
android:background="#4b4534"
app:firstColor="#fbfbfb"
app:secondColor="#22211a"
app:circleWidth="20dp"
app:dotCount="10"
app:currentCount="6"
app:splitSize="40"
app:bg="@drawable/ic_lock_ringer_on"
/>
<com.example.administrator.customview.customview03.CustomVolumControlBar
android:id="@+id/customVolumControlBar02"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginTop="20dp"
android:layout_centerHorizontal="true"
android:layout_below="@+id/customVolumControlBar01"
android:background="#4b4534"
app:firstColor="#fbfbfb"
app:secondColor="#22211a"
app:circleWidth="10dp"
app:dotCount="6"
app:currentCount="4"
app:splitSize="40"
app:bg="@drawable/clock_dial"
/>
</RelativeLayout>
3.5 CustomViewActivity3.java
package com.example.administrator.customview.customview03;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import com.example.administrator.customview.R;
public class CustomViewActivity3 extends ActionBarActivity {
public static final String TAG = "CustomVolumControlBar";
private CustomVolumControlBar customVolumControlBar01;
private CustomVolumControlBar customVolumControlBar02;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_view_activity3);
init();
}
private void init() {
customVolumControlBar01 = (CustomVolumControlBar) findViewById(R.id.customVolumControlBar01);
customVolumControlBar02 = (CustomVolumControlBar) findViewById(R.id.customVolumControlBar02);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
Log.i(TAG,"KEYCODE_VOLUME_DOWN");
customVolumControlBar01.down();
customVolumControlBar02.down();
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
customVolumControlBar01.up();
customVolumControlBar02.up();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
参考资料:
http://blog.csdn.net/lmj623565791/article/details/24252901
http://blog.csdn.net/lmj623565791/article/details/24300125
3.Android 自定义View (三) 圆环交替 等待效果
http://blog.csdn.net/lmj623565791/article/details/24500107
http://blog.csdn.net/lmj623565791/article/details/24529807