您的位置:首页 > 移动开发 > Android开发

Android自定义viewgroup实现自定义布局

2017-08-14 16:12 465 查看
有时候为了实现自己业务相关的一些操作或者一些布局,用Android自身的布局方式去做的话,代码控制比较多,或者维护很麻烦,在这种情况下就要根据自己的需求定义一个否和自己业务需求的layout。首先肯定 的是,要继承viewgroup,然后重写里面的重要方法,不说了直接上代码把

public class MyLayout extends ViewGroup {
private static final String TAG = "MyLayout";

private int lineCount;//最大显示行数,0表示不限制行数
private int lineChildCount;//每行最大显示个数
private int lineGap;//行间距
private int childViewGap;//view间距

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

public MyLayout(Context context) {
super(context);
}

public MyLayout(Context context, AttributeSet attrs) {
super(context, attrs);

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyLayout);

lineCount = typedArray.getInteger(R.styleable.MyLayout_lineCount, 1);
lineChildCount = typedArray.getInteger(R.styleable.MyLayout_lineChildCount, 3);
lineGap = (int) typedArray.getDimension(R.styleable.MyLayout_lineGap, 50);
childViewGap = (int) typedArray.getDimension(R.styleable.MyLayout_childViewGap, 50);

typedArray.recycle();
}

public void setLineCount(int lineCount) {
this.lineCount = lineCount;

}

public int getLineCount() {
return lineCount;
}

public void setLineChildCount(int lineChildCount) {
this.lineChildCount = lineChildCount;
this.notify();
}

public int getLineChildCount() {
return lineChildCount;
}

public void setLineGap(int lineGap) {
this.lineGap = lineGap;
this.invalidate();
}

public int getLineGap() {
return lineGap;
}

public int getChildViewGap() {
return childViewGap;
}

public void setChildViewGap(int childViewGap) {
this.childViewGap = childViewGap;
}

/**
* 计算所有ChildView的宽度和高度 然后根据ChildView的计算结果,设置自己的宽和高
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
*/
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);

int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);

// 计算出所有的childView的宽和高
measureChildren(widthMeasureSpec, heightMeasureSpec);
/**
* 记录如果是wrap_content是设置的宽和高,那么就需要将mylayout的宽和高计算出来才行
* 现在项目中的宽肯定是match_parent,高度是wrap,因此需要计算高度值
*/

int childCount = getChildCount();
int a = childCount / lineChildCount;
int b = childCount % lineChildCount;
int trueLineCount = b == 0 ? a : a + 1;//真正显示几行,linecount是最大限制并不是真正显示的行数
if (lineCount!=0){
if (trueLineCount>lineCount){
trueLineCount=lineCount;
}
}

int height;
if (childCount == 0) {
height = 0;
} else {
int childHeight = getChildAt(0).getMeasuredHeight();
height = childHeight * trueLineCount + lineGap * (trueLineCount - 1);
}

/**
* 如果是wrap_content设置为我们计算的值
* 否则:直接设置为父容器计算的值
*/
//        System.out.println("width: "+sizeWidth+"   height: "+sizeHeight);
setMeasuredDimension(sizeWidth, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight : height);
}

// abstract method in viewgroup
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {这个方法在之前的myview  自定义view中也出现了,自定义view中这个方法是绘制自已
想要的view的形状,但是这个layout就不一样了,这里是在确定layout下的控件或者layout下的子view的位置,你想让你的子view怎么摆放你就怎么放。

if (getChildCount() != 0) {
int cWidth = getChildAt(0).getMeasuredWidth();
int cHeight = getChildAt(0).getMeasuredHeight();

int width = getWidth();
int height = getHeight();
int margin = (width - lineChildCount * cWidth - (lineChildCount - 1) * childViewGap) / 2;

for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);

int atLine = i / lineChildCount;
int lineIndex = i % lineChildCount;
int cl = 0, ct = 0, cr = 0, cb = 0;

cl = margin + lineIndex * cWidth + childViewGap * lineIndex;
ct = lineGap * atLine + atLine * cHeight;
cr = cl + cWidth;
cb = ct + cHeight;
childView.setVisibility(VISIBLE);
childView.layout(cl, ct, cr, cb);
}
}
}

@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}

@Override
protected LayoutParams generateDefaultLayoutParams() {
Log.e(TAG, "generateDefaultLayoutParams");
return new MarginLayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
}

@Override
protected LayoutParams generateLayoutParams(
LayoutParams p) {
Log.e(TAG, "generateLayoutParams p");
return new MarginLayoutParams(p);
}

/*
* if (heightMode == MeasureSpec.UNSPECIFIED)
{
int tmpHeight = 0 ;
LayoutParams lp = getLayoutParams();
if (lp.height == LayoutParams.MATCH_PARENT)
{
Rect outRect = new Rect();
getWindowVisibleDisplayFrame(outRect);
tmpHeight = outRect.height();
}else
{
tmpHeight = getLayoutParams().height ;
}
height = Math.max(height, tmpHeight);

}
*/
}

定义属性,在values目录下的attrs文件下定义:

<declare-styleable name="MyLayout">
<attr name="lineCount" format="integer" />
<attr name="lineChildCount" format="integer" />
<attr name="lineGap" format="dimension" />
<attr name="childViewGap" format="dimension" />
</declare-styleable>

然后在xml中就可以直接使用了:
<com.example.srcb04178.vrplayer.ui.MyLayout
android:id="@+id/liveWraper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:childViewGap="30dp"
app:lineChildCount="3"
app:lineCount="1"
app:lineGap="30dp">
<!--views-->

</com.example.srcb04178.vrplayer.ui.MyLayout>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android开发