自定义View 入门

自定义View

attrs

  • values 文件夹中定义attr属性文件

    
        
    

  • attr的format取值类型( 属性定义时可以指定多种类型 )
typeName Description
reference 参考某一资源id
color 颜色值
flag 可用于位或运算
enum 枚举值
fraction 百分数
boolean 布尔值
dimension 尺寸值
float 浮点数
integer 整数
string 字符串
    
        
        
    
        
            
            
            
            
            
            
            
            
            
            
        
  • xml中使用自定义的attr属性值

    
    
  • 代码中获取自定义的attr属性值

    context.obtainStyledAttributes(attrs, R.styleable.CircleView)?.apply {
              paint.color = getColor(R.styleable.CircleView_color_circle, Color.GREEN)
              recycle()
          }
    

onMeasure

View只要对自身的MeasureSpec进行处理即可,而ViewGroup中的onMeasure除了对本身的
MeasureSpec要进行处理,还要调用ChildView的测量的相关方法,
如:
measureChildren(widthMeasureSpec, heightMeasureSpec)

  • measureSpecMode

    availableSize指父容器中剩余可用的全部空间

parentLayoutParams parentSpecMode childLayoutParams childSpecMode childSpecSize
match_parent EXACTLY match_parent EXACTLY childSize
match_parent EXACTLY wrap_content AT_MOST availableSize
match_parent EXACTLY dp EXACTLY childSize
wrap_parent AT_MOST wrap_content AT_MOST availableSize
wrap_parent AT_MOST match_parent AT_MOST availableSize
wrap_parent AT_MOST dp EXACTLY childSize
dp EXACTLY match_parent EXACTLY childSize
dp EXACTLY dp EXACTLY childSize
dp EXACTLY wrap_content AT_MOST availableSize
UNSPECIFIED match_parent UNSPECIFIED UNSPECIFIED
UNSPECIFIED wrap_content UNSPECIFIED UNSPECIFIED
UNSPECIFIED dp EXACTLY childSize
  • example(只对Height进行了处理)

    据表可以看到在子View的 SpecMode 为 AT_MOST的时候,SpecSize都为 availableSize,
    即使LayoutParams设置为wrap_content(AT_MOST)。所以在自定义View在重写onMeasure时,
    对AT_MOST要进行特殊处理来支持wrap_content.可以设置一个默认的SIZE,或者也可以通过计算得到SIZE

 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val heightSpecMode = MeasureSpec.getMode(heightMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        if (heightSpecMode == MeasureSpec.AT_MOST) {
            val mHeight = TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                DEFAULT_HEIGHT,
                Resources.getSystem().displayMetrics
            ).toInt()
            setMeasuredDimension(widthSize, mHeight)
        } else {
            setMeasuredDimension(widthSize, heightSize)
        }
    }

onLayout

一般自定义View时,ViewGroup需要重写onLayout()方法,经过自定义处理后在其中调用childView的layout方法。
View一般则不需要重写onLayout()

protected void onLayout(boolean changed, int left, int top, int right, int bottom) { }
`

paramName Description
changed 见View.java的 protected boolean setFrame(int left, int top, int right, int bottom)
left View左边界距父布局的左边界距离
top View上边界距父布局的上边界距离
right View右边界距父布局的右边界距离
bottom View底边界距父布局的底边界距离

onDraw

自定义View时,一般ViewGroup不需要重写onDraw()方法。
View则需要重写onDraw()方法,画出相应的内容

ViewGroup扩展

  • ViewGroup的子项是否延迟按下状态,通常像ListView这种可以滚动的布局返回是 true
    属于ViewGroup中的方法
    public boolean shouldDelayChildPressedState() { return true; }

  • 自定义ViewGroup中的LayoutParams属性,使其在childView中使用, 如LinearLayout中的layout_weight

    1. 定义attr属性文件
    
            
                
                
                
                
            
        
    1. 自定义LayoutParams(内部类),获取属性值
      inner class LayoutParams : ViewGroup.MarginLayoutParams {
            var position: Int = 0
    
            constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
                context.obtainStyledAttributes(attrs, R.styleable.VerticalLinearLayout_Layout)?.run {
                    position = getInteger(R.styleable.VerticalLinearLayout_Layout_layout_position, position)
                    recycle()
                }
            }
    
            constructor(width: Int, height: Int) : super(width, height) {}
    
            constructor(p: ViewGroup.LayoutParams) : super(p)
    
            constructor(source: ViewGroup.MarginLayoutParams) : super(source)
    
            constructor(source: LayoutParams) : super(source)
    
        }
    
    1. 重写ViewGroup中的相关方法
     override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams {
            return LayoutParams(context, attrs)
        }
    
        override fun generateLayoutParams(p: ViewGroup.LayoutParams?): ViewGroup.LayoutParams {
            return LayoutParams(p)
        }
    
        override fun generateDefaultLayoutParams(): LayoutParams {
            return LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
        }
    
        override fun checkLayoutParams(p: ViewGroup.LayoutParams?): Boolean {
            return p is VerticalLinearLayout.LayoutParams
        }
    

资源

Android开发艺术探索 - 任玉刚
例子

你可能感兴趣的:(自定义View 入门)