自定义控件的优势很明显,这篇文章为自己总结
自定义View类的构造方法
创建自定义控件的3种主要实现方式
1:继承已有的控件来实现自定义控件
主要是当要实现的控件和已有的控件在很多方面比较类似,通过对已有控件的扩展来满足要求
2:通过继承一个布局文件实现自定义控件
一般说来做组合控件时可以通过这个方式来实现
注意此时不用onDraw方法,在构造函数中通过inflater加载自定义控件的布局文件,再addView(view),自定义控件的图形界面就加载进来了
例如:假如我已经有了一个布局的XML文件,里面有一个textview和一个imageView,那么在自定义view的构造方法里这样写就可以使用刚刚的布局xml啦
package com.example.xuan.customview_demo2; import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; public class MyView extends LinearLayout { public MyView(Context context) { super(context); initView(context,null); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); initView(context, attrs); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } private ImageView mImageView; private TextView mTextView; private void initView(Context context, AttributeSet attrs) { LayoutInflater inflater= (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.my_view,this); mImageView= (ImageView) findViewById(R.id.iv_icon); mTextView= (TextView) findViewById(R.id.tv_content); } }</pre><pre name="code" class="java" style="font-size: 9pt;">3:通过继承view类来实现自定义控件,使用GDI绘制出界面,一般无法通过上述两种方式时用该放方式</pre><pre name="code" class="java" style="font-size: 9pt;"><strong>自定义View增加属性的两种方法</strong></pre><pre name="code" class="java" style="font-size: 9pt;">1:在view类中定义,通过构造函数中引入的AttributeSet去查找XML布局的属性名称,然后找到对应引用的资源的ID去找值</pre><pre name="code" class="java" style="font-size: 9pt;">在下面的自定义了两个属性Text src布局文件package com.example.xuan.customview_demo2; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.LayoutInflater; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; /** * ClassName MyView2 * Description * Company * author youxuan E-mail:[email protected] * date createTime:2015/7/28 16:18 * version */ public class MyView2 extends LinearLayout { public MyView2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context,attrs); } public MyView2(Context context, AttributeSet attrs) { super(context, attrs); initView(context, attrs); } public MyView2(Context context) { super(context); initView(context, null); } private ImageView mImageView; private TextView mTextView; private void initView(Context context, AttributeSet attrs) { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.my_view2, this); //方式1 mImageView = (ImageView) findViewById(R.id.iv_icon); mTextView = (TextView) findViewById(R.id.tv_content); /* int srcId = attrs.getAttributeResourceValue(null, "Src", 0); if(srcId!=0) mImageView.setImageResource(srcId); int textId = attrs.getAttributeResourceValue(null, "Text", 0); if(textId!=0) mTextView.setText(textId);*/ TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.MyView2); int srcId2=array.getResourceId(R.styleable.MyView2_Src,0); mImageView.setImageResource(srcId2); int textId2=array.getResourceId(R.styleable.MyView2_Text, 0); mTextView.setText(textId2); int selectId=array.getInt(R.styleable.MyView2_Select,0); Toast.makeText(context,"id:"+selectId,Toast.LENGTH_LONG).show(); array.recycle(); } @Override public boolean isInEditMode() { // return super.isInEditMode(); return true; } }布局:<com.example.xuan.customview_demo2.MyView2 android:layout_width="match_parent" android:layout_height="100dp" app:Text="@string/app_name" app:Select="close" android:background="#6cf" app:Src="@drawable/pp"> </com.example.xuan.customview_demo2.MyView2>
自定义属性<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyView2"> <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>说明:
在使用自定义布局的时候需要添加这样的一行在布局的开头位置xmlns:myView="http://schemas.android.com/apk/res/com.xxx.myview"1、myView是自定义的一个命名空间,你可以取一个喜欢的名称。2、"http://schemas.android.com/apk/res/com.xxx.myview"这部分的字符串是由”http://schemas.android.com/apk/res/”和应用的包名”com.xxx.myview”组成。
然后在自定义View类的构造方法中读取
自定义View的常用方法
onFinishInflate()回调方法,当应用从XML加载改组件并用它构建界面之后调用的方法onMeasure() 检查View组件及其子组件的大小onLayout()当该组件需要分配其子组件的位置,大小时onSizeChange()当该组件的大小被改变时onDraw()当该组件将要绘制它的内容时onKeyDown() 当组件按下某个键盘是onKeyUp() 当松开某个键盘时onTrackballEvent当发生轨迹事件时onTouchEvent当发生触屏事件时onWindowFoucsChanged(boolean)当该组件得到,失去焦点时onAttachedToWindow()当把该组件放入到某个窗口时onDetachedFormWindow()当把该组件从某个窗口上分离onWindowVisibilityChanged(int)当包含该组件的窗口的可见性发生改变时触发的方法view的设计理念1.重用性目的为了可以在不同的模块,项目中重复使用而设计2:灵活目的自定义view可以方便的实现系统提供的控件所没有的功能,开发项目的时候灵活性大大增加3:解耦和目的由于自定义View可以方便的实现系统提供的控件所没有的功能,开发项目的时候灵活性大大增加既然有了目的,那么怎么实现就成为了以下的命题了,在这里我想跟你们谈谈设计模式在前面大家已经了解了实现一个自定义view的基本方法,但是做起来估计也是不知所措,不知大奥高如何具体实现,我把这种迷惘叫缺少指导思想这里的指导思想也就是设计模式,业务逻辑代码应该放哪?数据存储代码又放哪?只有明确了上面三个问题才算达成了自定义View的设计目的1:适配器模式对于 android 开发者来说起,适配器模式简直太熟悉不过,有很多应用可以说是天天在直接或者间接的用到适配器模式,比如 ListView 。ListView 用于显示列表数据,但是作为列表数据集合有很多形式,有 Array ,有 Cursor ,我们需要对应的适配器作为桥梁,处理相应的数据(并能形成 ListView 所需要的视图)。正是因为定义了这些适配器接口和适配器类,才能使我们的数据简单灵活而又正确的显示到了 adapterview 的实现类上。目的:适配器模式,把一个类的接口编号成客户端所期待的宁一种接口,从而根本不匹配而无法在一起工作的两个类能够在一起工作适配器模式分为类适配器模式和对象适配器模式关于类适配器模式,因为java的单继承,如果继承一个类,宁外的则只能是接口,需要手动实现相应的方法2:组合模式android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是View和ViewGroup的类的使用组合模式, Composite Pattern ,是一个非常巧妙的模式。几乎所有的面向对象系统都应用到了组合模式。
目的:将对象 View 和 ViewGroup 组合成树形结构以表示 " 部分 - 整体 " 的层次结构 (View 可以做为 ViewGroup 的一部分 ) 。组合模式使得用户对单个对象 View 和组合对象 ViewGroup 的使用具有一致性。3:MVC模式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模式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、低耦合