自定义控件那些事儿 ----- 四【布局】

来源:互联网 发布:java字符串前后去空格 编辑:程序博客网 时间:2024/06/06 14:06

一、onLayout布局出洞


在上一篇文章自定义控件那些事儿 ------ 三【量测】中,为实现图片和文字组合控件的正确显示,就已经引入了onLayout实现的整体过程。

onLayout具体实现中,主要执行在layout()方法上。分别为left,top,right,bottom。实则是确定了当前控件的左上角和右下角,从而确定了当前控件展示的区域。所以,对一个View强制使用layout()方法,可以修改当前view所在的位置。在之后的属性动画实现中,就应用了这一特点。

达到理解整个布局过程作用,重写ViewGroup作为布局文件,其中内部子View均采用已有控件。从而避免较多的实现onMeasure()方法,导致整个过程的复杂。


二、初步实现


1,布局控件ViewGroup的实现


public class LayoutView extends ViewGroup {    public LayoutView(Context context) {        super(context);    }    public LayoutView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public LayoutView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onLayout(boolean b, int left, int top, int right, int bottom) {        int tempHeight = 0;        int mViewGroupWidth = getMeasuredWidth();  //当前ViewGroup的总宽度        int mPainterPosX = left;  //当前绘图光标横坐标位置        int mPainterPosY = top;  //当前绘图光标纵坐标位置        int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            View childView = getChildAt(i);            int width = childView.getMeasuredWidth();            int height = childView.getMeasuredHeight();            if (height > tempHeight) {                tempHeight = height;            }            //如果剩余的空间不够,则移到下一行开始位置            if (mPainterPosX + width > mViewGroupWidth) {                mPainterPosX = left;                mPainterPosY += tempHeight;            }            //执行ChildView的绘制            childView.layout(mPainterPosX, mPainterPosY, mPainterPosX + width, mPainterPosY + height);            //记录当前已经绘制到的横坐标位置            mPainterPosX += width;        }    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        measureChildren(widthMeasureSpec, heightMeasureSpec);    }}

模仿LinearLayout的实现,做一个内部包含多个控件的控件。从左向右开始排列。当行距不够时,切换下一行显示。
注意点:
在类中使用tempHeight变量,用于记录当前行View的最高高度。换行以后的View只能在这个View之下,防止View之间的叠加。在换行中,要将tempHeight归零,第二行的高度不再以第一行的高度为最高值。
布局中使用:


<?xml version="1.0" encoding="utf-8"?><com.future.layoutdemo.view.LayoutView xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.future.layoutdemo.MainActivity">    <View        android:layout_width="50dp"        android:layout_height="50dp"        android:background="@color/colorAccent" />    <View        android:layout_width="180dp"        android:layout_height="130dp"        android:background="@color/colorPrimaryDark" />    <View        android:layout_width="30dp"        android:layout_height="80dp"        android:background="@color/colorPrimary" />    <View        android:layout_width="100dp"        android:layout_height="100dp"        android:background="@color/colorAccent" />    <View        android:layout_width="200dp"        android:layout_height="100dp"        android:background="@color/colorPrimary" /></com.future.layoutdemo.view.LayoutView>

展示效果:



三、内容完善


在以上的实现中,添加margin值是没有效果的,当然不符合使用要求了!


1,修改控件实现,添加布局LayoutParams
在布局中优化布局计算,添加margin值影响。


public class Layout2View extends ViewGroup {    public Layout2View(Context context) {        super(context);    }    public Layout2View(Context context, AttributeSet attrs) {        super(context, attrs);    }    public Layout2View(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    protected void onLayout(boolean b, int left, int top, int right, int bottom) {        int tempHeight = 0;        int mViewGroupWidth = getMeasuredWidth();  //当前ViewGroup的总宽度        int mViewGroupHeight = getMeasuredHeight(); //当前ViewGroup的总高度        int mPainterPosX = left; //当前绘图光标横坐标位置        int mPainterPosY = top;  //当前绘图光标纵坐标位置        int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            View childView = getChildAt(i);            int width = childView.getMeasuredWidth();            int height = childView.getMeasuredHeight();            if (height > tempHeight) {                tempHeight = height;            }            Layout2View.LayoutParams margins = (Layout2View.LayoutParams) (childView.getLayoutParams());            //ChildView占用的width  = width+leftMargin+rightMargin            //ChildView占用的height = height+topMargin+bottomMargin            //如果剩余的空间不够,则移到下一行开始位置            if (mPainterPosX + width + margins.leftMargin + margins.rightMargin > mViewGroupWidth) {                mPainterPosX = left;//                mPainterPosY += height + margins.topMargin + margins.bottomMargin;                mPainterPosY += tempHeight + margins.topMargin + margins.bottomMargin;            }            //执行ChildView的绘制            childView.layout(mPainterPosX + margins.leftMargin, mPainterPosY + margins.topMargin, mPainterPosX + margins.leftMargin + width, mPainterPosY + margins.topMargin + height);            mPainterPosX += width + margins.leftMargin + margins.rightMargin;        }    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        measureChildren(widthMeasureSpec, heightMeasureSpec);    }    public static class LayoutParams extends ViewGroup.MarginLayoutParams {        public LayoutParams(Context c, AttributeSet attrs) {            super(c, attrs);        }    }    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new Layout2View.LayoutParams(getContext(), attrs);    }}

2,使用修改


<?xml version="1.0" encoding="utf-8"?><com.future.layoutdemo.view.Layout2View xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.future.layoutdemo.MainActivity">    <View        android:layout_width="50dp"        android:layout_height="50dp"        android:layout_margin="20dp"        android:background="@color/colorAccent" />    <View        android:layout_width="180dp"        android:layout_height="130dp"        android:background="@color/colorPrimaryDark" />    <View        android:layout_width="30dp"        android:layout_height="80dp"        android:background="@color/colorPrimary" />    <View        android:layout_width="100dp"        android:layout_height="100dp"        android:layout_margin="10dp"        android:background="@color/colorAccent" />    <View        android:layout_width="200dp"        android:layout_height="100dp"        android:background="@color/colorPrimary" /></com.future.layoutdemo.view.Layout2View>



布局中添加margin值,展示效果如下:




源码传送门




慢慢的都会成长起来的,不管经历什么,不管有多少伤痛,你都能习惯的展示云淡风轻的微笑。
天塌下来,塞满食物的嘴里,再加一点鸡腿!



原创粉丝点击