frank.sunny的个人技术空间
在互联网行业,实践总是走在理论的前面

Android 自定义组合控件小结

引言

接触Android UI开发的这段时间以来,对自定义组合控件有了一定的了解,为此小结一下,本文小结内容主要讨论的是如何使用Android SDK提供的布局和控件组成一个功能完整组合控件并将其封装为面向对象的类,而并非讨论如何继承自SDK提供的控件类(比如TextView),对其进行自定义扩展的问题。

进入正题前,我们先来看一组功能需求

clip_image002 clip_image004 clip_image006

假设在手机需求上,那么如上三个界面我们可以使用三个Activity,每个Activity一个布局文件,实现起来比较独立,但是假设在Android pad上要求如上三个界面在一个对话框上实现,而且切换过程中要有渐变动画,那么该如何实现呢?我的方案是通过使用一个浮动的Activity外加ViewFlipper来实现。虽然我们可以在这个浮动的Activity类内能够获取到每个界面的控件并添加相应的操作,但是如果全部放在一个Activity类内,会使这个类内代码量很大,代码走读起来也不容易,而且也少了些OO的概念,为此可以考虑将每一个界面用一个自定义的组合控件类来实现。同理,假设上面的第一个界面里面关于微博绑定的列表界面,我们可以考虑使用ListView控件外加一个自定义扩展的Adapter,其中的每一项,其实也是一个组合控件,需要我们在自定义的Adapter中创建出来。为了尝试自定义组合控件的使用,对于微博列表我也没有使用ListView来实现,而是采用了基础的addView方式。

自定义组合控件的步骤

下面具体分步骤来说明自定义组合控件的实现和封装:

新建并编写组合控件的布局

这一步和其它新建和编写布局资源的方法一样,可以使用拖动控件,也可以使用代码输入,亦可以通过其它第三方开发工具编写,总之,结果是要得到一个满足需求的布局,为了后面行文的方便,我就取上面第二个界面布局account_info_preview.xml中的部分代码来说明下

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:background="@drawable/login_view_back">

<RelativeLayout

android:layout_height="@dimen/content_top_land_height"

android:layout_width="fill_parent"

android:padding="@dimen/login_title_padding"

android:background="@drawable/login_title_back">

……

</RelativeLayout>

</LinearLayout>

这一步的实现要求就是通过所见即所得的原理,使得编辑完后布局在Graphical Layout界面看到的效果就是你想要实现的界面的静态效果就可以了。

新建一个组合控件的自定义类

上面的account_info_preview.xml布局文件创建好之后,就可以为这个组合控件的布局创建一个你需要的自定义类了,假设这个类被命名为AccountInfoPreView类,那么通过eclipse先创建该类,该类的父类就是组合控件根元素对应的类,这里是LinearLayout,所以这个AccountInfoPreView就应该派生自LinearLayout。

LinearLayout有两个构造函数,在AccountInfoPreView类中我们必须重写父类的构造函数,由于在使用中,我们不采用直接new AccountInfoPreView(Context context)方式创建该组合控件,所以我们只需要重载一个public AccountInfoPreView(Context context, AttributeSet attrs)构造函数就可以了,这个就看你自定义的组合控件的需求了。

另外为了在自定义类中进行对控件对象的实例化,我们需要重写protected void onFinishInflate()函数,以实现资源id和控件对象的一一对应。下面以贴出代码为例

package com.netease.pris.hd.view;

……

public class AccountInfoPreView extends LinearLayout

{

TextView mViewTitle;

ImageView mHeader;

public AccountInfoPreView(Context context, AttributeSet attrs)

{

super(context, attrs);

// TODO Auto-generated constructor stub

}

@Override

protected void onFinishInflate()

{

mViewTitle =(TextView)findViewById(R.id.aip_title_text);

mHeader = (ImageView)findViewById(R.id.aip_header_imgae);

}

……

}

如上就为自定义控件类搭好了框架,具体需要进行何种操作,就往里面写操作就可以了,比如添加控件点击事件等等。

修改组合控件布局根元素为自定义类

自定义组合控件的类有了,自定义组合控件的布局也有了,虽然类内也用findViewById对组合控件内部的控件进行了一一实例化,但是整个组合控件类和布局还没有建立起关联关系,这一步就通过用自定义类的包名+类名来修改自定义组合控件布局的根元素完成。我们看到我们上面的自定义类的包名为com.netease.pris.hd.view,类名为AccountInfoPreView,所以我们重新将上面的account_info_preview.xml内的LinearLayout根元素替换为AccountInfoPreView就可以了。具体如下示例

<?xml version="1.0" encoding="utf-8"?>

<com.netease.pris.hd.view.AccountInfoPreView

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:background="@drawable/login_view_back">

<RelativeLayout

android:layout_height="@dimen/content_top_land_height"

android:layout_width="fill_parent"

android:padding="@dimen/login_title_padding"

android:background="@drawable/login_title_back">

……

</RelativeLayout>

</com.netease.pris.hd.view.AccountInfoPreView>

使用自定义组合控件

好了,上面已经将创建自定义组合控件的步骤介绍完了,也创建了一个AccountInfoPreView的自定义组合控件,接下来我们就要讲解使用这个自定义组合控件了。

根据资源加载的静态和动态两种方式,使用自定义组合控件,也同样有静态和动态两种方式,其实也很简单的两种方式,跟Android的Inflate有关系。

静态加载

这里所说的静态加载,就是自定义组合控件被使用它的布局在XML资源中包含了,而这个布局又通过Activity的setConentView()函数设置进去了,那么我们就可以通过findViewById的方式来实例化自定义组合控件的对象。

比如上面创建的AccountInfoPreView自定义组合控件被Activity的setConentView函数设置的布局account_view_flipper.xml所包含,其内容如下所示:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="@dimen/login_dialog_width"

android:layout_height="@dimen/login_dialog_height">

<ViewFlipper

android:id="@+id/account_view_flippe"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<include

android:id="@+id/account_info_preview"

layout="@layout/account_info_preview" />

</ViewFlipper>

</LinearLayout>

我们就可以通过findViewById的方法来实例化自定义组合控件的对象,代码如下

AccountInfoPreView mAIPView = (AccountInfoPreView)findViewById(R.id.account_info_preview);

成功实例化了对象,至于后面对该对象的具体使用就不罗列了。

动态加载

动态加载,就是自定义组合控件在资源布局中没有包含,我们通过代码动态创建到所需要的ViewGroup中去。由于自定义组合控件,不像TextView等基础控件或自定义的不关联资源的View,所以不能通过new方式直接进行创建,需要通过Inflate方法实例化自定义组合控件对象,并将其添加到ViewGroup中去,我们可以改写下上面的布局account_view_flipper.xml文件为

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="@dimen/login_dialog_width"

android:layout_height="@dimen/login_dialog_height">

<ViewFlipper

android:id="@+id/account_view_flippe"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

</ViewFlipper>

</LinearLayout>

则在Activity的onCreate函数中我们可以如下编写代码

public void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

setContentView(R.layout.account_view_flipper);

ViewFlipper mViewFlipper =(ViewFlipper)findViewById(R.id.account_view_flippe);

AccountInfoPreView mAIPView = (AccountInfoPreView)getLayoutInflater().inflate(R.id.account_info_preview, null);

mViewFlipper.addView(mAIPView);

}

通过上面的代码,我们同样实例化了一个AccountInfoPreView控件对象,并将其动态加载到了界面上进行显示。具体操作同样根据具体需求来实现,这里就不做展开了。

关于Android自定义组合控件就小结到这里,假如文中有不正确的地方还望帮忙斧正,谢谢。

posted on 2012-01-12 13:52  frank.sunny  阅读(15703)  评论(3编辑  收藏  举报