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

来源:互联网 发布:真实二手房源 知乎 编辑:程序博客网 时间:2024/05/18 03:14

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


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

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

最上一排中左边的返回按钮,中间的标题(自己的手机厚码)以及右边的其余功能按钮(弹出一个消息框)就是一个典型的组合控件
3. 自绘控件
自绘控件就是自己绘制的控件,主要是在View的onDraw方法中实现
3. 继承控件
继承控件继承系统已有的控件,在原有控件的属性上,引入新的属性以满足需求

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


定义自定义控件步骤

  1. 自定义View属性
  2. 从构造方法中获得自定义的View属性
  3. 重写onDraw以及onMeasure
  4. 使用自定义控件

自定义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

阅读全文
0 0
原创粉丝点击