Kevin Su bio photo

Kevin Su

patience, persevere, and enjoy

Email Github Stackoverflow

关于自定义View

终于还是逃不过要实现自定义View的这一步,在实现调课的课程表这一功能中,没有发现合适的View可以处理,即使是GridView,也难以实现这上面盖有颜色,而且是在上面跨区域覆盖颜色这种情况.所以无可奈何之下,只能开始学习自定义绘制View.


Start:

  1. 定义一个View的子类,提供一个带有Context和AttributeSet参数的构造函数.
  2. 自定义属性:
    • 元素中为View定义属性
    • 在XML的布局文件中指定
    • 在运行过程中,获取属性的值
    • 把获取的值用在View上

        <resources>
            <declare-styleable name="PieChart">
       				<attr name="showText" format="boolean" />
       				<attr name="labelPosition" format="enum">
       				<enum name="left" value="0"/>
       				<enum name="right" value="1"/>
       				</attr>
               </declare-styleable>
        </resources>
      

    这段代码定义了两个自定义的属性,showText和labelPosition,这属于实体PieChart.

  3. 通过resource bundle 构成一个AttributeSet实体.之后再通过obtainStyledAttributes()方法,返回给一个TypeArray实体.

     public PieChart(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		TypedArray a = context.getTheme().obtainStyledAttributes(
     	attrs,
     	R.styleable.PieChart,
     	0, 0);
    
    		try {
    			mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
    			mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
    		} finally {
     		a.recycle();
    		}	
     }  用完之后记得把TypedArray recycled.
    
  4. 为属性提供getter和setter方法,以至于动态改变属性的值.在改变完属性的值后,要记得调用invalidate和requestLayout方法,保证view的行为发生了改变.
  5. 还需要暴露自定义的实现,特别是看得见的一些View.

自定义绘制: 绘制自定义的view是非常重要的一步.有以下几个常用的操作.

  1. 复写onDraw方法.它的参数Canvas对象可以用来自绘.Canvas定义一些方法,绘制文本,线条,图片还有一些其他图形,你可以用这些方法绘制你的自定义界面.在绘制之前,要先创建一个Paint对象,画笔对象.
  2. 创建一个绘画对象分为两步:
    • 画什么,取决于 Canvas
    • 怎么画.取决于 Paint

    例如,Canvas提供一个方法去画一条线,而Paint提供一个方法去定义这条线绘制什么颜色.Canvas定义你要在屏幕绘制怎么样的形状,而Paint定义颜色,样式,字体,还有其他等等.
    onDraw方法会被频繁调用,所以不要在其中做耗时的初始化操作,否者会出现卡顿的情况.

  3. 处理布局事件. 为了正确的绘制自定义的view,需要计算view的形状以及在屏幕的位置.但是,又无法假定屏幕的大小,因为android手机有多种多样的屏幕尺寸,还可以横屏,竖屏.
    虽然view提供多个计算尺寸的方法,但大多数都不需要复写,只需要复写一个方法:onSizeChanged.
    onSizeChanged方法在View第一次分配大小的时候,还有在view的大小发生改变的情况下会被调用.在其中计算位置,尺寸,还有其他与size相关的值.
    onMeasure方法是用来处理布局超出能显示的范围用的.方法中的MeasureSpec值会告诉你,你的view的parent期望的大小,还有告诉你这是硬性规定还是建议.
    需要注意三点:
    1. 需要计算view的padding
    2. resolveSizeAndState()方法计算最后的宽高的值.返回一个合适的值存在View.MeasureSpec中.最后传递给onMeasure()
    3. onMeasure方法不会返回值,取而代之的事通过调用setMeasureDimension方法来分享值.必须调用该方法,否则会抛运行异常.

给View加入各种反馈事件: