闲来无事自定义个viewGroup的控件来练练手。
比如说现在有这么个需求,一左一右分别有个textView,然后外面一个控件直接包裹这两个;
1:现在给这个自定义控件(本文中名叫RcLinearLayout)自定义一个属性,然后去通过这个属性去确定这两个textView是否需要处于同一水平线上。
①先建立values文件夹下的attrs.xml文件,如:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="c_view">
<attr name="gravity">
<enum name="CENTER_VERTICAL" value="0"/>
</attr>
</declare-styleable>
</resources>
当然了,你还可以增加别的属性,然后根据你设的属性去确定child的位置
2.重要的地方来了,就是自定义控件了,主要是重写onMeasure方法和onLayout方法,onMeasure方法主要是测量出容器的宽与高,而onlayout方法则是确定child的位置,代码如下:
public class RcLinearLayout extends ViewGroup{
private int mGravity;//这个用来标记布局中是否设置了垂直居中的属性
public RcLinearLayout(Context context) {
this(context,null);
}
public RcLinearLayout(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public RcLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.c_view);
mGravity = ta.getInt(R.styleable.c_view_gravity,-1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//获得此控件的宽和高,以及计算模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//计算所有child的高和宽
measureChildren(widthMeasureSpec,heightMeasureSpec);
//记录如果是wrap_content是设置的宽和高
int width = 0;
int height = 0;
int cCount = getChildCount();
int cWidth = 0;
int cHeight = 0;
MarginLayoutParams cParams = null;
// 用于计算左边的childView的高度
int lHeight = 0;
// 用于计算右边的childView的高度,最终高度取二者之间大值
int rHeight = 0;
// 用于计算两个childView的宽度
int tWidth = 0;
/**
* 根据childView计算的出的宽和高,以及设置的margin计算容器的宽和高,主要用于容器是warp_content时
*/
for (int i = 0; i < cCount; i++) {
View childView = getChildAt(i);
cWidth = childView.getMeasuredWidth();
cHeight = childView.getMeasuredHeight();
cParams = (MarginLayoutParams) childView.getLayoutParams();
// 得出两个childView的宽
tWidth += cWidth + cParams.leftMargin + cParams.rightMargin;
if(i==0) {
lHeight += cHeight + cParams.topMargin + cParams.bottomMargin;
}else {
rHeight +=cHeight + cParams.topMargin +cParams.bottomMargin;
}
}
//高取两个childView的取大值
height = Math.max(lHeight,rHeight);
width = tWidth;
/**
* 如果是wrap_content设置为我们计算的值
* 否则:直接设置为父容器计算的值
*/
setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth : width,
(heightMode == MeasureSpec.EXACTLY) ? sizeHeight : height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int cCount = getChildCount();
int cWidth = 0;
int cHeight = 0;
MarginLayoutParams cParams = null;//这个用来获取child的参数
//遍历所有childView根据其宽和高,以及margin进行布局
for (int i = 0; i < cCount; i++){
View childView = getChildAt(i);
cWidth = childView.getMeasuredWidth();
cHeight = childView.getMeasuredHeight();
cParams = (MarginLayoutParams) childView.getLayoutParams();
int cl = 0, ct = 0, cr = 0, cb = 0;
if(mGravity==-1) {
switch (i) {
case 0://第一个childview
cl = cParams.leftMargin + getPaddingLeft();
ct = cParams.topMargin + getPaddingTop();
break;
case 1://第二个childview
cl = getWidth() - cWidth - cParams.leftMargin - cParams.rightMargin - getPaddingRight();
ct = cParams.topMargin + getPaddingTop();
break;
}
}else
switch (i){
case 0:
cl = cParams.leftMargin + getPaddingLeft();
ct = getHeight()/2-cHeight/2;
break;
case 1:
cl = getWidth() - cWidth - cParams.leftMargin - cParams.rightMargin - getPaddingRight();
ct = getHeight()/2-cHeight/2;
break;
}
cr = cl + cWidth;
cb = cHeight + ct;
childView.layout(cl, ct, cr, cb);
}
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
}
3.接下来是在布局文件中使用了
<?xml version="1.0" encoding="utf-8"?>
<com.example.administrator.CustomView.widget.RcLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:c_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#123456"
c_view:gravity="CENTER_VERTICAL">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="昵称" />
<TextView
android:id="@+id/id_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="赵四"
android:textSize="24sp" />
</com.example.administrator.CustomView.widget.RcLinearLayout>
嗯,基本上就这么多了。