Android自定义控件之自定义View(四)

来源:互联网 发布:c语言 static const 编辑:程序博客网 时间:2024/05/05 19:29

转载请注明出处:http://blog.csdn.net/xiaohao0724/article/details/61918934

穿插了几篇自定义控件热身之后,今天我们来继续学习自定义控件。

老规矩先上图:



OK,很简单吧,这个效果不用自定义控件也可以实现,只需要在ImageView外层包一个布局并把布局的背景设置成圆角的就可以了,那今天我们就用自定义View的方式来实现这个效果,并穿插介绍一下onMeasure的使用场景。


首先在value目录下面新建attr.xml自定义属性文件

<?xml version="1.0" encoding="utf-8"?><resources>    <!-- 图片来源 -->    <attr name="src" format="reference" />    <!-- 图片展示方式,在自定义View中控制 -->    <attr name="scaleType">        <enum name="fitXY" value="1" />        <enum name="centerCrop" value="2" />    </attr>    <!-- 圆角矩形的圆角大小 -->    <attr name="corner" format="dimension" />    <declare-styleable name="BorderImageView">        <attr name="src" />        <attr name="scaleType" />        <attr name="corner" />    </declare-styleable></resources>

新建BorderImageView继承View

public class BorderImageView extends View {/**控件的宽*/private int width;/**控件的高*/private int height;/**控件中设置的图片*/private Bitmap bitmap;/**图片的展示方式*/private int scaleType;/**圆角矩形*/private RectF rectF;/**边框的圆角*/private int corner;/**画笔*/private Paint paint;private final int FIT_XY = 1;private final int CENTER_CROP = 2;public BorderImageView(Context context) {this(context, null);}public BorderImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public BorderImageView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.BorderImageView, defStyleAttr, 0);int indexCount = typedArray.getIndexCount();for (int i = 0; i < indexCount; i++) {int index = typedArray.getIndex(i);switch (index) {case R.styleable.BorderImageView_src:bitmap = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(i, 0));break;case R.styleable.BorderImageView_scaleType:scaleType = typedArray.getInt(i, 0);break;case R.styleable.BorderImageView_corner:corner = typedArray.getDimensionPixelSize(indexCount, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,20, getResources().getDisplayMetrics()));break;default:break;}}//回收typedArray.recycle();rectF = new RectF();paint = new Paint();paint.setAntiAlias(true);paint.setStrokeWidth(5.0f);paint.setStyle(Style.FILL);paint.setColor(Color.GREEN);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec); height = MeasureSpec.getSize(heightMeasureSpec);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);rectF = new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight());canvas.drawRoundRect(rectF, corner, corner, paint);rectF.left = getPaddingLeft();rectF.right = width - getPaddingRight();rectF.top = getPaddingTop();rectF.bottom = height - getPaddingBottom();if (scaleType == FIT_XY){canvas.drawBitmap(bitmap, null, rectF, paint);} else if(scaleType == CENTER_CROP){//计算居中的矩形范围rectF.left = width / 2 - bitmap.getWidth() / 2;rectF.right = width / 2 + bitmap.getWidth() / 2;rectF.top = height / 2 - bitmap.getHeight() / 2;rectF.bottom = height / 2 + bitmap.getHeight() / 2;canvas.drawBitmap(bitmap, null, rectF, paint);}}}

布局文件activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:havorld="http://schemas.android.com/apk/res/com.havorld.borderimageview"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.havorld.borderimageview.MainActivity" >    <com.havorld.borderimageview.view.BorderImageView        android:layout_width="300dp"        android:layout_height="300dp"        android:layout_margin="10dp"        android:padding="10dp"        havorld:scaleType="fitXY"        havorld:corner="15sp"        havorld:src="@drawable/ql" /></RelativeLayout>

运行效果如下图:



我们更改布局文件为:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:havorld="http://schemas.android.com/apk/res/com.havorld.borderimageview"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.havorld.borderimageview.MainActivity" >    <com.havorld.borderimageview.view.BorderImageView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_margin="10dp"        android:padding="10dp"        havorld:scaleType="fitXY"        havorld:corner="15sp"        havorld:src="@drawable/ql" /></RelativeLayout>

运行效果如下:



那么问题来了,我们明明设置的是wrap_content,为什么显示的效果却是match_parent的呢?

当我们在xml中把自定义控件设置为具体某一个值时控件显示效果为设置的确定值,当设置为match_parent或者wrap_content时显示都为match_parent。为了在设置为wrap_content时显示正确的大小我们需要重写onMeasure()方法重新测量控件的大小


/** * 1、设置宽度 MeasureSpec.EXACTLY:精确模式.一般是设置了明确的值或者是 MATCH_PARENT * 在这种模式下,尺寸的值是具体值多少(如30dp),那么这个组件的长或宽就是多少。 * 父视图希望子视图的大小应该是specSize中指定的. *  * 2、MeasureSpec.AT_MOST:最大模式.一般为 WARP_CONTENT * 这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小 * 。子视图的大小最多是specSize中指定的值,也就是说不建议子视图的大小超过specSize中给定的值. *  * 3、MeasureSpec.UNSPECIFIED:未指定模式. * 这个就是说,当前组件可以随便用空间,不受限制,我们可以随意指定视图的大小(很少使用). *  */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec); //根据测量模式和测量值获取宽 width = getMeasuredLength(widthMeasureSpec, true); //根据测量模式和测量值获取高 height = getMeasuredLength(heightMeasureSpec, false); setMeasuredDimension(width, height);}private int getMeasuredLength(int measureSpec, boolean isWidth) {int size = 0;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);int padding = isWidth ? getPaddingLeft() + getPaddingRight(): getPaddingTop() + getPaddingBottom();if (specMode == MeasureSpec.EXACTLY) { // 指定了确定的具体值或者match_parentsize = specSize;} else if (specMode == MeasureSpec.AT_MOST) { // 设置为wrap_contentsize = isWidth ? padding + bitmap.getWidth() : padding+ bitmap.getHeight();}size = Math.min(specSize, size);//如果图片超出屏幕,就设置为夫布局测量的尺寸return size;} 

运行显示正如第一幅图所示效果图。

点击下载源码


0 0
原创粉丝点击