自定义ViewGroup
2016-12-27 00:00
330 查看
关于View的基本知识和如何自定义View请见
自定义View - https://my.oschina.net/u/3026396/blog/812942
View需要根据ViewGroup传入的测量值和模式,对自己宽高进行确定(onMeasure中完成),然后在onDraw中完成对自己的绘制。
ViewGroup需要给View传入view的测量值和模式(onMeasure中完成),而且对于此ViewGroup的父布局,自己也需要在onMeasure中完成对自己宽和高的确定。此外,需要在onLayout中完成对其childView的位置的指定。
http://blog.csdn.net/u011733020/article/details/50849475
帮你搞定Android自定义View
http://www.jianshu.com/p/84cee705b0d3
Android手把手教您自定义ViewGroup
http://blog.csdn.net/lmj623565791/article/details/38339817/
为什么自定义ViewGroup ondraw方法不会被调用
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1014/1765.html
关于getMeasuredHeight和getHeight区别(getMeasureHeight是xml或代码中制定的测量高度,setMeasuredHeight; getHeight是实际显示出来的高度,通过view.layout()方便可以改变其值)
http://blog.csdn.net/qq_29951983/article/details/50571840
自定义View - https://my.oschina.net/u/3026396/blog/812942
ViewGroup的职责是啥?
ViewGroup相当于一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性,都是用于告诉容器的),我们的宽度layout_width、高度layout_height、对齐方式layout_gravity、内边距layout_maring等。所以ViewGroup的智能为:给childView计算出建议的宽和高的测量模式;决定childView的位置。(因为childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高,所以这里给出的建议宽和高)View的职责是啥?
View的职责就是根据测量模式和ViewGroup给出的建议的宽和高,计算出自己的宽和高;同事还有个更重要的职责:在ViewGroup为其指定的区域内绘制自己的形态。ViewGroup和LayoutParams之间的关系?
每个ViewGroup需要指定一个LayoutParams,用于确定它的childView支持哪些属性。不同的ViewGroup指定的LayoutParams是不同的,包含的属性也不尽相同。View的3种测量模式
EXACTLY、AT_MOST、UNSPECIFIED从API角度进行浅析
上面叙述了ViewGroup和View的职责,下面从API角度进行浅析。View需要根据ViewGroup传入的测量值和模式,对自己宽高进行确定(onMeasure中完成),然后在onDraw中完成对自己的绘制。
ViewGroup需要给View传入view的测量值和模式(onMeasure中完成),而且对于此ViewGroup的父布局,自己也需要在onMeasure中完成对自己宽和高的确定。此外,需要在onLayout中完成对其childView的位置的指定。
Demo
定义一个ViewGroup,内部可以传入0到4个childView,分别依次显示在左上角、右上角、左下角、右下角。同时在中央绘制一个圆。/** * Created by xupeng on 16/12/26. */ public class MyCustomViewGroup extends ViewGroup { private Paint mPaint; public MyCustomViewGroup(Context context) { super(context); init(); } public MyCustomViewGroup(Context context, AttributeSet attrs) { // super(context, attrs); this(context); } private void init() { setWillNotDraw(false); mPaint = new Paint(); mPaint.setColor(Color.RED); } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new MarginLayoutParams(getContext(), attrs); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { /** * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式 */ int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); // 计算出所有的children的宽和高 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的宽度,最重宽度取两者之间大值 int bWidth = 0; /** * 根据childView计算得出宽和高,以及设置的margin计算容器的宽和高,主要用于容器是wrap_content时 */ for (int i = 0; i < cCount; i++) { View childView = getChildAt(i); cWidth = childView.getMeasuredWidth(); cHeight = childView.getMeasuredHeight(); cParams = (MarginLayoutParams) childView.getLayoutParams(); // 上面两个childView if (i == 0 || i == 1) { tWidth += cWidth + cParams.leftMargin + cParams.rightMargin; } // 下面两个childView if (i == 2 || i == 3) { bWidth += cWidth + cParams.leftMargin + cParams.rightMargin; } if (i == 0 || i == 2) { lHeight += cHeight + cParams.topMargin + cParams.bottomMargin; } if (i == 1 || i == 3) { rHeight += cHeight + cParams.topMargin + cParams.bottomMargin; } } width = Math.max(tWidth, bWidth); height = Math.max(lHeight, rHeight); setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? widthSize : width, (heightMode == MeasureSpec.EXACTLY) ? heightSize : height); // super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @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; /** * 遍历所有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, cb = 0, cr = 0; switch (i) { case 0: cl = cParams.leftMargin; ct = cParams.topMargin; break; case 1: cl = getWidth() - cWidth - cParams.rightMargin; ct = cParams.topMargin; break; case 2: cl = cParams.leftMargin; ct = getHeight() - cHeight - cParams.bottomMargin; break; case 3: cl = getWidth() - cWidth - cParams.rightMargin; ct = getHeight() - cHeight - cParams.bottomMargin; break; } cr = cl + cWidth; cb = cHeight + ct; childView.layout(cl, ct, cr, cb); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = getWidth(); int measureWidth = getMeasuredWidth(); requestLayout(); int measureWidthAndState = getMeasuredWidthAndState(); canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, 100 , mPaint); // invalidate(); }
<?xml version="1.0" encoding="utf-8"?> <com.gnepux.sdkusage.widget.MyCustomViewGroup xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_custom_view_group" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffff00" tools:context="com.gnepux.sdkusage.activity.CustomViewGroupActivity"> <com.gnepux.sdkusage.widget.MyCustomView android:layout_width="50dp" android:layout_height="50dp" android:text="1" android:gravity="center" android:textColor="#ffffff" android:background="#ff0000"/> <com.gnepux.sdkusage.widget.MyCustomView android:layout_width="50dp" android:layout_height="50dp" android:text="2" android:gravity="center" android:textColor="#ffffff" android:background="#00ff00"/> <com.gnepux.sdkusage.widget.MyCustomView android:layout_width="50dp" android:layout_height="50dp" android:text="3" android:gravity="center" android:textColor="#ffffff" android:background="#0000ff"/> <com.gnepux.sdkusage.widget.MyCustomView android:layout_width="50dp" android:layout_height="50dp" android:text="4" android:gravity="center" android:textColor="#ffffff" android:background="#ff00ff"/> </com.gnepux.sdkusage.widget.MyCustomViewGroup>
参考链接:
View原理简介http://blog.csdn.net/u011733020/article/details/50849475
帮你搞定Android自定义View
http://www.jianshu.com/p/84cee705b0d3
Android手把手教您自定义ViewGroup
http://blog.csdn.net/lmj623565791/article/details/38339817/
为什么自定义ViewGroup ondraw方法不会被调用
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1014/1765.html
关于getMeasuredHeight和getHeight区别(getMeasureHeight是xml或代码中制定的测量高度,setMeasuredHeight; getHeight是实际显示出来的高度,通过view.layout()方便可以改变其值)
http://blog.csdn.net/qq_29951983/article/details/50571840
相关文章推荐
- 自定义View
- 自定义view
- 自定义View系列教程08--滑动冲突的产生及其处理
- 自定义view
- 自定义View
- 自定义View系列(6)--RatingBarView
- 自定义View
- Kotlin自定义View系列教程之标尺控件(选择身高、体重等)的实现
- 自定义view 添加动画的时候一定要注意
- 自定义View
- 自定义View(一)
- 自定义View 中很关键的问题View获取宽/高是0 的解决办法
- 自定义VIEW②绘制流程
- 安卓面试之=》自定义View
- 自定义view
- 自定义View
- 自定义View android 像支付宝支付界面的progress
- 自定义view
- 自定义View
- 自定义VIEW③Canvas