论坛风格切换切换到宽版
发帖 回复
返回列表  提醒:不能用迅雷等P2P下载,否则下载失败标(二级)的板块,需二级才能下载,没二级不要购买,下载不了
  • 1155阅读
  • 0回复

[移动软件开发]玩转自定义View,让你的代码高效又简洁 [复制链接]

上一主题 下一主题
在线longxuekai
 

性别:
帅哥
发帖
18770
金币
29173
提示:会员销售的附件,下载积分 = 版块积分 + 销售积分       只看楼主 正序阅读 使用道具 0楼 发表于: 2016-03-07
做过了一段时间的安卓开发都会接触到自定义控件,那么对于自定义控件大家都有什么样的看法呢?自定义控件他的优势是明显的,设计他的思想又有哪些呢?会用到什么模式呢?希望大家看了这个文章之后,可以发表自己对于自定义控件的看法和思想,这个帖就是为了跟大家交流而发。下面是我自己的一些看法,大侠们请指导!!! nD!^0?  
:U~[%]  

自定义View: {W0@lMrD  
在这里我们先来了解自定义控件view所需要的一些基础知识,以及常见做法。 9 eP @}C6  

自定义View类的构造方法 ]LD@I;(_  
创建自定义控件的3种主要实现方式: rVkHo*Q  
>4;A (s`  
1)继承已有的控件来实现自定义控件 46*?hA7@r(  
主要是当要实现的控件和已有的控件在很多方面比较类似, 通过对已有控件的扩展来满足要求。 JV@>dK8  
TygR G+G-  
2)通过继承一个布局文件实现自定义控件 il*bsnwpZv  
一般来说做组合控件时可以通过这个方式来实现。 c1c0b|B!U  
注意此时不用onDraw方法,在构造函数中通过inflater加载自定义控件的布局文件,再addView(view),自定义控件的图形界面就加载进来了。 ztf(.~  
例如: vsc&$r3!5{  
假设我已经有了一个布局的XML文件,里面有一个textview和一个imageview,那么在自定义view的构造方法里这样写就可以使用刚刚的布局XML啦。 =!7yX ;|  
[mw_shl_code=java,true]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);

    }[/mw_shl_code] En1pz\'  
7[Y<5T]  
3)通过继承view类来实现自定义控件,使用GDI绘制出组件界面,一般无法通过上述两种方式来实现时用该方式。 67d0JQTu  



自定义View增加属性的两种方法 SwQb"  
\OOj]gAe  
1)在View类中定义。通过构造函数中引入的AttributeSet 去查找XML布局的属性名称,然后找到它对应引用的资源ID去找值。 Tmq:,.^}  
klPc l[.w  
在下面自定义了两个属性Text, Src。 k)4   
布局文件: .Emw;+>  
[mw_shl_code=xml,true]<?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>[/mw_shl_code] ) ~X\W\  
QQqWJq~  
属性Text, Src在自定义View类的构造方法中读取。 i2EB.Zlv  
Java代码: qV5ME #TJ  
[mw_shl_code=java,true]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();

        

    }[/mw_shl_code] TzVNZDQ`Jl  
-B R&b2  
^/c&Ud  
2)通过XML为View注册属性。与Android提供的标准属性写法一样。 QGfU:  
例如: *&LVn)@[`  
需要自定义属性Text,Select和Src,先创建attrs.xml进行属性声明, 文件放在values目录下。 JrO2"S  
[mw_shl_code=xml,true]<?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>[/mw_shl_code] ! XA07O[@  
\07 s'W U  
在布局中使用这个自定义属性: lepgmQ|oY  
$BCqz! 4K  
[mw_shl_code=xml,true]<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>[/mw_shl_code] an.)2*u  
x2;92I{5C,  
说明: +,If|5>(  
FD[* mCGZ  
在使用自定义布局的时候需要添加这样的一行在布局的开头位置 iz>a0~(K  
xmlns:myView="http://schemas.android.com/apk/res/com.apkbus.myview" HnrT;!C~  
?mnwD]u  
1、myView是自定义的一个命名空间,你可以取一个喜欢的名称。 a(!:a+9WOP  
(1*?2u*j  
2、"http://schemas.android.com/apk/res/com.apkbus.myview"这部分的字符串是由”http://schemas.android.com/apk/res/”和应用的包名”com.apkbus.myview”组成。 LDO@$jg  
^KmyB6Yg  
然后在自定义View类的构造方法中读取 u[y>DPPx  
Bx2E9/S3  
[mw_shl_code=java,true]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();

        

    }[/mw_shl_code] P5kkaLzG  


自定义View的常用方法 VMsAT3^w  
onFinishInflate() 回调方法,当应用从XML加载该组件并用它构建界面之后调用的方法  !1;DRF  
$}N'm  
onMeasure() 检测View组件及其子组件的大小 Rax}r  
yin"+&<T  
onLayout() 当该组件需要分配其子组件的位置、大小时 IYb@@Jzo  
| \C{R  
onSizeChange() 当该组件的大小被改变时 qK#\k@E  
J8~3LE )G  
onDraw() 当组件将要绘制它的内容时 1vu=2|QN  
I"y=A7Nq  
onKeyDown 当按下某个键盘时 BB>3Kj:|  
0 ; M+8  
onKeyUp  当松开某个键盘时 h{VCx#!]  
XQ.JzzY$  
onTrackballEvent 当发生轨迹球事件时 }r9f}yX9Q  
R@u6mMX{N,  
onTouchEvent 当发生触屏事件时 esWgYAc3{  
79z(n[^  
onWindowFocusChanged(boolean)  当该组件得到、失去焦点时 JstX# z  
5,|^4 ZA  
onAtrrachedToWindow() 当把该组件放入到某个窗口时 P10`X&  
U_y)p Cd  
onDetachedFromWindow() 当把该组件从某个窗口上分离时触发的方法 7?dB&m6W  
IX,/ZOZ|  
onWindowVisibilityChanged(int): 当包含该组件的窗口的可见性发生改变时触发的方法 /k l0(='  




View的设计理念: ~W_m<#K(  
看到这个小标题可能感觉好高端,都到了理念的层次了。其实跟“View的设计目的”这个标题是差不多意思的。 5JBenTt  
做了开发这么久了,我总结了大概这几个设计的目的。 rX)PN3TD  
  9Ld3  
1、重用性目的 N yFa2Ihd  
为了可以在不同的模块、项目中重复使用而设计。 -H#{[M8xX  
[}N?'foLb  
)UO:J7K  
2、灵活性目的 3#j%F  
自定义View可以方便的实现系统提供的控件所没有的功能,开发项目的时候灵活性大大增加。 h 1:uTrtA  
t=d~\_Oa  
3、解耦合目的 fr4#< 6,  
由于自定义控件是相对独立的,自然其与其他模块之间的耦合性也是比较低的。模块间解耦自然就不可或缺咯。 pdngM 8n  
GTyS8`5E*  
既然有了目的,那么怎么实现就成为以下命题了,在这里我想跟你们谈谈设计模式。 %F` c Nw]  
在前面大家已经了解了实现一个自定义View的基本方法,但是做起来估计也是蹑手蹑脚的,不知道该如何具体的实现,我把这种迷惘叫缺少指导思想。 %e+hM $Q  
=;9Wh!{  
这里的指导思想就是设计模式,业务逻辑代码应该放哪?UI代码应该放哪?数据存储代码又应该放哪?只有明确了上面三个问题才算是达到了自定义View的设计目的。 (>%Ddj6_>  





用在View设计的模式: [>=D9I@~  
用在View上的设计模式是很多的,下面列举一些比较常用的模式供大家了解学习 9N:Bu'j&/  
FAzshR  
1、设配器模式 ~ b!mKyrZ  
MMN2X xS  
对于Android开发者来说起,适配器模式简直太熟悉不过,有很多应用可以说是天天在直接或者间接的用到适配器模式,比如ListView。 Kzb&aOw  
ListView用于显示列表数据,但是作为列表数据集合有很多形式,有Array,有Cursor,我们需要对应的适配器作为桥梁,处理相应的数据(并能形成ListView所需要的视图)。 F<M#T  
正是因为定义了这些适配器接口和适配器类,才能使我们的数据简单灵活而又正确的显示到了adapterview的实现类上。 HpC|dtro  
l`UJHX  
目的: dP=1*  
适配器模式,把一个类的接口变换成客户端所期待的另一种接口,从而使原本不匹配而无法在一起工作的两个类能够在一起工作。 zQMsS  
适配器模式分为类适配器模式和对象适配器模式。 be|k"s|6)  
关于类适配器模式,因为java的单继承,如果继承一个类,另外的则只能是接口,需要手动实现相应的方法。 ;O}%_ef@  
|CexP^;!U  
2、组合模式 5?&k? v@  
mqrP0/sN  
Android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是View和ViewGroup类的使用。在android UI设计,几乎所有的widget和布局类都依靠这两个类。 z | Hl*T  
组合模式,Composite Pattern,是一个非常巧妙的模式。几乎所有的面向对象系统都应用到了组合模式。 O5CIK}A  
`(vgBz`e[  
目的: Py^F},?J  
将对象View和ViewGroup组合成树形结构以表示"部分-整体"的层次结构(View可以做为ViewGroup的一部分)。 -v'7;L0K  
组合模式使得用户对单个对象View和组合对象ViewGroup的使用具有一致性。 >_!pg<{,  
m\(4y Gj  
3、MVC模式 /pan{.< k  
,B(UkPGT  
,A[40SZA  
MVC是三个单词的缩写,分别为: 模型(Model),视图(View)和控制Controller)。 MVC模式的目的就是实现Web系统的职能分工。 Model层实现系统中的业务逻辑。 View层用于与用户的交互。 Controller层是Model与View之间沟通的桥梁,它可以分派用户的请求并选择恰当的视图以用于显示,同时它也可以解释用户的输入并将它们映射为模型层可执行的操作。 *]nha1!S  
%*wJODtB|  
1) 视图层(View):一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入。当然,如何你对Android了解的比较的多了话,就一定可以想到在Android中也可以使用JavaScript+HTML等的方式作为View层,当然这里需要进行Java和JavaScript之间的通信,幸运的是,Android提供了它们之间非常方便的通信实现。     TG8QT\0G  
^>gRK*,  
2) 控制层(Controller):Android的控制层的重任通常落在了众多的Acitvity的肩上,这句话也就暗含了不要在Acitivity中写代码,要通过Activity交割Model业务逻辑层处理,这样做的另外一个原因是Android中的Acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。 7Vr .&`l  
kj~)#KDN  
3) 模型层(Model):对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。就是应用程序中二进制的数据。 $Q{)AN;m  
\$}xt`6p  
4、MVP模式 s-Q-1lKV,  
`<i|K*u  
TviC1 {2  
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。 [yM{A<\L  
!MS z%QcO  
在MVP模式里通常包含4个要素: {9vvj  
A)Wp W M  
(1)View:负责绘制UI元素、与用户进行交互(在Android中体现为Activity); L MC-1  
VI,z7 \  
Z#BwJHh  
(2)View interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便进行单元测试; gV1&b (h  
RXh0hD  
;n$j?n+|  
(3)Model:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合); fQi7e5  
3Q*K+(`{  
.\= GfF'  
(4)Presenter:作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。 %_;q<@9)  


核心思想: )_"Cz".|9  
看了这么多模式,看到头都晕了,我都没耐性看咯。好啦,我来解说一下吧! [X0Wfb}{  
0kS[`a(}J  
其实这么多的模式都有这样的一个核心思想,了解了这个思想之后这些模式不过是同一个思想的不同实现罢了。 M,"4r^%k  
I~H:-"2  
?v PwI  
1、物理分离 ny%-u &1k  
将处理业务逻辑、UI布局、数据存储的代码进行物理分离,分别放在不同的文件中。 .%_scNP  
ks< gSCB  
%Ez%pT0TQ#  
2、外部调用 . \M@oF  
不同的层之间交互一定是通过调用层的开放方法来实现,比如逻辑层不会调用UI层(view类)的父类方法,而是调用其自定义方法。 $j0] +vT  
Izm8 qt=m  
I~y[8  
3、低耦合 Yxz(g]  



最后: u6IEBYG ((  
了解了这么多大家可能会感觉信息量太多一时还消化不了,在后面我会发帖跟大家聊聊MVP模式,请期待。 #x$.  
ugcWFB5|  
最后,希望大家广开金口,聊聊自己对自定义view的见解,或者对模式的理解。对于新手来说你们的经验是很珍贵的,希望大家可以分享,如果文章有错也希望可以指正。 $J"}7+  
6pDb5@QjTy  
【安卓巴士】安卓巴士贯穿:资源下载,学习提升,技术研究,开发交流这个四个主题为出发点,汇聚各路英雄好汉的心得,好东西当然要share啦!问题只有一个,解决办法却不止一个,更多精彩内容,尽在安卓巴士! dy N`9  
                        猛戳:www.apkbus.com jCqs^`-  
                    新浪关注号:安卓巴士社区 }C!N$8d,  
                   微信公众号:安卓巴士Android开发者门户


评价一下你浏览此帖子的感受

精彩

感动

搞笑

开心

愤怒

一般

差劲
快速回复
限150 字节
 
上一个 下一个