Android 自定义控件基础知识
我一般写自定义控件步骤是这样的:
- 规划onDraw onLayout里面需要用到那些知识点
- 根据设定的自定义效果,提前规划出自定义属性(也可以最后提取)
- onMeasuae的测量模式
- 实现onDraw绘图效果
- onLayout的实现(是否需要)
- 检测 是否需要开启硬件加速,是否存在内存泄漏 ,对外暴露方法
自定义属性
在res/values下创建attrs.xml文件,写自定义属性:比如
<declare-styleable name="ArcGradualView">
<attr name="color_gradual_start" format="color" />
<attr name="color_gradual_end" format="color" />
<attr name="height_arc" format="dimension" />
<attr name="gradual_or">
<!--水平方向-->
<enum name="orizontal" value="0" />
<!--垂直方向-->
<enum name="vertical" value="1" />
<!--对角线45-->
<enum name="angles45" value="2" />
<!--对角线135-->
<enum name="angles135" value="3" />
</attr>
<attr name="color_other_arc" format="color" />
</declare-styleable>
其中name代表你自定义控件的名字
导入自定义属性需要注意的是,要让XML识别出来,需要在跟布局里加入:
<com.hujin.test.view.ArcGradualView
android:id="@+id/header_view"
app:height_arc="30dp"
app:color_gradual_start="@color/colorAccent"
app:color_gradual_end="@color/colorPrimary"
app:gradual_or="vertical"
app:color_other_arc="@android:color/black"
android:layout_width="match_parent"
android:layout_height="200dp"/>
xmlns:app="http://schemas.android.com/apk/res-auto"
其中app是你的自定义属性标识,可以随意命名。
declare-styleable标签
- reference 某个资源的ID
属性定义:
<declare-styleable name="ArcGradualView">
<attr name="src" format="reference"/>
</declare-styleable>
属性使用:
<com.example.hasee.widget.ArcGradualView
app:src="@drawable/bg_white_point"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- color 颜色值
属性定义
<declare-styleable name="ArcGradualView">
<attr name="bgColor" format="color"/>
</declare-styleable>
属性使用:
<com.example.hasee.widget.ArcGradualView
app:bgColor="#FFFFFF"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- boolean 布尔值
属性定义:
<declare-styleable name="ArcGradualView">
<attr name="isFocusable" format="boolean"/>
</declare-styleable>
属性使用:
<com.example.hasee.widget.ArcGradualView
app:isFocusable="true"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- dimension 尺寸值
属性定义
<declare-styleable name="ArcGradualView">
<attr name="line_width" format="dimension"/>
</declare-styleable>
属性使用
<com.example.hasee.widget.ArcGradualView
app:line_width="@dimen/dp_10"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- float浮点值
属性定义
<declare-styleable name="ArcGradualView">
<attr name="startAlpha" format="float"/>
<attr name="endAlpha" format="float"/>
</declare-styleable>
属性使用
<com.example.hasee.widget.ArcGradualView
app:startAlpha="0.0"
app:endAlpha="1.0"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- integer整型值
属性定义
<declare-styleable name="ArcGradualView">
<attr name="job_level" format="integer"/>
</declare-styleable>
属性使用
<com.example.hasee.widget.ArcGradualView
app:job_leveal="3"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- string 字符串
属性定义
<declare-styleable name="ArcGradualView">
<attr name="apiId" format="string"/>
</declare-styleable>
属性使用
<com.example.hasee.widget.ArcGradualView
app:apiId="123456789"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- fraction 百分数
属性定义
<declare-styleable name="ArcGradualView">
<attr name="pivotX" format="fraction"/>
<attr name="pivotY" format="fraction"/>
</declare-styleable>
属性使用
<com.example.hasee.widget.ArcGradualView
app:pivotX="200%"
app:pivotY="300%"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- enum 枚举值
属性定义
<declare-styleable name="ArcGradualView">
<attr name="gradual_or">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
<enum name="angles45" value="2" />
<enum name="angles135" value="3" />
</attr>
</declare-styleable>
属性使用:
<com.example.hasee.widget.ArcGradualView
app:gradual_or="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
枚举属性 只能写入列举的值之一
10.flag 位或运算
属性定义
<declare-styleable name="ArcGradualView">
<attr name="flagMode">
<flag name="right" value="0" />
<flag name="left" value="1" />
<flag name="top" value="2" />
<flag name="bottom" value="3" />
</attr>
</declare-styleable>
属性使用
<com.example.hasee.widget.ArcGradualView
app:flagMode="right|top"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
对于flag位或运算,如果变量比较少,建议是计算完后,使用enum枚举值去处理。
往往xml中定义的属性,希望是初始化的属性值,是静态的。而需求经常需要动态的,就需要重写该属性的get)_set()方法暴露出去,动态更新view。
这里要理解invalidate()
和 requestLayout()
的更新区别:
invalidate() —onDraw()
请求重绘View树,即draw()过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些“需要重绘的”视图,即谁(View的话,只绘制该View ;ViewGroup,则绘制整个ViewGroup)请求invalidate()方法,就绘制该视图。
一般方法会重新调用invalidate()
- 直接调用invalidate()方法:请求重新draw(),但只会绘制调用者本身
- setSelection()方法 :请求重新draw(),但只会绘制调用者本身
- setVisibility()方法 : 当View可视状态在INVISIBLE转换VISIBLE时,会间接调用invalidate()方法,继而绘制该View。
- setEnabled()方法 : 请求重新draw(),但不会重新绘制任何视图包括该调用者本身。
requestLayout()—onMeasure() 和onLayout() 或 onDraw()
只是对View树重新布局layout过程包括measure()和layout()过程。对于是否会调用draw()方法,大佬分析是:on在onLayout中发现r,l,t,b的值发生改变,就会重走draw()。
一般方法会重新调用requestLayout()
- 直接调用requestLayout()方法:请求重新onMeasure()和 onLayout(),但不会重新绘制。View只会走onMeasure()。
- setVisibility()方法:当View的可视状态在INVISIBLE/ VISIBLE 转换为GONE状态时,会间接调用requestLayout() 和invalidate方法。 同时,由于整个个View树大小发生了变化,会请求measure()过程以及draw()过程,同样地,只绘制需要“重新绘制”的视图。
测量onMeasure
以后再写