Skip to content

Commit b17d4d0

Browse files
committed
add-custom-view
1 parent 545864b commit b17d4d0

2 files changed

Lines changed: 351 additions & 0 deletions

File tree

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
# 自定义View入门
2+
3+
> 在Android应用开发过程中,固定的一些控件和属性可能满足不了开发的需求,所以在一些特殊情况下,我们需要自定义控件与属性。
4+
5+
# 一、实现步骤
6+
7+
  
8+
9+
1. 继承View类或其子类 
10+
11+
2. 复写view中的一些函数
12+
13+
3. 为自定义View类增加属性(两种方式)
14+
15+
4. 绘制控件(导入布局)
16+
17+
5. 响应用户事件
18+
19+
6. 定义回调函数(根据自己需求来选择)
20+
21+
# 二、哪些方法需要被重写
22+
23+
  
24+
25+
- ``onDraw()``
26+
27+
  view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的绘制。对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的(但必须实现dispatchDraw()函数,告诉子view绘制自己)。
28+
29+
- ``onLayout()``
30+
31+
  主要是为viewGroup类型布局子视图用的,在View中这个函数为空函数。
32+
33+
- ``onMeasure()``
34+
35+
  用于计算视图大小(即长和宽)的方式,并通过setMeasuredDimension(width, height)保存计算结果。
36+
37+
- ``onTouchEvent``
38+
39+
  定义触屏事件来响应用户操作。
40+
  
41+
42+
----
43+
44+
还有一些不常用的方法:
45+
46+
``onKeyDown()`` 当按下某个键盘时  
47+
48+
``onKeyUp()`` 当松开某个键盘时  
49+
  
50+
``onTrackballEvent()`` 当发生轨迹球事件时  
51+
  
52+
``onSizeChange()`` 当该组件的大小被改变时  
53+
  
54+
``onFinishInflate()`` 回调方法,当应用从XML加载该组件并用它构建界面之后调用的方法  
55+
  
56+
``onWindowFocusChanged(boolean)`` 当该组件得到、失去焦点时  
57+
58+
``onAttachedToWindow()`` 当把该组件放入到某个窗口时  
59+
  
60+
``onDetachedFromWindow()`` 当把该组件从某个窗口上分离时触发的方法  
61+
  
62+
``onWindowVisibilityChanged(int)`` 当包含该组件的窗口的可见性发生改变时触发的方法  
63+
64+
**View的绘制流程**
65+
绘制流程函数调用关系如下图:
66+
67+
![这里写图片描述](http://img.blog.csdn.net/20160617150747985)
68+
69+
我们调用requestLayout()的时候,会触发measure 和 layout 过程,调用invalidate,会执行 draw 过程。
70+
71+
# 三.自定义控件的三种方式
72+
73+
  
74+
75+
1. 继承已有的控件
76+
77+
  当要实现的控件和已有的控件在很多方面比较类似, 通过对已有控件的扩展来满足要求。
78+
79+
2. 继承一个布局文件
80+
81+
  一般用于自定义组合控件,在构造函数中通过inflater和addView()方法加载自定义控件的布局文件形成图形界面(不需要onDraw方法)。
82+
83+
3.继承view
84+
85+
  通过onDraw方法来绘制出组件界面。
86+
87+
# 四.自定义属性的两种方法
88+
89+
1.在布局文件中直接加入属性,在构造函数中去获得。
90+
91+
92+
布局文件:
93+
94+
```
95+
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
96+
android:layout_width="match_parent"
97+
android:layout_height="match_parent"
98+
>
99+
<com.example.demo.myView
100+
android:layout_width="wrap_content"
101+
android:layout_height="wrap_content"
102+
Text="@string/hello_world"
103+
/>
104+
</RelativeLayout>
105+
106+
```
107+
获取属性值:
108+
109+
```
110+
public myView(Context context, AttributeSet attrs) {
111+
super(context, attrs);
112+
// TODO Auto-generated constructor stub
113+
int textId = attrs.getAttributeResourceValue(null, "Text", 0);
114+
String text = context.getResources().getText(textId).toString();
115+
}
116+
```
117+
118+
2.在res/values/ 下建立一个attrs.xml 来声明自定义view的属性。
119+
120+
121+
可以定义的属性有:
122+
123+
```
124+
<declare-styleable name = "名称">
125+
//参考某一资源ID (name可以随便命名)
126+
<attr name = "background" format = "reference" />
127+
//颜色值
128+
<attr name = "textColor" format = "color" />
129+
//布尔值
130+
<attr name = "focusable" format = "boolean" />
131+
//尺寸值
132+
<attr name = "layout_width" format = "dimension" />
133+
//浮点值
134+
<attr name = "fromAlpha" format = "float" />
135+
//整型值
136+
<attr name = "frameDuration" format="integer" />
137+
//字符串
138+
<attr name = "text" format = "string" />
139+
//百分数
140+
<attr name = "pivotX" format = "fraction" />
141+
142+
//枚举值
143+
<attr name="orientation">
144+
<enum name="horizontal" value="0" />
145+
<enum name="vertical" value="1" />
146+
</attr>
147+
148+
//位或运算
149+
<attr name="windowSoftInputMode">
150+
<flag name = "stateUnspecified" value = "0" />
151+
<flag name = "stateUnchanged" value = "1" />
152+
</attr>
153+
154+
//多类型
155+
<attr name = "background" format = "reference|color" />
156+
</declare-styleable>
157+
```
158+
159+
- attrs.xml进行属性声明
160+
161+
  
162+
163+
```
164+
<?xml version="1.0" encoding="utf-8"?>
165+
<resources>
166+
<declare-styleable name="myView">
167+
<attr name="text" format="string"/>
168+
<attr name="textColor" format="color"/>
169+
</declare-styleable>
170+
</resources>
171+
172+
```
173+
174+
- 添加到布局文件
175+
176+
  
177+
178+
```
179+
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
180+
android:layout_width="match_parent"
181+
android:layout_height="match_parent"
182+
xmlns:myview="http://schemas.android.com/apk/com.example.demo"
183+
>
184+
<com.example.demo.myView
185+
android:layout_width="wrap_content"
186+
android:layout_height="wrap_content"
187+
myview:text = "test"
188+
myview:textColor ="#ff0000"
189+
/>
190+
</RelativeLayout>
191+
192+
```
193+
这里注意命名空间:
194+
xmlns:前缀="http://schemas.android.com/apk/res/包名(或res-auto)",
195+
196+
前缀:TextColor 使用属性。
197+
198+
- 在构造函数中获取属性值
199+
200+
  
201+
202+
```
203+
public myView(Context context, AttributeSet attrs) {
204+
super(context, attrs);
205+
// TODO Auto-generated constructor stub
206+
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView);
207+
String text = a.getString(R.styleable.myView_text);
208+
int textColor = a.getColor(R.styleable.myView_textColor, Color.WHITE);
209+
210+
a.recycle();
211+
}
212+
```
213+
214+
 或者:
215+
216+
 
217+
218+
```
219+
public myView(Context context, AttributeSet attrs) {
220+
super(context, attrs);
221+
// TODO Auto-generated constructor stub
222+
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView);
223+
int n = a.getIndexCount();
224+
for(int i=0;i<n;i++){
225+
int attr = a.getIndex(i);
226+
switch (attr) {
227+
case R.styleable.myView_text:
228+
229+
break;
230+
231+
case R.styleable.myView_textColor:
232+
233+
break;
234+
235+
}
236+
}
237+
a.recycle();
238+
}
239+
```
240+
241+
# 五. 自定义随手指移动的小球(小例子)
242+
243+
244+
<img src="http://img.blog.csdn.net/20160503143613554" width="210" height="334" />
245+
246+
247+
实现上面的效果我们大致需要分成这几步
248+
249+
- 在res/values/ 下建立一个attrs.xml 来声明自定义view的属性
250+
- 一个继承View并复写部分函数的自定义view的类
251+
- 一个展示自定义view 的容器界面
252+
253+
1.自定义view命名为myView,它有一个属性值,格式为color:
254+
255+
```
256+
<?xml version="1.0" encoding="utf-8"?>
257+
<resources>
258+
<declare-styleable name="myView">
259+
<attr name="TextColor" format="color"/>
260+
</declare-styleable>
261+
</resources>
262+
```
263+
264+
2.在构造函数获取获得view的属性配置和复写onDraw和onTouchEvent函数实现绘制界面和用户事件响应。
265+
266+
```
267+
public class myView extends View{
268+
//定义画笔和初始位置
269+
Paint p = new Paint();
270+
public float currentX = 50;
271+
public float currentY = 50;
272+
public int textColor;
273+
274+
public myView(Context context, AttributeSet attrs) {
275+
super(context, attrs);
276+
//获取资源文件里面的属性,由于这里只有一个属性值,不用遍历数组,直接通过R文件拿出color值
277+
//把属性放在资源文件里,方便设置和复用
278+
TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.myView);
279+
textColor = array.getColor(R.styleable.myView_TextColor,Color.BLACK);
280+
array.recycle();
281+
}
282+
283+
@Override
284+
protected void onDraw(Canvas canvas) {
285+
super.onDraw(canvas);
286+
//画一个蓝色的圆形
287+
p.setColor(Color.BLUE);
288+
canvas.drawCircle(currentX,currentY,30,p);
289+
//设置文字和颜色,这里的颜色是资源文件values里面的值
290+
p.setColor(textColor);
291+
canvas.drawText("BY finch",currentX-30,currentY+50,p);
292+
}
293+
294+
@Override
295+
public boolean onTouchEvent(MotionEvent event) {
296+
297+
298+
currentX = event.getX();
299+
currentY = event.getY();
300+
invalidate();//重新绘制图形
301+
return true;
302+
}
303+
}
304+
```
305+
306+
  这里通过不断的更新当前位置坐标和重新绘制图形实现效果,要注意的是使用TypedArray后一定要记得recycle(). 否则会对下次调用产生影响。
307+
  ![这里写图片描述](http://img.blog.csdn.net/20160503144335969) 
308+
309+
310+
3.把myView加入到activity_main.xml布局里面
311+
312+
  
313+
314+
```
315+
<?xml version="1.0" encoding="utf-8"?>
316+
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
317+
xmlns:tools="http://schemas.android.com/tools"
318+
android:layout_width="match_parent"
319+
android:layout_height="match_parent"
320+
xmlns:myview="http://schemas.android.com/apk/res-auto"
321+
android:paddingBottom="@dimen/activity_vertical_margin"
322+
android:paddingLeft="@dimen/activity_horizontal_margin"
323+
android:paddingRight="@dimen/activity_horizontal_margin"
324+
android:paddingTop="@dimen/activity_vertical_margin"
325+
tools:context="finch.scu.cn.myview.MainActivity">
326+
327+
328+
<finch.scu.cn.myview.myView
329+
android:layout_width="match_parent"
330+
android:layout_height="match_parent"
331+
myview:TextColor="#ff0000"
332+
/>
333+
</RelativeLayout>
334+
```
335+
336+
4.最后是MainActivity
337+
338+
```
339+
public class MainActivity extends AppCompatActivity {
340+
341+
@Override
342+
protected void onCreate(Bundle savedInstanceState) {
343+
super.onCreate(savedInstanceState);
344+
setContentView(R.layout.activity_main);
345+
}
346+
}
347+
```
348+
349+
- 具体的view要根据具体的需求来,比如我们要侧滑删除的listview我们可以继承listview,监听侧滑事件,显示删除按钮实现功能。
350+

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
### 自定义View
3232

33+
- [自定义View入门](/Android自定义View/自定义View入门.md)
3334
- [CameraView](/Android自定义View/自定义View——CameraView.md)
3435
- [CheckView](/Android自定义View/自定义View——CheckView.md)
3536
- [CircleView](/Android自定义View/自定义View——CircleView.md)

0 commit comments

Comments
 (0)