@guhuizaifeiyang
2018-06-23T03:15:06.000000Z
字数 3278
阅读 1345
Android自定义控件
自定义View系列 谷歌的小弟博客
让你的app提升一个档次-Android酷炫自定义控件
Android 自定义 View 合集 文章合集
自定义控件三部曲之视图篇 启舰的博客
自定义View系列 从零起步,比较系统的自定义View教程。
This class represents the basic building block for user interface components. A View occupies a rectangular area on the screen and is responsible for drawing and event handling.
简而言之,View就是屏幕上一块用来和用户进行交互的矩形区域,View需要进行绘制和处理事件。
共有4个,具体如下:
自定义View必须重写至少一个构造函数:// 如果View是在Java代码里面new的,则调用第一个构造函数public CarsonView(Context context) {super(context);}// 如果View是在.xml里声明的,则调用第二个构造函数// 自定义属性是从AttributeSet参数传进来的public CarsonView(Context context, AttributeSet attrs) {super(context, attrs);}// 不会自动调用// 一般是在第二个构造函数里主动调用// 如View有style属性时public CarsonView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}//API21之后才使用// 不会自动调用// 一般是在第二个构造函数里主动调用// 如View有style属性时public CarsonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}

event.getX(); //触摸点相对于其所在组件坐标系的坐标event.getY();event.getRawX(); //触摸点相对于屏幕默认坐标系的坐标event.getRawY();

大部分情况二者的值是一样的,但需要注意的是:
如果要自定义ViewGroup支持子控件的layout_margin参数,则自定义的ViewGroup类必须重载generateLayoutParams()函数,并且在该函数中返回一个ViewGroup.MarginLayoutParams派生类对象,这样才能使用margin参数。
实例
效果图

@Overrideprotected LayoutParams generateLayoutParams(LayoutParams p) {return new MarginLayoutParams(p);}@Overridepublic LayoutParams generateLayoutParams(AttributeSet attrs) {return new MarginLayoutParams(getContext(), attrs);}@Overrideprotected LayoutParams generateDefaultLayoutParams() {return new MarginLayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);}
需要将margin也计算进去
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int measureWidth = MeasureSpec.getSize(widthMeasureSpec);int measureHeight = MeasureSpec.getSize(heightMeasureSpec);int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);int lineWidth = 0;int lineHeight = 0;int height = 0;int width = 0;int count = getChildCount();for (int i=0;i<count;i++){View child = getChildAt(i);measureChild(child,widthMeasureSpec,heightMeasureSpec);MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();int childWidth = child.getMeasuredWidth() + lp.leftMargin +lp.rightMargin;int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;if (lineWidth + childWidth > measureWidth){//需要换行width = Math.max(lineWidth,width);height += lineHeight;//因为由于盛不下当前控件,而将此控件调到下一行,所以将此控件的高度和宽度初始化给lineHeight、lineWidthlineHeight = childHeight;lineWidth = childWidth;}else{// 否则累加值lineWidth,lineHeight取最大高度lineHeight = Math.max(lineHeight,childHeight);lineWidth += childWidth;}//最后一行是不会超出width范围的,所以要单独处理if (i == count -1){height += lineHeight;width = Math.max(width,lineWidth);}}//当属性是MeasureSpec.EXACTLY时,那么它的高度就是确定的,// 只有当是wrap_content时,根据内部控件的大小来确定它的大小时,大小是不确定的,属性是AT_MOST,此时,就需要我们自己计算它的应当的大小,并设置进去setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY) ? measureWidth: width, (measureHeightMode == MeasureSpec.EXACTLY) ? measureHeight: height);}
需要将margin加到控件里