通过android自定义View之(一)------基本篇,我们学习了如何画一些基本的图形,android自定义View之(二)------简单进度条显示样例篇,我们明白了如何自定义一个view。这个文章,我打算参考《Android 自定义View (四) 视频音量调控》,明白一个音量调控控件是如何实现的。
在做这个之前,我们还要讲一个自定义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);
view的更新界面有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。
<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>
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); } }
<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>
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); } }
1.Android 自定义View (一)
http://blog.csdn.net/lmj623565791/article/details/24252901
2.Android 自定义View (二) 进阶
http://blog.csdn.net/lmj623565791/article/details/24300125
3.Android 自定义View (三) 圆环交替 等待效果
http://blog.csdn.net/lmj623565791/article/details/24500107
4.Android 自定义View (四) 视频音量调控
http://blog.csdn.net/lmj623565791/article/details/24529807