安卓开发自定义控件的那些事

前两天改一个自定义翻页滑动控件DefinedScrollView,为了解决界面滑动冲突问题,当时使用场景是这样的,本来设计的页面是分作第一页和第二页,滑动翻页,当时直接就用这个控件了,但是后来发现页面内容太多,部分手机上第一页根本显示不全,为了赶着上线我在第一页里面放入了scroolview 但是这样造成了滑动冲突问题,我监听了scroolview的事件强制传递给了父控件,用简陋的方法解决了滑动冲突将app上线了,但是这样毕竟体验太差,所以还是得在控件上面做。改这个控件改了半天,基本实现了我要的功能,在这里记录相关的一些坑。

自定义控件的执行会先执行onMeasure方法,再执行onLayout方法。onMeasure中做布局的测量,一般使用方法传入的参数就可以了。下面是之前原作者的写法

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		// ///final int width = MeasureSpec.getSize(widthMeasureSpec);
		final int height = MeasureSpec.getSize(heightMeasureSpec);
		final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
//		if (widthMode != MeasureSpec.EXACTLY) {
//			throw new IllegalStateException(
//					"ScrollLayout only canmCurScreen run at EXACTLY mode!");
//		}

		
		final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//		if (heightMode != MeasureSpec.EXACTLY) {
//			throw new IllegalStateException(
//					"ScrollLayout only can run at EXACTLY mode!");
//		}

		// The children are given the same width and height as the scrollLayout
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
		}
		scrollTo(0, mCurrentScreen * height);
	}
其中他采用的也是我们使用的方法,这样默认测量出来子布局和父布局宽高显示是一样的,如果我们将传入的heightMeasureSpec改为0,就是精确测量高度,在onLayout下我们就能获得测量出的高度,做我们想要的处理。

onLayout是控制布局显示,当我们调用measure后,就会触发onMeasure和onLayout(onMeasure中调用子view的测量不会),如果在onLayout调用会导致无限循环!

子view通过layout方法控制显示的区域,注意宽度如果采用精确测量,子view采用match_parent,不论layout宽度设置多大都是无法铺满全屏,这是一个大坑!

下面附上原作者写的onLayout

@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		// /int childLeft = 0;
		int childTop = 0;
		int childCount = getChildCount();
		for (int i = 0; i < childCount; i++) {
			final View childView = getChildAt(i);
			if (childView.getVisibility() != View.GONE) {
				// //final int childWidth = childView.getMeasuredWidth();
				final int childHeight = childView.getMeasuredHeight();
				/*
				 * childView.layout(childLeft, 0, childLeft + childWidth,
				 * childView.getMeasuredHeight());
				 */
				childView.layout(0, childTop, childView.getMeasuredHeight(),
						childTop + childHeight);
				childTop += childHeight;
				// /childLeft += childWidth;
			}
		}
	}

安卓中,LinearLayout、FrameLayout、RelativeLayout等布局都继承自ViewGroup,因此用ViewGroup做instanceof的话这几种类型的View都会通过判断。


自定义View有的构造函数重载有三个,建议这样写

public View(Context context) {
		this(context, null);
	}

	public View(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public View(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
}
在最后一个重载里面做初始化操作

自定义View中可以通过AttributeSet获取一些自定义属性,通过attr/attrs.xml里面添加属性

例如

<declare-styleable name="RoundProgressBar">
        <attr name="roundColor" format="color" />
        <attr name="roundProgressColor" format="color" />
        <attr name="roundWidth" format="dimension"></attr>
        <attr name="textColor" format="color" />
        <attr name="textSize" format="dimension" />
        <attr name="max" format="integer"></attr>
        <attr name="textIsDisplayable" format="boolean"></attr>
        <attr name="style">
            <enum name="STROKE" value="0"></enum>
            <enum name="FILL" value="1"></enum>
        </attr>
        <attr name="startText" format="string" />
        <attr name="overText" format="string" />
    </declare-styleable>
然后再构造函数中:

	public RoundChartView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		paint = new Paint();

		TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
				R.styleable.RoundProgressBar);

		// 获取自定义属性和默认值
		roundColor = mTypedArray.getColor(
				R.styleable.RoundProgressBar_roundColor, Color.RED);
		roundProgressColor = mTypedArray.getColor(
				R.styleable.RoundProgressBar_roundProgressColor, Color.GREEN);
		textColor = mTypedArray.getColor(
				R.styleable.RoundProgressBar_textColor, Color.GREEN);
		textSize = mTypedArray.getDimension(
				R.styleable.RoundProgressBar_textSize, 15);
		roundWidth = mTypedArray.getDimension(
				R.styleable.RoundProgressBar_roundWidth, 5);
		max = mTypedArray.getInteger(R.styleable.RoundProgressBar_max, 100);
		textIsDisplayable = mTypedArray.getBoolean(
				R.styleable.RoundProgressBar_textIsDisplayable, true);
		style = mTypedArray.getInt(R.styleable.RoundProgressBar_style, 0);
		starttext = mTypedArray
				.getString(R.styleable.RoundProgressBar_startText);
		overtext = mTypedArray.getString(R.styleable.RoundProgressBar_overText);
		mTypedArray.recycle();

		items = new ArrayList<RoundChartView.ChartItem>();
		tempitems = new ArrayList<RoundChartView.ChartItem>();
	}
再在布局最外层加入

 xmlns:app="http://schemas.android.com/apk/res/包名"
布局中即可使用

<包名路径.RoundProgressBar
            android:id="@+id/roundProgressBar"
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="10dp"
            app:overText="已投满"
            app:roundColor="#d2f1ff"
            app:roundProgressColor="#42c3ff"
            app:roundWidth="4dp"
            app:textColor="#42c3ff"
            app:textSize="12dp" />









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值