转自:http://www.apkbus.com/forum.php?mod=viewthread&tid=242501
做过了一段时间的
安卓开发都会接触到自定义控件,那么对于自定义控件大家都有什么样的看法呢?自定义控件他的优势是明显的,设计他的思想又有哪些呢?会用到什么模式呢?希望大家看了这个文章之后,可以发表自己对于自定义控件的看法和思想,这个帖就是为了跟大家交流而发。下面是我自己的一些看法,大侠们请指导!!!
自定义View:
在这里我们先来了解自定义控件
view
所需要的一些基础知识,以及常见做法。
自定义View类的构造方法
创建自定义控件的
3
种主要实现方式
:
1)继承已有的控件来实现自定义控件
主要是当要实现的控件和已有的控件在很多方面比较类似
,
通过对已有控件的扩展来满足要求。
2)通过继承一个布局文件实现自定义控件
一般来说做组合控件时可以通过这个方式来实现。
注意此时不用
onDraw
方法,在构造函数中通过
inflater
加载自定义控件的布局文件,再
addView(view)
,自定义控件的图形界面就加载进来了。
例如:
假设我已经有了一个布局的
XML
文件,里面有一个
textview
和一个
imageview
,那么在自定义
view
的构造方法里这样写就可以使用刚刚的布局
XML
啦。
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.myView, this);
imageView=(ImageView) findViewById(R.id.imageView1);
textView=(TextView)findViewById(R.id.textView1);
}
3) 通过继承 view 类来实现自定义控件,使用 GDI 绘制出组件界面,一般无法通过上述两种方式来实现时用该方式。
自定义View增加属性的两种方法
在布局中使用这个自定义属性:
1)在
View
类中定义。通过构造函数中引入的
AttributeSet
去查找
XML
布局的属性名称,然后找到它对应引用的资源
ID
去找值。
在下面自定义了两个属性
Text, Src
。
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.apkbus.MyView
android:id="@+id/myView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Text="@string/hello_world"
Src="@drawable/logo"/>
</LinearLayout>
属性
Text, Src
在自定义
View
类的构造方法中读取。
Java
代码:
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
int resourceId = 0;
int textId = attrs.getAttributeResourceValue(null, "Text",0);
int srcId = attrs.getAttributeResourceValue(null, "Src", 0);
mtext = context.getResources().getText(textId).toString();
}
2)通过
XML
为
View
注册属性。与
Android
提供的标准属性写法一样。
例如:
需要自定义属性
Text,Select
和
Src
,先创建
attrs.xml
进行属性声明, 文件放在
values
目录下。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyView">
<attr name="Text" format="reference|string"></attr>
<attr name="Select" >
<enum name="open" value="1"></enum>
<enum name="close" value="0"></enum>
</attr>
<attr name="Src" format="reference|integer"></attr>
</declare-styleable>
</resources>
在布局中使用这个自定义属性:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:myView="http://schemas.android.com/apk/res/com.apkbus.myview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<com.apkbus.MyView
android:id="@+id/myView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
myView:Text="www.apkbug.com"
myView:Src="@drawable/img"
myView:Select="open">
</com.apkbus.MyView>
</LinearLayout>
说明:
在使用自定义布局的时候需要添加这样的一行在布局的开头位置
xmlns:myView="http://schemas.android.com/apk/res/com.apkbus.myview"
1
、
myView
是自定义的一个命名空间,你可以取一个喜欢的名称。
2
、
"http://schemas.android.com/apk/res/com.apkbus.myview"
这部分的字符串是由”http://schemas.android.com/apk/res/”和应用的包名”com.apkbus.myview”组成。
然后在自定义
View
类的构造方法中读取
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
String pkName = "http://schemas.android.com/apk/res/" + context.getPackageName();
int textId = attrs.getAttributeResourceValue(pkName , "Text",0);
int srcId = attrs.getAttributeResourceValue(pkName , "Src", 0);
int select = attrs.getAttributeIntValue(pkName, "select", 0);
mtext = context.getResources().getText(textId).toString();
}
自定义View的常用方法
onFinishInflate()
回调方法,当应用从
XML
加载该组件并用它构建界面之后调用的方法
onMeasure()
检测
View
组件及其子组件的大小
onLayout()
当该组件需要分配其子组件的位置、大小时
onSizeChange()
当该组件的大小被改变时
onDraw()
当组件将要绘
制它的内容时
onKeyDown
当按下某个键盘时
onKeyUp
当松开某个键盘时
onTrackballEvent
当发生轨迹球事件时
onTouchEvent
当发生触屏事件时
onWindowFocusChanged(boolean)
当该组件得到、失去焦点时
onAtrrachedToWindow()
当把该组件放入到某个窗口时
onDetachedFromWindow()
当把该组件从某个窗口上分离时触发的方法
onWindowVisibilityChanged(int):
当包含该组件的窗口的可见性发生改变时触发的方法
View的设计理念:
用在View设计的模式:
看到这个小标题可能感觉好高端,都到了理念的层次了。其实跟“
View
的设计目的”这个标题是差不多意思的。
做了开发这么久了,我总结了大概这几个设计的目的。
1、重用性目的
为了可以在不同的模块、项目中重复使用而设计。
2、灵活性目的
自定义
View
可以方便的实现系统提供的控件所没有的功能,开发项目的时候灵活性大大增加。
3
、解耦合目的
由于自定义控件是相对独立的,自然其与其他模块之间的耦合性也是比较低的。模块间解耦自然就不可或缺咯。
既然有了目的,那么怎么实现就成为以下命题了,在这里我想跟你们谈谈设计模式。
在前面大家已经了解了实现一个自定义
View
的基本方法,但是做起来估计也是蹑手蹑脚的,不知道该如何具体的实现,我把这种迷惘叫缺少指导思想。
这里的指导思想就是设计模式,业务逻辑代码应该放哪?
UI
代码应该放哪?数据存储代码又应该放哪?只有明确了上面三个问题才算是达到了自定义
View
的设计目的。
用在
View
上的设计模式是很多的,下面列举一些比较常用的模式供大家了解学习。
1、设配器模式
![](http://www.apkbus.com/data/attachment/forum/201506/01/103201z79lcpccpzc9berj.jpg)
对于
android
开发者来说起,适配器模式简直太熟悉不过,有很多应用可以说是天天在直接或者间接的用到适配器模式,比如
ListView
。
ListView
用于显示列表数据,但是作为列表数据集合有很多形式,有
Array
,有
Cursor
,我们需要对应的适配器作为桥梁,处理相应的数据(并能形成
ListView
所需要的视图)。
正是因为定义了这些适配器接口和适配器类,才能使我们的数据简单灵活而又正确的显示到了
adapterview
的实现类上。
目的:
适配器模式,把一个类的接口变换成客户端所期待的另一种接口,从而使原本不匹配而无法在一起工作的两个类能够在一起工作。
适配器模式分为类适配器模式和对象适配器模式。
关于类适配器模式,因为
java
的单继承,如果继承一个类,另外的则只能是接口,需要手动实现相应的方法。
2、组合模式
![](http://www.apkbus.com/data/attachment/forum/201506/01/103201z7ww1wc26ruym7ga.jpg)
Android
中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是
View
和
ViewGroup
类的使用。在
android UI
设计,几乎所有的
widget
和布局类都依靠这两个类。
组合模式,
Composite Pattern
,是一个非常巧妙的模式。几乎所有的面向对象系统都应用到了组合模式。
目的:
将对象
View
和
ViewGroup
组合成树形结构以表示
"
部分
-
整体
"
的层次结构
(View
可以做为
ViewGroup
的一部分
)
。
组合模式使得用户对单个对象
View
和组合对象
ViewGroup
的使用具有一致性。
3、MVC模式
![](http://www.apkbus.com/data/attachment/forum/201506/01/103201zq4zfrw5lfrz2mzx.jpg)
MVC是三个单词的缩写,分别为: 模型(Model),视图(View)和控制Controller)。 MVC模式的目的就是实现Web系统的职能分工。 Model层实现系统中的业务逻辑。 View层用于与用户的交互。 Controller层是Model与View之间沟通的桥梁,它可以分派用户的请求并选择恰当的视图以用于显示,同时它也可以解释用户的输入并将它们映射为模型层可执行的操作。
1) 视图层(View):一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入。当然,如何你对Android了解的比较的多了话,就一定可以想到在Android中也可以使用JavaScript+HTML等的方式作为View层,当然这里需要进行Java和JavaScript之间的通信,幸运的是,Android提供了它们之间非常方便的通信实现。
2) 控制层(Controller):Android的控制层的重任通常落在了众多的Acitvity的肩上,这句话也就暗含了不要在Acitivity中写代码,要通过Activity交割Model业务逻辑层处理,这样做的另外一个原因是Android中的Acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。
3) 模型层(Model):对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。就是应用程序中二进制的数据。
4、MVP模式
![](http://www.apkbus.com/data/attachment/forum/201506/01/103201gp2l0do2wotwht5t.png)
MVP
是从经典的模式
MVC
演变而来,它们的基本思想有相通的地方:
Controller/Presenter
负责逻辑的处理,
Model
提供数据,
View
负责显示。作为一种新的模式,
MVP
与
MVC
有着一个重大的区别:在
MVP
中
View
并不直接使用
Model
,它们之间的通信是通过
Presenter (MVC
中的
Controller)
来进行的,所有的交互都发生在
Presenter
内部,而在
MVC
中
View
会从直接
Model
中读取数据而不是通过
Controller
。
在
MVP
模式里通常包含
4
个要素:
(1)View:
负责绘制
UI
元素、与用户进行交互
(
在
Android
中体现为
Activity);
(2)View interface:
需要
View
实现的接口,
View
通过
View interface
与
Presenter
进行交互,降低耦合,方便进行单元测试
;
(3)Model:
负责存储、检索、操纵数据
(
有时也实现一个
Model interface
用来降低耦合
);
(4)Presenter:
作为
View
与
Model
交互的中间纽带,处理与用户交互的负责逻辑。
核心思想:
看了这么多模式,看到头都晕了,我都没耐性看咯。好啦,我来解说一下吧!
其实这么多的模式都有这样的一个核心思想,了解了这个思想之后这些模式不过是同一个思想的不同实现罢了。
1、物理分离
将处理业务逻辑、
UI
布局、数据存储的代码进行物理分离,分别放在不同的文件中。
2、外部调用
不同的层之间交互一定是通过调用层的开放方法来实现,比如逻辑层不会调用
UI
层(
view
类)的父类方法,而是调用其自定义方法。
3、低耦合