Android AttributeSetカスタムコントロールの使用
21166 ワード
Androidが公式に提供する標準コントロールが私たちのニーズを満たすことができない場合、私たちは自分のニーズに応じてコントロールをカスタマイズすることができます.主にAttributeSetとPaint図面クラスを使用します.具体的な操作手順は以下の通りです.
1.私たちのカスタムコントロールは他のコントロールと同じように、Viewを継承するクラスとして書くべきですが、このクラスの属性は自分で決めています.
定的
2.res/valuesディレクトリの下にattrsを作成します.xmlのファイルで、コントロールのプロパティの定義を追加します.たとえば、次のようにします.
attrs属性に関する知識、すなわちAttr属性がXMLでどのように定義されているかについては、カスタム属性のValue値には10種類のタイプとそのタイプの組合せ値があり、その具体的な使用方法は以下の通りである.
3.コントロールクラスのコンストラクタを完了するにはAttributeSetを使用し、コンストラクタでカスタムコントロールクラスの変数とattrsを使用します.xml
のプロパティを接続します.
ここでは完全な例で説明します.例のオリジナルはgithubから来て、自作の注釈です.
4.カスタムコントロールクラスをレイアウト用のxmlファイルに定義します.
注意したいのは
これはカスタムコントロールのネーミングスペースです.
1.私たちのカスタムコントロールは他のコントロールと同じように、Viewを継承するクラスとして書くべきですが、このクラスの属性は自分で決めています.
定的
2.res/valuesディレクトリの下にattrsを作成します.xmlのファイルで、コントロールのプロパティの定義を追加します.たとえば、次のようにします.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CProgressBar_Style" >
<attr name="WhorlView_SmallWhorlColor" format="color" />
<attr name="WhorlView_MiddleWhorlColor" format="color" />
<attr name="WhorlView_BigWhorlColor" format="color" />
<attr name="WhorlView_CircleSpeed" format="integer" />
<attr name="WhorlView_Parallax" >
<enum name="fast" value="1" />
<enum name="medium" value="0" />
<enum name="slow" value="2" />
</attr>
<attr name="WhorlView_SweepAngle" format="float" />
<attr name="WhorlView_StrokeWidth" format="float" />
</declare-styleable>
</resources>
attrs属性に関する知識、すなわちAttr属性がXMLでどのように定義されているかについては、カスタム属性のValue値には10種類のタイプとそのタイプの組合せ値があり、その具体的な使用方法は以下の通りである.
1. reference: ID。
(1) :
<declare-styleable name = " ">
<attr name = "background" format = "reference" />
</declare-styleable>
(2) :
<ImageView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:background = "@drawable/ ID"
/>
2. color: 。
(1) :
<declare-styleable name = " ">
<attr name = "textColor" format = "color" />
</declare-styleable>
(2) :
<TextView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:textColor = "#00FF00"
/>
3. boolean: 。
(1) :
<declare-styleable name = " ">
<attr name = "focusable" format = "boolean" />
</declare-styleable>
(2) :
<Button
android:layout_width = "42dip"
android:layout_height = "42dip"
android:focusable = "true"
/>
4. dimension: 。
(1) :
<declare-styleable name = " ">
<attr name = "layout_width" format = "dimension" />
</declare-styleable>
(2) :
<Button
android:layout_width = "42dip"
android:layout_height = "42dip"
/>
5. float: 。
(1) :
<declare-styleable name = "AlphaAnimation">
<attr name = "fromAlpha" format = "float" />
<attr name = "toAlpha" format = "float" />
</declare-styleable>
(2) :
<alpha
android:fromAlpha = "1.0"
android:toAlpha = "0.7"
/>
6. integer: 。
(1) :
<declare-styleable name = "AnimatedRotateDrawable">
<attr name = "visible" />
<attr name = "frameDuration" format="integer" />
<attr name = "framesCount" format="integer" />
<attr name = "pivotX" />
<attr name = "pivotY" />
<attr name = "drawable" />
</declare-styleable>
(2) :
<animated-rotate
xmlns:android = "http://schemas.android.com/apk/res/android"
android:drawable = "@drawable/ ID"
android:pivotX = "50%"
android:pivotY = "50%"
android:framesCount = "12"
android:frameDuration = "100"
/>
7. string: 。
(1) :
<declare-styleable name = "MapView">
<attr name = "apiKey" format = "string" />
</declare-styleable>
(2) :
<com.google.android.maps.MapView
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:apiKey = "0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g"
/>
8. fraction: 。
(1) :
<declare-styleable name="RotateDrawable">
<attr name = "visible" />
<attr name = "fromDegrees" format = "float" />
<attr name = "toDegrees" format = "float" />
<attr name = "pivotX" format = "fraction" />
<attr name = "pivotY" format = "fraction" />
<attr name = "drawable" />
</declare-styleable>
(2) :
<rotate
xmlns:android = "http://schemas.android.com/apk/res/android"
android:interpolator = "@anim/ ID"
android:fromDegrees = "0"
android:toDegrees = "360"
android:pivotX = "200%"
android:pivotY = "300%"
android:duration = "5000"
android:repeatMode = "restart"
android:repeatCount = "infinite"
/>
9. enum: 。
(1) :
<declare-styleable name=" ">
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
</declare-styleable>
(2) :
<LinearLayout
xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "vertical"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
>
</LinearLayout>
10. flag: 。
(1) :
<declare-styleable name=" ">
<attr name="windowSoftInputMode">
<flag name = "stateUnspecified" value = "0" />
<flag name = "stateUnchanged" value = "1" />
<flag name = "stateHidden" value = "2" />
<flag name = "stateAlwaysHidden" value = "3" />
<flag name = "stateVisible" value = "4" />
<flag name = "stateAlwaysVisible" value = "5" />
<flag name = "adjustUnspecified" value = "0x00" />
<flag name = "adjustResize" value = "0x10" />
<flag name = "adjustPan" value = "0x20" />
<flag name = "adjustNothing" value = "0x30" />
</attr>
</declare-styleable>
(2) :
<activity
android:name = ".StyleAndThemeActivity"
android:label = "@string/app_name"
android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
:
。
(1) :
<declare-styleable name = " ">
<attr name = "background" format = "reference|color" />
</declare-styleable>
(2) :
<ImageView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:background = "@drawable/ ID|#00FF00"
/>
3.コントロールクラスのコンストラクタを完了するにはAttributeSetを使用し、コンストラクタでカスタムコントロールクラスの変数とattrsを使用します.xml
のプロパティを接続します.
ここでは完全な例で説明します.例のオリジナルはgithubから来て、自作の注釈です.
package com.duke.chenyouhui.customprograssbar;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by chenyouhui on 2015/9/7.
* progressbar
*/
public class CProgressBar extends View{
private static final int CIRCLE_NUM = 3;//
//
public static final int FAST = 1;
public static final int MEDIUM = 0;
public static final int SLOW = 2;
//
private static final int PARALLAX_FAST = 60;
private static final int PARALLAX_MEDIUM = 72;
private static final int PARALLAX_SLOW = 90;
private static final long REFRESH_DURATION = 16L;//
//
private long mCircleTime;
//
private int[] mLayerColors = new int[CIRCLE_NUM];
//
private int mCircleSpeed;
//
private int mParallaxSpeed;
//
private float mSweepAngle;
//
private float mStrokeWidth;
/**
* Code View , xml ,
* , View( View, CustomTextView
* Button) , ,
* R.attr.CustomizeStyle 。 ,
* Style, , Style Application Activity
* Theme Style
*/
public CProgressBar(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public CProgressBar(Context context) {
this(context,null,0);
}
/**
* @param attrs AttributeSet
* @param context
* @param defStyleAttr
*/
public CProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//
Resources res = getResources();
final int defaultSmallColor = res.getColor(R.color.material_red);
final int defaultMiddleColor = res.getColor(R.color.material_green);
final int defaultBigColor = res.getColor(R.color.material_blue);
// , 180°/s, ,
final int defaultCircleSpeed = 270;
final float defaultSweepAngle = 90f;
final float defaultStrokeWidth = 5f;
if (attrs != null){
final TypedArray typedArray =
context.obtainStyledAttributes(attrs,R.styleable.CProgressBar_Style);
mLayerColors[0] = typedArray.getColor(
R.styleable.CProgressBar_Style_WhorlView_SmallWhorlColor,defaultSmallColor);
mLayerColors[1] = typedArray.getColor(
R.styleable.CProgressBar_Style_WhorlView_MiddleWhorlColor,defaultMiddleColor);
mLayerColors[2] = typedArray.getColor(
R.styleable.CProgressBar_Style_WhorlView_BigWhorlColor,defaultBigColor);
mCircleSpeed = typedArray.getInt(
R.styleable.CProgressBar_Style_WhorlView_CircleSpeed,defaultCircleSpeed);
int index = typedArray.getInt(R.styleable.CProgressBar_Style_WhorlView_Parallax,0);
//
setParallax(index);
mSweepAngle = typedArray.getFloat(
R.styleable.CProgressBar_Style_WhorlView_SweepAngle,defaultSweepAngle);
//
if(mSweepAngle <= 0 || mSweepAngle >= 360) {
throw new IllegalArgumentException("sweep angle out of bound");
}
mStrokeWidth = typedArray.getFloat(
R.styleable.CProgressBar_Style_WhorlView_StrokeWidth,defaultStrokeWidth);
typedArray.recycle();//
}else {
/** , Code View ,
* xml , attrs , attrs ,
*
*/
mLayerColors[0] = defaultSmallColor;
mLayerColors[1] = defaultMiddleColor;
mLayerColors[2] = defaultBigColor;
mCircleSpeed = defaultCircleSpeed;
mParallaxSpeed = PARALLAX_MEDIUM;
mSweepAngle = defaultSweepAngle;
mStrokeWidth = defaultStrokeWidth;
}
}
private void setParallax(int index) {
switch (index) {
case FAST:
mParallaxSpeed = PARALLAX_FAST;
break;
case MEDIUM:
mParallaxSpeed = PARALLAX_MEDIUM;
break;
case SLOW:
mParallaxSpeed = PARALLAX_SLOW;
break;
default:
throw new IllegalStateException("no such parallax type");
}
}
/**
* onDraw
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//
for (int i = 0; i < CIRCLE_NUM; i++){
// , ,
float angle = (mCircleSpeed + mParallaxSpeed * (CIRCLE_NUM - i - 1)) * mCircleTime * 0.001f;
drawArc(canvas, i, angle);
}
}
private boolean mIsCircling = false;
/**
*
* */
public void start(){
mIsCircling = true;
new Thread(new Runnable() {
@Override
public void run() {
mCircleTime = 0L;
while (mIsCircling){
invalidateWrap();
mCircleTime = mCircleTime + REFRESH_DURATION;
try {
Thread.sleep(REFRESH_DURATION);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
public void stop(){
mIsCircling = false;
mCircleTime = 0L;
invalidateWrap();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void invalidateWrap() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
postInvalidateOnAnimation();
} else {
postInvalidate();
}
}
/**
* canvas.drawArc
* oval: 。
* startAngle: 。
* sweepAngle: 。
* useCenter: ,true ,false 。
* paint: 。
*/
private void drawArc(Canvas canvas, int index, float startAngle){
Paint paint = checkArcPaint(index);
// view
RectF oval = checkRectF(calcuRadiusRatio(index));//
canvas.drawArc(oval,startAngle,mSweepAngle,false,paint);
}
private Paint mArcPaint;
private Paint checkArcPaint(int index){
if (mArcPaint == null){
mArcPaint = new Paint();
}else {
mArcPaint.reset();
}
mArcPaint.setColor(mLayerColors[index]);
mArcPaint.setStyle(Paint.Style.STROKE);
mArcPaint.setStrokeWidth(mStrokeWidth);
mArcPaint.setAntiAlias(true);// , , 。
return mArcPaint;
}
private RectF mOval;//
private RectF checkRectF(float radiusRatio){
if (mOval == null){
mOval = new RectF();
}
float start = getMinLength() * 0.5f * (1 - radiusRatio) + mStrokeWidth;
float end = getMinLength() - start;
mOval.set(start,start,end,end);//Set the rectangle's coordinates to the specified values.
return mOval;
}
private int getMinLength(){
return Math.min(getWidth(),getHeight());
}
private static final float RADIUS_RATIO_P = 0.2f;
/**
*
*
* @param index
* @return
*/
private float calcuRadiusRatio(int index){
return 1f - (CIRCLE_NUM - index - 1) * RADIUS_RATIO_P;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int minSize = (int) (mStrokeWidth * 4 * CIRCLE_NUM);
int wantSize = (int) (mStrokeWidth * 8 * CIRCLE_NUM);
int size = measureSize(widthMeasureSpec,wantSize,minSize);
// setMeasuredDimension(int,int) View measured width and height。
setMeasuredDimension(size,size);
}
/**
* view
*
* @param measureSpec MeasureSpec 。 MeasureSpec
* (size) (mode) 。MeasureSpecs <size,
* mode> int , 。 ,
* int size mode。
* @param wantSize
* @param minSize
* @return
*/
public static int measureSize(int measureSpec, int wantSize, int minSize){
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY){
result = specSize;//parent
}else {
result = wantSize;//
if (specMode == MeasureSpec.AT_MOST){
// wrap_content
result = Math.min(result,specSize);
}
}
//
return Math.max(result, minSize);
}
}
4.カスタムコントロールクラスをレイアウト用のxmlファイルに定義します.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
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=".MainActivity">
<com.duke.chenyouhui.customprograssbar.CProgressBar
android:id="@+id/cprogress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:WhorlView_SweepAngle = "10"
app:WhorlView_Parallax = "fast"/>
</RelativeLayout>
注意したいのは
xmlns:app="http://schemas.android.com/apk/res-auto"
これはカスタムコントロールのネーミングスペースです.