Android自定义View的探讨

自定义控件的优势很明显,这篇文章为自己总结


自定义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"
1myView是自定义的一个命名空间,你可以取一个喜欢的名称。
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自定义View的探讨_第1张图片
   
   
   
   
对于 android 开发者来说起,适配器模式简直太熟悉不过,有很多应用可以说是天天在直接或者间接的用到适配器模式,比如 ListView
ListView 用于显示列表数据,但是作为列表数据集合有很多形式,有 Array ,有 Cursor ,我们需要对应的适配器作为桥梁,处理相应的数据(并能形成 ListView 所需要的视图)。
正是因为定义了这些适配器接口和适配器类,才能使我们的数据简单灵活而又正确的显示到了 adapterview 的实现类上。
目的:适配器模式,把一个类的接口编号成客户端所期待的宁一种接口,从而根本不匹配而无法在一起工作的两个类能够在一起工作
适配器模式分为类适配器模式和对象适配器模式
关于类适配器模式,因为java的单继承,如果继承一个类,宁外的则只能是接口,需要手动实现相应的方法
2:组合模式
Android自定义View的探讨_第2张图片
android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是View和ViewGroup的类的使用
   
   
   
   
组合模式, Composite Pattern ,是一个非常巧妙的模式。几乎所有的面向对象系统都应用到了组合模式。

目的:
将对象 View ViewGroup 组合成树形结构以表示 " 部分 - 整体 " 的层次结构 (View 可以做为 ViewGroup 的一部分 )
组合模式使得用户对单个对象 View 和组合对象 ViewGroup 的使用具有一致性。
3:MVC模式
Android自定义View的探讨_第3张图片
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模式
Android自定义View的探讨_第4张图片
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、低耦合

你可能感兴趣的:(android,view)