资源描述
android 自定义圆角头像以及使用declare-styleable进行配置属性解析
由于最新项目中正在检查UI是否与效果图匹配,结果关于联系人模块给的默认图片是四角稍带弧度的圆角,而我们截取的图片是正方形的,现在要给应用统一替换。应用中既用到大圆角头像(即整个头像是圆的)又用到四角稍带弧度的圆角头像,封装一下以便重用。以下直接见代码
[java] view plain copy 在CODE上查看代码片派生到我的代码片
package com.test.demo;
import com.test.demo.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.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.widget.ImageView;
/**
* 圆角imageview
*/
public class RoundImageView extends ImageView {
private static final String TAG = "RoundImageView";
/**
* 图片的类型,圆形or圆角
*/
private int type;
public static final int TYPE_CIRCLE = 0;
public static final int TYPE_ROUND = 1;
/**
* 圆角大小的默认值
*/
private static final int CORNER_RADIUS_DEFAULT = 10;
/**
* 圆角的大小
*/
private int mCornerRadius;
/**
* 绘图的Paint
*/
private Paint mBitmapPaint;
// 按下状态颜色
private Paint mPressedColorPaint;
private int pressedColor;
/**
* 圆角的半径
*/
private int mRadius;
/**
* 3x3 矩阵,主要用于缩小放大
*/
private Matrix mMatrix;
/**
* view的宽度
*/
private int mWidth;
private RectF mRoundRect;
public RoundImageView(Context context, AttributeSet attrs) {
super(context, attrs);
mMatrix = new Matrix();
mBitmapPaint = new Paint();
mBitmapPaint.setAntiAlias(true);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.RoundImageView);
pressedColor = a.getColor(R.styleable.RoundImageView_pressed_color, -1);
if (pressedColor != -1) {
mPressedColorPaint = new Paint();
mPressedColorPaint.setAntiAlias(true);
mPressedColorPaint.setColor(pressedColor);
}
mCornerRadius = a.getDimensionPixelSize(
R.styleable.RoundImageView_corner_radius, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
CORNER_RADIUS_DEFAULT, getResources()
.getDisplayMetrics()));// 默认为10dp
type = a.getInt(R.styleable.RoundImageView_type, TYPE_CIRCLE);// 默认为Circle
a.recycle();
}
public RoundImageView(Context context) {
this(context, null);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 如果类型是圆形,则强制改变view的宽高一致,以小值为准
*/
if (type == TYPE_CIRCLE) {
mWidth = Math.min(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
mRadius = mWidth / 2;
}
}
/**
* 初始化BitmapShader
*/
private void setUpShader() {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
Bitmap bmp = drawableToBitamp(drawable);
// 将bmp作为着色器,就是在指定区域内绘制bmp
// 渲染图像,使用图像为绘制图形着色
BitmapShader mBitmapShader = new BitmapShader(bmp, TileMode.CLAMP,
TileMode.CLAMP);
float scale = 1.0f;
if (type == TYPE_CIRCLE) {
// 拿到bitmap宽或高的小值
int bSize = Math.min(bmp.getWidth(), bmp.getHeight());
scale = mWidth * 1.0f / bSize;
} else if (type == TYPE_ROUND) {
if (!(bmp.getWidth() == getWidth() && bmp.getHeight() == getHeight())) {
// 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;
scale = Math.max(getWidth() * 1.0f / bmp.getWidth(),
getHeight() * 1.0f / bmp.getHeight());
}
}
// shader的变换矩阵,我们这里主要用于放大或者缩小
mMatrix.setScale(scale, scale);
// 设置变换矩阵
mBitmapShader.setLocalMatrix(mMatrix);
// 设置shader
mBitmapPaint.setShader(mBitmapShader);
}
@Override
protected void onDraw(Canvas canvas) {
if (getDrawable() == null) {
return;
}
setUpShader();
if (type == TYPE_ROUND) {
canvas.drawRoundRect(mRoundRect, mCornerRadius, mCornerRadius,
mBitmapPaint);
if (isPressed() && mPressedColorPaint != null) {
canvas.drawRoundRect(mRoundRect, mCornerRadius, mCornerRadius,
mPressedColorPaint);
}
} else {
canvas.drawCircle(mRadius, mRadius, mRadius, mBitmapPaint);
if (isPressed() && mPressedColorPaint != null) {
canvas.drawCircle(mRadius, mRadius, mRadius, mPressedColorPaint);
}
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Log.d(TAG, "onSizeChanged,w=" + w + ",h=" + h + ",oldw=" + oldw
+ ",oldh=" + oldh);
// 圆角图片的范围
if (type == TYPE_ROUND) {
mRoundRect = new RectF(0, 0, w, h);
}
}
/**
* drawable转bitmap
*/
private Bitmap drawableToBitamp(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bd = (BitmapDrawable) drawable;
return bd.getBitmap();
}
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
drawable.draw(canvas);
return bitmap;
}
private static final String STATE_INSTANCE = "state_instance";
private static final String STATE_TYPE = "state_type";
private static final String STATE_BORDER_RADIUS = "state_border_radius";
private static final String STATE_PRESSED_COLOR = "state_pressed_color";
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(STATE_INSTANCE, super.onSaveInstanceState());
bundle.putInt(STATE_TYPE, type);
bundle.putInt(STATE_BORDER_RADIUS, mCornerRadius);
bundle.putInt(STATE_PRESSED_COLOR, pressedColor);
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
super.onRestoreInstanceState(((Bundle) state)
.getParcelable(STATE_INSTANCE));
this.type = bundle.getInt(STATE_TYPE);
this.mCornerRadius = bundle.getInt(STATE_BORDER_RADIUS);
this.pressedColor = bundle.getInt(STATE_PRESSED_COLOR);
if (pressedColor != -1) {
mPressedColorPaint = new Paint();
mPressedColorPaint.setAntiAlias(true);
mPressedColorPaint.setColor(pressedColor);
}
} else {
super.onRestoreInstanceState(state);
}
}
public void setType(int type) {
if (this.type != type) {
this.type = type;
if (this.type != TYPE_ROUND && this.type != TYPE_CIRCLE) {
this.type = TYPE_CIRCLE;
}
requestLayout();
}
}
@Override
protected void dispatchSetPressed(boolean pressed) {
// imageView.setClickable(true),或imageView.setOnClickListener时才可触发dispatchSetPressed
super.dispatchSetPressed(pressed);
invalidate();
}
}
declare-styleable:declare-styleable是给自定义控件添加自定义属性用的。
发现它的很多属性都是通过自定义控件并设定相关的配置属性进行配置,即我们需要
1.首先,先写attrs.xml
在res-vlaues文件夹下创建资源文件attrs.xml或则自定义一个资源文件xx.xml,都可以。
之后在里面配置are-styleable ,name为RoundImageView
[html] view plain copy 在CODE上查看代码片派生到我的代码片
<declare-styleable name="RoundImageView">
<attr name="corner_radius" format="dimension" />
<attr name="pressed_color" format="color" />
<attr name="type">
<enum name="circle" value="0" />
<enum name="round" value="1" />
</attr>
</declare-styleable>
这里的format就是格式,里面的就是这个属性对应的格式,下面列出来大致的格式有:
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:颜色值
<declare-styleable name = "名称">
<attr name = "textColor" format = "color" />
</declare-styleable>
3. boolean:布尔值
<declare-styleable name = "名称">
<attr name = "focusable" format = "boolean" />
</declare-styleable>
4. dimension:尺寸值。注意,这里如果是dp那就会做像素转换
<declare-styleable name = "名称">
<attr name = "layout_width" format = "dimension" />
</declare-styleable>
5. float:浮点值。
6. integer:整型值。
7. string:字符串
8. fraction:百分数。
9. enum:枚举值
10. flag:是自己定义的,类似于 android:gravity="top",就是里面对应了自己的属性值。
11. reference|color:颜色的资源文件。
12.reference|boolean:布尔值的资源文件
注意://由于reference是从资源文件中获取:所以在XML文件中写这个属性的时候必须 personattr:name="@string/app_name"这种格式,否则会出错
2.设置好属性文件后,在使用的布局中写相关配置:
[html] view lain copy 在CODE上查看代码片派生到我的代码片
< android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.test.demo.RoundImageView
android:id="@+id/contact_head_img"
android:layout_width="@dimen/contact_photo_size"
android:layout_height="@dimen/contact_photo_size"
android:background="@null"
android:clickable="false"
android:focusable="false"
android:scaleType="fitCenter"
android:layout_marginBottom="@dimen/contact_picture_margin_top_bottom"
android:layout_marginLeft="@dimen/contact_view_margin_left"
android:layout_marginTop="@dimen/contact_picture_margin_top_bottom"
roundImageattr:corner_radius="5dp"
roundImageattr:pressed_color="@color/bottom_bar_bg_color"
roundImageattr:type="circle"
/>
</RelativeLayout>
注意这里首先要配置这个attr:即 xmlns:roundImageattr=" "
对应结构是:xmlns:自己定义的名称="你程序的package包名"
(包名即在AndroidManifest.xml里面可看到, package="com.test.demo" 这样格式的)
之后在布局中自定义的类中设相关属性:
自己定义的名称:自定义的属性 ="属性值";
3.最后在自定义控件的构造方法中获取你配置的属性值:
[html] view plain copy 在CODE上查看代码片派生到我的代码片
public RoundImageView(Context context, AttributeSet attrs) {
super(context, attrs);
mMatrix = new Matrix();
mBitmapPaint = new Paint();
mBitmapPaint.setAntiAlias(true);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.RoundImageView);
pressedColor = a.getColor(R.styleable.RoundImageView_pressed_color, -1);
if (pressedColor != -1) {
mPressedColorPaint = new Paint();
mPressedColorPaint.setAntiAlias(true);
mPressedColorPaint.setColor(pressedColor);
}
mCornerRadius = a.getDimensionPixelSize(
R.styleable.RoundImageView_corner_radius, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
CORNER_RADIUS_DEFAULT, getResources()
.getDisplayMetrics()));// 默认为10dp
type = a.getInt(R.styleable.RoundImageView_type, TYPE_CIRCLE);// 默认为Circle
a.recycle();
}
展开阅读全文