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

【Android学习】自定义View的实现----以圆形图片控件为例

2017-06-12 16:57 796 查看
在Android开发过程中,系统提供的控件有时并不能满足要求,往往需要通过自定义控件来实现需求。今天我们以圆形图片控件为例,简单介绍下它的具体实现方式

自定义控件的三种实现方式

组合控件

组合控件,简单说来就是将系统的一些控件组合成一个新的控件便于使用。譬如常见的标题栏控件,如图



最上一排中左边的返回按钮,中间的标题(自己的手机厚码)以及右边的其余功能按钮(弹出一个消息框)就是一个典型的组合控件

3. 自绘控件

自绘控件就是自己绘制的控件,主要是在View的onDraw方法中实现

3. 继承控件

继承控件继承系统已有的控件,在原有控件的属性上,引入新的属性以满足需求

我们今天的圆形图片控件就是结合上述的方法2.3实现。

定义自定义控件步骤

自定义View属性

从构造方法中获得自定义的View属性

重写onDraw以及onMeasure

使用自定义控件

自定义View属性

首先,在res/values中声明我们这个控件样式以及相关属性



一般说来的话attr.xml这个文件声明的都是view的相关信息,如果没有可以新建,也可以在别的xml文件中声明。

我这边的项目是要开发一个登录功能的界面,需要这个控件作为头像。因此,需要自定义的一些属性是这个头像的边框颜色以及宽度。具体代码如下。

<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="borderColor" format="color" />
<attr name="borderWidth" format="dimension" />

<declare-styleable name="RoundImageView">
<attr name="borderColor" />
<attr name="borderWidth" />
</declare-styleable>
</resources>

View attrs_imageviewplus.xml


format中color表示颜色值,dimension表示尺寸值,其他的格式不再赘述。

从构造方法中获得自定义View的属性

新建一个RoundImageView类,继承ImageView,正如我们上面所述的这是一种继承控件(好像由于版本原因,单单继承ImageView无法正常显示,Android Studio自身给我提供了这样一个新的继承android.support.v7.widget.AppCompatImageView,可以使用)

public class RoundImageView extends android.support.v7.widget.AppCompatImageView{
private Paint mPaintBitmap = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mPaintBorder = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap mRawBitmap;
private BitmapShader mShader;
private Matrix mMatrix = new Matrix();
...


之后是在构造方法中获取我们刚刚定义的新的属性

private static final int DEFAULT_BORDER_COLOR = Color.TRANSPARENT;
private static final int DEFAULT_BORDER_WIDTH = 0;

public ImageViewPlus(Context context, AttributeSet attrs) {
super(context, attrs);
// 从attrs.xml文件中读取我们对RoundImageView的配置信息
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ImageViewPlus);
mBorderColor = ta.getColor(R.styleable.ImageViewPlus_borderColor, DEFAULT_BORDER_COLOR);
mBorderWidth = ta.getDimensionPixelSize(R.styleable.ImageViewPlus_borderWidth, dip2px(DEFAULT_BORDER_WIDTH));
ta.recycle();
}
...


重写onDraw以及onMeasure

在View实现之前,View会先做一次测量,算出自己需要占用多大的面积,是一个Measure过程。View给我们提供了onMeasure的接口去实现测量方法,这个重写是可选的,今天这个空间并不需要,我们需要重写的是绘制接口onDraw,代码如下。

@Override
protected void onDraw(Canvas canvas) {
Bitmap rawBitmap = getBitmap(getDrawable());
if (rawBitmap != null){
int viewWidth = getWidth();
int viewHeight = getHeight();
int viewMinSize = Math.min(viewWidth, viewHeight);
float dstWidth = viewMinSize;
float dstHeight = viewMinSize;
if (mShader == null || !rawBitmap.equals(mRawBitmap)){
mRawBitmap = rawBitmap;
mShader = new BitmapShader(mRawBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
}
if (mShader != null){
mMatrix.setScale((dstWidth - mBorderWidth * 2) / rawBitmap.getWidth(), (dstHeight - mBorderWidth * 2) / rawBitmap.getHeight());
mShader.setLocalMatrix(mMatrix);
}
mPaintBitmap.setShader(mShader);
mPaintBorder.setStyle(Paint.Style.STROKE);
mPaintBorder.setStrokeWidth(mBorderWidth);
mPaintBorder.setColor(mBorderColor);
float radius = viewMinSize / 2.0f;
canvas.drawCircle(radius, radius, radius - mBorderWidth / 2.0f, mPaintBorder);
canvas.translate(mBorderWidth, mBorderWidth);
canvas.drawCircle(radius - mBorderWidth, radius - mBorderWidth, radius - mBorderWidth, mPaintBitmap);
} else {
super.onDraw(canvas);
}
}

private Bitmap getBitmap(Drawable drawable){
if (drawable instanceof BitmapDrawable){
return ((BitmapDrawable)drawable).getBitmap();
} else if (drawable instanceof ColorDrawable){
Rect rect = drawable.getBounds();
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
int color = ((ColorDrawable)drawable).getColor();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawARGB(Color.alpha(color), Color.red(color), Color.green(color), Color.blue(color));
return bitmap;
} else {
return null;
}
}

private int dip2px(int dipVal)
{
float scale = getResources().getDisplayMetrics().density;
return (int)(dipVal * scale + 0.5f);
}
}


关于代码中的一些解释

mMatrix.setScale与mShader.setLocalMatrix主要是为了保证图片在不同尺寸的手机大小下,固定大小的头像能够自适应缩放来吻合背景。

mPaintBitmap.setShader决定了圆形中的具体内容,而canvas.drawCircle 决定了画出来的形状是圆形

边框的实现则是先用实心纯色的Paint画了一个圆边,再在其中画圆形图片

使用自定义控件

最后就可以在我们的布局中使用我们自定义的控件拉,贴一下我自己的布局与效果图,大家可以做一下参考

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="10" />

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">

<com.example.aaron_zrrrrr.gcms.view.RoundImageView
android:id="@+id/roundImageView"
android:layout_centerInParent="true"
android:layout_width="150dp"
android:layout_height="150dp"
android:src="@drawable/login"
/>
</RelativeLayout>

<LinearLayout
android:id="@+id/accountRel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:orientation="horizontal"
android:layout_weight="2">

<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1" />

<EditText
android:id="@+id/account"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="2"
android:hint="account"
android:textSize="15sp" />

<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>

<LinearLayout
android:id="@+id/pwdRel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:orientation="horizontal"
android:layout_weight="2">

<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1" />

<EditText
android:id="@+id/pwd"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="2"
android:hint="account"
android:textSize="15sp" />

<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>

<LinearLayout
android:id="@+id/loginRel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:orientation="horizontal"
android:layout_weight="2">

<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1" />

<Button
android:id="@+id/login"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="2"
android:hint="Login"
android:textSize="15sp" />

<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="30" />
</LinearLayout>


丑丑的登录界面OuO



参考源码:http://www.cnblogs.com/snser/p/5159126.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 控件