您的位置:首页 > 移动开发 > Android开发

自定义控件之RoundView

2016-06-18 14:40 351 查看
自定义控件一般分为分为4种,继承现有空间的 view  和  viewGoup,  还有就是自定义 view  和 viewGroup,今天我们以第一种情况为例,继承ImageView实现一个圆形图片和矩形圆角图片
第一步:   定义自定义控件继承ImageView类,重写构造方法,一般重新两个就可以了,其他的构造函数我们用不到,构造函数的第三个第四个是
               主题
                
public class RoundImageView extends ImageView {
public RoundImageView(Context context) {
super(context);
}

public RoundImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}


          
第二步:  在values创建attrs.xml 进行自定义属性,我们定义了 圆形  矩形圆角 及 角的度数这几个属性
 
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RoundImageView">
<attr name="bordaoRadius" format="dimension"/> <!-- 我们想要的角度 -->
<attr name="type">          <!-- 在这里面定义我们想要的类型 -->
<enum name="round" value="0"/>
<enum name="round" value="1"/>
</attr>
</declare-styleable>

</resources>


第三步 :  我们可以直接在xml布局文件中声明我们的自定义控件了

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.qkhl.mycustomerview.MainActivity">

<com.qkhl.mycustomerview.RoundImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:type="round"
app:bordaoRadius="7dp"/>
</RelativeLayout>

第四步:  在构造函数中获取我们设置的属性
 
/**
* 圆角大小的默认值
*/
private static final int BODER_RADIUS_DEFAULT = 10;
/**
* 图片的类型,圆形or圆角
*/
private int type;
public static final int TYPE_CIRCLE = 0;
public static final int TYPE_ROUND = 1;
/**
* 矩阵  主要用于 平面的 缩放 平移  旋转
*/
private Matrix mMatrix;
private Paint mBitmapPaint;

public RoundImageView(Context context) {
super(context);
}

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);
/**
* 获取圆角的角度
*/
int  mBorderRadius = a.getDimensionPixelSize(
R.styleable.RoundImageView_bordaoRadius, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
BODER_RADIUS_DEFAULT, getResources()
.getDisplayMetrics()));// 默认为10dp
/**
* 获取类型
*/
type = a.getInt(R.styleable.RoundImageView_type, TYPE_CIRCLE);// 默认为Circle
//回收
a.recycle();
}
}


第五步:  在onMesure()中测量我们传入的宽高,强制改变 控件大小

}

/**
* 测量控件的宽高
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 如果我们想要的type是圆形,则我们需要让宽高一致,以较小的值为准、
*/
if(type == TYPE_CIRCLE){
int min = Math.min(getMeasuredWidth(),getMeasuredHeight());
mRadius = min/2;
setMeasuredDimension(min,min);
}
}

第六步: 对图片进行渲染.裁剪 

@Override
protected void onDraw(Canvas canvas) {
//  super.onDraw(canvas);  一定要注意,把这个方法去点
Log.e("TAG", "onDraw");
if (getDrawable() == null) {
return;
}
//  canvas.drawRect();
setUpShader();

if (type == TYPE_ROUND) {
canvas.drawRoundRect(rectF, mBorderRadius, mBorderRadius,
mBitmapPaint);
} else {
canvas.drawCircle(mRadius, mRadius, mRadius, mBitmapPaint);
// drawSomeThing(canvas);
}
}

/**和
* 初始化BitmapShader
*/

/**
*
/**
* 初始化BitmapShader,利用BitmapShader进行渲染(这里只用到了缩放边缘部分),利用矩阵进行缩放
*/
/**
* BitmapShaser作用:
*
* BitmapShader(图像渲染)

  BitmapShader的作用是使用一张位图作为纹理来对某一区域进行填充。可以想象成在一块区域内铺瓷砖,只是这里的瓷砖是一张张位图而已。

  BitmapShader函数原型为:

  public BitmapShader (Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY);

  其中,参数bitmap表示用来作为纹理填充的位图;参数tileX表示在位图X方向上位图衔接形式;参数tileY表示在位图Y方向上位图衔接形式。

  Shader.TileMode有3种参数可供选择,分别为CLAMP、REPEAT和MIRROR。

  CLAMP的作用是如果渲染器超出原始边界范围,则会复制边缘颜色对超出范围的区域进行着色。REPEAT的作用是在横向和纵向上以平铺的形式重复渲染位图。MIRROR的作用是在横向和纵向上以镜像的方式重复渲染位图。

*
*/

private void setUpShader() {
/**
* 获取图片
*/
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
/**
* 将drawable转化为bitmap
*/
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 = mWinth * 1.0f / bSize;

} else if (type == TYPE_ROUND) {
Log.e("TAG",
"b'w = " + bmp.getWidth() + " , " + "b'h = "
+ bmp.getHeight());
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);
}

/**
* 当大小改变的时候调用这个方法,最少调用一次
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(type == TYPE_ROUND){
rectF = new RectF(0,0,w,h);
}
}

/**
* drawable转bitmap
*
* @param drawable 要转换的图片
* @return 结果图片
*/
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;
}

第七步:异常情况的处理

/**
* 异常退出的时候保存
* @return
*/
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable(STATE_INSTANCE, super.onSaveInstanceState());
bundle.putInt(STATE_TYPE, type);
bundle.putInt(STATE_BORDER_RADIUS, mBorderRadius);
return bundle;
}

/**
* 恢复的时候从这里面获取
* @param state
*/
@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.mBorderRadius = bundle.getInt(STATE_BORDER_RADIUS);
} else {
super.onRestoreInstanceState(state);
}

}

/**
* 用于代码中调用
* @param borderRadius
*/
public void setBorderRadius(int borderRadius) {
int pxVal = dp2px(borderRadius);
if (this.mBorderRadius != pxVal) {
this.mBorderRadius = pxVal;
invalidate();
}
}

/**
* 用于代码中调用
* @param type
*/
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();
}

}

public int dp2px(int dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, getResources().getDisplayMetrics());
}


该项目的地址为:  https://github.com/zhouwei5200/MyRoundView
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息