android 自定义view


在继承View类时,需要重写两个方法,分别是onMeasure和onLayout。
1,在方法onMeasure中调用setMeasuredDimension方法

void android.view.View.setMeasuredDimension(int measuredWidth, int measuredHeight)

public class MyTestView extends View {
	
	private Bitmap backGround;
	private Context mContext;
	public MyTestView(Context context) {
		super(context);
		init(context);
		
		// TODO Auto-generated constructor stub
	}

	public MyTestView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
		// TODO Auto-generated constructor stub
	}

	public MyTestView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init(context);
		// TODO Auto-generated constructor stub
	}
	
	private void init(Context mContext){
		mContext = mContext;
		backGround = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.jiaoyu);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		System.out.println("widthMeasureSpec"+widthMeasureSpec+"    heightMeasureSpec"+heightMeasureSpec);
		
		setMeasuredDimension(measureWidth
				(widthMeasureSpec), measureHeight(heightMeasureSpec));
		
		
	}
	
	private int measureWidth(int widthMeasureSpec) {
		int result = 0;
		int model = MeasureSpec.getMode(widthMeasureSpec);
		int size = MeasureSpec.getSize(widthMeasureSpec);
		if (model == MeasureSpec.EXACTLY) {
			result = size;
		} else {

			if (backGround != null) {
				result = backGround.getWidth() + getPaddingLeft()
						+ getPaddingRight();
			}

			if (model == MeasureSpec.AT_MOST) {
				result = Math.min(result, size);
			}
		}
		return result;
	}
	
	private int measureHeight(int heightMeasureSpec){
		int result = 0;
		int model = MeasureSpec.getMode(heightMeasureSpec);
		int size = MeasureSpec.getSize(heightMeasureSpec);
		if (model == MeasureSpec.EXACTLY) {
			result = size;
		} else {

			if (backGround != null) {
				result = backGround.getHeight() + getPaddingTop()
						+ getPaddingBottom();
			}

			if (model == MeasureSpec.AT_MOST) {
				result = Math.min(result, size);
			}
		}
		return result;
	}
	
	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		// TODO Auto-generated method stub
		super.onLayout(changed, left, top, right, bottom);
		
	}
	
	
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		this.setBackgroundResource(R.color.red);
		canvas.save();
		canvas.translate(15, 0);
		
		canvas.drawBitmap(backGround, 0, 0, new Paint());
		canvas.restore();
	}
	
	
}

解析View.MeasureSpec类

android.view.View.MeasureSpec

MeasureSpec对象,封装了layout规格说明,并且从父view传递给子view。每个MeasureSpec对象代表了width或height的规格。

MeasureSpec对象包含一个size和一个mode,其中mode可以取以下三个数值之一:

    UNSPECIFIED,1073741824 [0x40000000],未加规定的,表示没有给子view添加任何规定。
    EXACTLY,0 [0x0],精确的,表示父view为子view确定精确的尺寸。

    AT_MOST,-2147483648 [0x80000000],子view可以在指定的尺寸内尽量大。


view在测量显示的时候是受到父View的限制的,如果父View的宽高设置为wrap_content,那么就对子View的显示没有什么限制,可以让子View按照最大的尺寸显示,如果父View的尺寸限定了,那么子View的显示的大小一定不会超过父view,如果超过的话会被截掉。

MeasureSpec,是从父view传给子View的,所以MeasureSpec中规定了子View显示的最大区域。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    
    android:layout_height="wrap_content" >
    <com.fy.communityapp.view.MyTestView
        android:layout_width="fill_parent"
        android:layout_centerInParent="true"
        android:layout_height="fill_parent"
        android:paddingLeft="10dp"
        android:visibility="gone"
        android:paddingRight="20dp"
        >
       </com.fy.communityapp.view.MyTestView>
</RelativeLayout>

如果我们给RelativeLayout设定宽高为 match-parent或者wrap_content,那么MyTestView的宽显示的最大区域都为整个屏幕的宽度(高度同理),如过 宽度设定为一个固定的值的话,那么子View       MyTestView   显示的最大宽度也为这个值。

int model = MeasureSpec.getMode(heightMeasureSpec);
int size = MeasureSpec.getSize(heightMeasureSpec);
而我们在计算的时候 这个size就是RelativeLayout测量出的MyTestView显示的最大宽度,这个最大宽度是不会超过父View的宽度的


例如:
if (model == MeasureSpec.EXACTLY) {
			result = size;
		} else {

			if (backGround != null) {
				result = backGround.getHeight() + getPaddingTop()
						+ getPaddingBottom();
			}

			if (model == MeasureSpec.AT_MOST) {
				result = Math.min(result, size);
			}
		}
如果你给MyTestView设定的固定的值比父view设定的固定的值要大,那么MyTestView最多显示父View宽度的空间,如果设定的固定的值比父view设定的固定的值要小,那么显示的是MyTestView设定的值。
如果 MyTestView设定的宽为match-parent的话,那么显示的也是屏幕的宽度,如果设定的是wrap_content,那么MyTestView所能显示的最大宽度为屏幕宽,但是显示的是MyTestView的所能显示的宽度就是以下计算结果
if (backGround != null) {
				result = backGround.getHeight() + getPaddingTop()
						+ getPaddingBottom();
			}

			if (model == MeasureSpec.AT_MOST) {
				result = Math.min(result, size);
			}

得到的值,肯定是比size要小的。

那么这个MyTestView的所能显示的宽度,是如何计算的呢?
包括你显示的Text或者图片的宽度+paddingLeft+paddingRight 
就是MyTestView所能显示的宽度了。
2、onDraw()
如果我们自定义的控件中画一个图

如果我们给MyTestView设定了属性Padding的话,就像我们代码中一样,paddingleft是10,paddingRight是20,那么我们如果直接用
protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		this.setBackgroundResource(R.color.red);
		canvas.drawBitmap(backGround, 0, 0, new Paint());
		
	}
那么显示的时候宽度虽然算上paddingleft和paddingRight了但是,图像是在画的时候,是在最左边画起的如图


右边红色的宽度就是paddingLeft+paddingRight

如果想要我们的左填充起到应有的效果的话,那么我们就移动canvas   15是10dp转换后的px值
protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		this.setBackgroundResource(R.color.red);
		canvas.save();
		canvas.translate(15, 0);
		
		canvas.drawBitmap(backGround, 0, 0, new Paint());
		canvas.restore();
	}
这样就得到我们想要的结果了,左边padding10dp,右边padding20dp


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值