Android 自定义属性的使用

来源:互联网 发布:mysql数据库同步工具 编辑:程序博客网 时间:2024/05/17 03:59

最近在做项目使用到一些自定义控件的内容,主要是总结了一下自定义属性的使用。

自定义属性使用涉及到:attr.xml的属性定义,java文件代码的属性定义引用,布局文件的命名空间定义和引用。

一、在attr文件的定义属性

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="CircleImageView">        <attr name="border_width" format="dimension" />        <attr name="border_color" format="color" />    </declare-styleable></resources>

说明一下:

attr子元素:
定义具体的属性,format表示这个属性的值的类型,类型有以下几种:
     1.reference:参考指定Theme中资源ID,这个类型意思就是你传的值可以是引用资源
     2.string:字符串,如果你想别人既能直接写值也可以用类似"@string/test"引用资源的方式,可以写成format="string|reference"
     3.Color:颜色
     4.boolean:布尔值
     5.dimension:尺寸值
     6.float:浮点型
     7.integer:整型
     8.fraction:百分数
     9.enum:枚举 ,如果你提供的属性只能让别人选择,不能随便传入,就可以写成这样
        <attr name="language">
                   <enum name="china" value="1"/>
                   <enum name="English" value="2"/>
            </attr>
     10.flag:位或运算
declare-styleable子元素:
定义一个styleable对象,每个styleable对象就是一组attr属性的集合 注意:这里的name属性并不是一定要和自定义类名相同,只是为了好区分对应类的属性而已
注意:上面的属性资源文件定义了该属性之后,至于到底是哪个自定义View组件中来使用该属性,该属性到底能发挥什么作用, 就不归该属性资源文件管了,也就是说这个属性资源文件是个公共的,大家都可以用,但是为了方便管理,一般都是一个自定义View里的属性写成一个declare-styleable集合.属性资源所定义的属性到底可以返回什么作用,取决于自定义组件的代码实现


二、在布局文件使用上述属性:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:cby="http://schemas.android.com/apk/com.cby.ui"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:orientation="vertical" >  <RelativeLayout    android:layout_width="match_parent"    android:layout_height="0dp"    android:layout_weight="1"    android:padding="@dimen/base_padding"    android:background="@color/light">    <com.cby.ui.CircleImageView      android:layout_width="160dp"      android:layout_height="160dp"      android:layout_centerInParent="true"      android:src="@drawable/demo"      cby:border_width="2dp"      cby:border_color="@color/dark" />  </RelativeLayout>  <RelativeLayout    android:layout_width="match_parent"    android:layout_height="0dp"    android:layout_weight="1"    android:padding="@dimen/base_padding"    android:background="@color/dark">    <com.cby.ui.CircleImageView      android:layout_width="160dp"      android:layout_height="160dp"      android:layout_centerInParent="true"      android:src="@drawable/lena"      cby:border_width="2dp"      cby:border_color="@color/light" />  </RelativeLayout></LinearLayout>

说明一下:

在上述使用自定义的属性格式是:

cby:border_width="2dp"cby:border_color="@color/light"

前面的是事cby而不是android

是因为在这里需要引入自己的命名空间,我在正常使用android的是因为sdk中已经定义好了同意的命名空间

在新建布局文件默认是

xmlns:android="http://schemas.android.com/apk/res/android"

这里是我是添加了自己的命名空间

xmlns:cby="http://schemas.android.com/apk/res/com.cby.ui
定义的命名空间格式是
"http://schemas.android.com/apk/+你的清单文件所定义的报名,也就是R所在的包名,其实原理就是类似在java文件引用R资源

还有一种方便的是

"http://schemas.android.com/apk/res-auto   意思就是自动匹配

三、在自定义控件(java文件)中,属性的使用:

package com.cby.ui;import com.cby.ui.R;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Bitmap;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.RectF;import android.graphics.Shader;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.ColorDrawable;import android.graphics.drawable.Drawable;import android.util.AttributeSet;import android.widget.ImageView;public class CircleImageView extends ImageView {  private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;  private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;  private static final int COLORDRAWABLE_DIMENSION = 1;  private static final int DEFAULT_BORDER_WIDTH = 0;  private static final int DEFAULT_BORDER_COLOR = Color.BLACK;  private final RectF mDrawableRect = new RectF();  private final RectF mBorderRect = new RectF();  private final Matrix mShaderMatrix = new Matrix();  private final Paint mBitmapPaint = new Paint();  private final Paint mBorderPaint = new Paint();  private int mBorderColor = DEFAULT_BORDER_COLOR;  private int mBorderWidth = DEFAULT_BORDER_WIDTH;  private Bitmap mBitmap;  private BitmapShader mBitmapShader;  private int mBitmapWidth;  private int mBitmapHeight;  private float mDrawableRadius;  private float mBorderRadius;  private boolean mReady;  private boolean mSetupPending;  public CircleImageView(Context context) {    super(context);  }  public CircleImageView(Context context, AttributeSet attrs) {    this(context, attrs, 0);  }  public CircleImageView(Context context, AttributeSet attrs, int defStyle) {    super(context, attrs, defStyle);    super.setScaleType(SCALE_TYPE);    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);    mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);    mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR);    a.recycle();    mReady = true;    if (mSetupPending) {      setup();      mSetupPending = false;    }  }  @Override  public ScaleType getScaleType() {    return SCALE_TYPE;  }  @Override  public void setScaleType(ScaleType scaleType) {    if (scaleType != SCALE_TYPE) {      throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));    }  }  @Override  protected void onDraw(Canvas canvas) {    if (getDrawable() == null) {      return;    }    canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);    canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);  }  @Override  protected void onSizeChanged(int w, int h, int oldw, int oldh) {    super.onSizeChanged(w, h, oldw, oldh);    setup();  }  public int getBorderColor() {    return mBorderColor;  }  public void setBorderColor(int borderColor) {    if (borderColor == mBorderColor) {      return;    }    mBorderColor = borderColor;    mBorderPaint.setColor(mBorderColor);    invalidate();  }  public int getBorderWidth() {    return mBorderWidth;  }  public void setBorderWidth(int borderWidth) {    if (borderWidth == mBorderWidth) {      return;    }    mBorderWidth = borderWidth;    setup();  }  @Override  public void setImageBitmap(Bitmap bm) {    super.setImageBitmap(bm);    mBitmap = bm;    setup();  }  @Override  public void setImageDrawable(Drawable drawable) {    super.setImageDrawable(drawable);    mBitmap = getBitmapFromDrawable(drawable);    setup();  }  @Override  public void setImageResource(int resId) {    super.setImageResource(resId);    mBitmap = getBitmapFromDrawable(getDrawable());    setup();  }  private Bitmap getBitmapFromDrawable(Drawable drawable) {    if (drawable == null) {      return null;    }    if (drawable instanceof BitmapDrawable) {      return ((BitmapDrawable) drawable).getBitmap();    }    try {      Bitmap bitmap;      if (drawable instanceof ColorDrawable) {        bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);      } else {        bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);      }      Canvas canvas = new Canvas(bitmap);      drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());      drawable.draw(canvas);      return bitmap;    } catch (OutOfMemoryError e) {      return null;    }  }  private void setup() {    if (!mReady) {      mSetupPending = true;      return;    }    if (mBitmap == null) {      return;    }    mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);    mBitmapPaint.setAntiAlias(true);    mBitmapPaint.setShader(mBitmapShader);    mBorderPaint.setStyle(Paint.Style.STROKE);    mBorderPaint.setAntiAlias(true);    mBorderPaint.setColor(mBorderColor);    mBorderPaint.setStrokeWidth(mBorderWidth);    mBitmapHeight = mBitmap.getHeight();    mBitmapWidth = mBitmap.getWidth();    mBorderRect.set(0, 0, getWidth(), getHeight());    mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);    mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);    mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);    updateShaderMatrix();    invalidate();  }  private void updateShaderMatrix() {    float scale;    float dx = 0;    float dy = 0;    mShaderMatrix.set(null);    if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {      scale = mDrawableRect.height() / (float) mBitmapHeight;      dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;    } else {      scale = mDrawableRect.width() / (float) mBitmapWidth;      dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;    }    mShaderMatrix.setScale(scale, scale);    mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth);    mBitmapShader.setLocalMatrix(mShaderMatrix);  }}
说明一下:

 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0); mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH); mBorderColor = a.getColor(R.styleable.CircleImageView_border_color, DEFAULT_BORDER_COLOR); a.recycle();

TypeArray本身就是一个存放资源的Array,首先从上下文获取R.styleable.CircleImageView这个属性资源的资源数组,

attrs是构造函数传进来的,应该对应着attr.xml文件。

a.getDimensionPixelSize(R.styleable.CircleImageView_border_width, DEFAULT_BORDER_WIDTH);就是用来获取attr.xml下对应的资源属性

该文件是定义属性名和格式的地方,需要用<declare-styleable name="CircleImageView"></declare-styleable>包围所有属性。

其中name为该属性集的名字,主要用途是标识该属性集。在获取某属性标识时,用到"R.styleable.CircleImageView_border_width",

很显然,他在每个属性前面都加了"CircleImageView_"。

a.recycle()绑定资源结束。




0 0