LinearLayoutCompat源码简单分析
来源:互联网 发布:平面设计app软件 编辑:程序博客网 时间:2024/05/17 07:12
前言:伟大的foune说过,不看源码的安卓工程师不是一个优秀的程序猿。 今天,我将开始试水第一篇源码分析的博客,感谢各位那么帅、那么美,还来看我的博客。 目前水平有限,只是简单分析,大家参考着看看,如有纰漏,敬请指出。
一、应用示例
<android.support.v7.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"android:padding="20dp"app:divider="@drawable/abc_list_divider_mtrl_alpha"app:showDividers="beginning|middle|end"><TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="LinearLayoutCompat" /><TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="LinearLayoutCompat" /><TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="LinearLayoutCompat" /><TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="made by foune" /></android.support.v7.widget.LinearLayoutCompat>
xml文件很简单,只需将我们常用的LinearLayout换成LinearLayoutCompat,并且设置divider和showDividers两个属性,即可完成对子控件添加分割线。
divider:设置分割线的drawable图片
showDividers:设置分割线的展示位置,如示例图所示,beginning显示在第一个子控件的顶部,middle显示在子控件两两之间,end显示在最后一个子控件的底部。
附加说明:默认orientation为horizontal,分割线为竖直方向。
二、源码分析
public class LinearLayoutCompat extends ViewGroup
LinearLayoutCompat继承自ViewGroup,如果之前了解过自定义ViewGroup,那么我们就知道主要涉及onMeasure(测量自身和内部的所有子控件),onLayout(摆放内部所有的子控件),onDraw(绘制)这三个方法。
下面我们来一步步分析下近2000行代码中的关键方法。
注:以下代码均以orientaion为vertical举例,horizontal代码类似。
1.构造方法
public LinearLayoutCompat(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, R.styleable.LinearLayoutCompat, defStyleAttr, 0); 。。。。。。//此处省略 setDividerDrawable(a.getDrawable(R.styleable.LinearLayoutCompat_divider)); mShowDividers = a.getInt(R.styleable.LinearLayoutCompat_showDividers, SHOW_DIVIDER_NONE); mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayoutCompat_dividerPadding, 0); a.recycle();}
构造方法中主要关注setDiveiderDrawable(Drawable divider)这个方法,请往下看。
2.设置分割线图片
public void setDividerDrawable(Drawable divider) { if (divider == mDivider) { return; } mDivider = divider; if (divider != null) { mDividerWidth = divider.getIntrinsicWidth(); mDividerHeight = divider.getIntrinsicHeight(); } else { mDividerWidth = 0; mDividerHeight = 0; } setWillNotDraw(divider == null); requestLayout(); //重新测量摆放}
通过setDividerDrawable()方法我们可以设置分割线的图片,与xml中设置divider属性效果一致,不同的是通过java代码我们可以动态的设置分割线的图片。同理,setShowDividers()方法设置分割线的展现位置,与showDividers属性一致。setDividerPadding()类似。
3.onMeasure()
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mOrientation == VERTICAL) { measureVertical(widthMeasureSpec, heightMeasureSpec); } else { measureHorizontal(widthMeasureSpec, heightMeasureSpec); }}
见名知意,measureVertical()是orientation为vertical时的测量方法,这个方法代码较长,这里就不贴了,有兴趣的可以自行查看源码。我们不需要去详细理解其中的每一行代码,我们只要知道它通过遍历所有子控件测量每个子控件的width和height,我们设置的权重weight和边距padding等属性均参与了计算。
4.onLayout()
protected void onLayout(boolean changed, int l, int t, int r, int b) { if (mOrientation == VERTICAL) { layoutVertical(l, t, r, b); } else { layoutHorizontal(l, t, r, b); }}void layoutVertical(int left, int top, int right, int bottom) { final int paddingLeft = getPaddingLeft(); int childTop; int childLeft; // Where right end of child should go final int width = right - left; int childRight = width - getPaddingRight(); // Space available for child int childSpace = width - paddingLeft - getPaddingRight(); final int count = getVirtualChildCount(); final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; final int minorGravity = mGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK; switch (majorGravity) { case Gravity.BOTTOM: // mTotalLength contains the padding already childTop = getPaddingTop() + bottom - top - mTotalLength; break; // mTotalLength contains the padding already case Gravity.CENTER_VERTICAL: childTop = getPaddingTop() + (bottom - top - mTotalLength) / 2; break; case Gravity.TOP: default: childTop = getPaddingTop(); break; } for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); if (child == null) { childTop += measureNullChild(i); } else if (child.getVisibility() != GONE) { final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); final LinearLayoutCompat.LayoutParams lp = (LinearLayoutCompat.LayoutParams) child.getLayoutParams(); int gravity = lp.gravity; if (gravity < 0) { gravity = minorGravity; } final int layoutDirection = ViewCompat.getLayoutDirection(this); final int absoluteGravity = GravityCompat.getAbsoluteGravity(gravity, layoutDirection); switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childLeft = paddingLeft + ((childSpace - childWidth) / 2) + lp.leftMargin - lp.rightMargin; break; case Gravity.RIGHT: childLeft = childRight - childWidth - lp.rightMargin; break; case Gravity.LEFT: default: childLeft = paddingLeft + lp.leftMargin; break; } if (hasDividerBeforeChildAt(i)) { childTop += mDividerHeight; } childTop += lp.topMargin; setChildFrame(child, childLeft, childTop + getLocationOffset(child), childWidth, childHeight); childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); i += getChildrenSkipCount(child, i); } }}
同样的,layoutVertical()是orientation为vertical时摆放子控件的方法,并且权重和边距参与计算。
5.onDraw()
protected void onDraw(Canvas canvas) { if (mDivider == null) { return; } if (mOrientation == VERTICAL) { drawDividersVertical(canvas); } else { drawDividersHorizontal(canvas); }}
只有当我们设置了divider属性或者通过java代码setDividerDrawable(),才会绘制分割线;当orientation为vertical时,调用drawDividersVertical()方法中 的drawHorizontalDivider()方法绘制水平方向的分割线。
void drawDividersVertical(Canvas canvas) { final int count = getVirtualChildCount(); for (int i = 0; i < count; i++) { final View child = getVirtualChildAt(i); if (child != null && child.getVisibility() != GONE) { if (hasDividerBeforeChildAt(i)) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int top = child.getTop() - lp.topMargin - mDividerHeight; drawHorizontalDivider(canvas, top); } } } if (hasDividerBeforeChildAt(count)) { final View child = getVirtualChildAt(count - 1); int bottom = 0; if (child == null) { bottom = getHeight() - getPaddingBottom() - mDividerHeight; } else { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); bottom = child.getBottom() + lp.bottomMargin; } drawHorizontalDivider(canvas, bottom); }}
drawDividersVertical()绘制先遍历子控件并通过hasDividerBeforeChildAt()方法判断是否需要绘制分割线。
protected boolean hasDividerBeforeChildAt(int childIndex) { if (childIndex == 0) { return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0; } else if (childIndex == getChildCount()) { return (mShowDividers & SHOW_DIVIDER_END) != 0; } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) { boolean hasVisibleViewBefore = false; for (int i = childIndex - 1; i >= 0; i--) { if (getChildAt(i).getVisibility() != GONE) { hasVisibleViewBefore = true; break; } } return hasVisibleViewBefore; } return false;}
这里的mShowDividers属性就是我们在xml中设置的showDividers属性或是java代码setShowDividers()方法设置的”beginning|middle|end”三种属性。
如果hasDividerBeforeChildAt()方法结果为true,那么就调用drawHorizontalDivider()方法绘制水平的分割线。
void drawHorizontalDivider(Canvas canvas, int top) { mDivider.setBounds(getPaddingLeft() + mDividerPadding, top, getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight); mDivider.draw(canvas);}
三、总结
本篇讲解的是google官方的自定义VieGroup,主要关注onMeasure()、onLayout()及onDraw()三个方法,后面一段时间的博客我将以自定义View和ViewGroup为主题,有机会,我会写一些好看好玩儿的自定义View,欢迎各位帅哥美女前来围观。
- LinearLayoutCompat源码简单分析
- LinearLayoutCompat分割线效果源码分析
- LinearLayoutCompat的源码分析分割线的具体实现
- LinearLayoutCompat源码浅析
- LinearLayoutCompat源码浅析
- LinearLayoutCompat
- 分析v7中的LinearLayoutCompat类
- PostgreSQL源码简单分析
- htmlparser源码简单分析
- 传奇源码简单分析
- ASIHTTPRequest源码简单分析
- jQuery源码简单分析
- MediaPlayer源码简单分析
- FragmentManager源码简单分析
- ThreadLocal源码简单分析
- Hessian 源码简单分析
- Retrofit 源码简单分析
- LinearLayoutCompat组件
- 测试
- 设计模式之代理模式
- Android数据库的学习(一)
- UESToj 64 CD Making ( 水题
- 排序算法(一):插入排序与堆排序
- LinearLayoutCompat源码简单分析
- linux下ftp的配置及使用
- java面试题-设计模式
- 状态机思维
- 【滴滴出行2017春招研发工程师笔试题】俄罗斯套娃
- OGRE学习系列四:基础教程2 灯光,相机和阴影
- 作业1:诗歌欣赏
- 触发器 对商品订单管理 简单分析
- jsp页面input标签中时间日期回显的格式问题&在input标签中指定时间的格式