`
终端前的我
  • 浏览: 724 次
  • 性别: Icon_minigender_1
  • 来自: 成都
最近访客 更多访客>>
社区版块
存档分类
最新评论

Adapter软件设计模式在Android的应用

阅读更多




Adapter软件设计模式在Android中的应用












目  录


1.本文摘要 3
2.界面体验 4
2.1.ListView显示声音文件列表 4
2.2.Grid显示图片列表 5
2.3.Gallery显示图片 6
3.界面元素的分解 6
4.Adapter的由来 8
5.Adapter接口 9
6.ArrayAdapter 9
6.1.成员变量 9
6.2.函数getCount 10
6.3.函数getItem 10
6.4.函数getView 10
7.Adapter类图 11
8.Activity和Adapter的关系 12
8.1.ListActivity 12
8.2.GridView 13
9.ListView,GridView和Gallery开发步骤 14
10.开发中的一个小技巧 15
11.示例代码 15
11.1.继承Activity定义自己的Activity 15
11.2.使用XML定义自己的Activity的Layout 16
11.3.使用XML定义自己的子Item的视图 16
11.4.实现自定义Adapter。一般基类是BaseAdapter 17
11.5.在onCreate函数中获取GridView实例,设置好自己定义的Adapter 18
12.总结 18

1. 本文摘要
在Android虚拟项目中,我做的是媒体播放器部分。里面有一个功能就是需要让用户选择一个文件如mp3,然后进行播放。也就是说需要以列表的方式显示媒体列表让用户进行选择。这样就用到了ListActivity。同时对于图片浏览,开始的想法是操作上和PC上一致,也就是以缩略图的方式供用户选择,然后使用ImageView显示选择的图片。这样就用到了GridView。不过Android对图片似乎有更好的诠释,那就是Gallery。翻译成中文就是“长廊”。有点象是画展中的“画廊”的概念,非常的形象。
在对ListActivity,ListView,Grid,Gallery的学习和使用中,我发现Android广泛的利用了Adapter软件设计模式。通过这种设计模式,可以非常方便的开发出非常人性话的界面,同时对于数据,显示以及显示的维护做到了非常好的分离,非常的符合软件设计模式中数据和显示分离,低耦合度的思想。
本文通过实际的例子,一方面是希望开发人员不仅能体会到Android开发人性化界面的方便,另一方面是希望开发人员在开发ListView,Grid,Gallery的时候能知道需要实现哪些类,哪些函数,同时也能知道为什么需要实现类和函数。再者就是再次体会Adapter软件设计模式的优点,能对以后的软件开发设计起到一定的帮助和指导。
2. 界面体验
2.1. ListView显示声音文件列表
声音列表的显示方式可以多样的。可以只是显示声音文件名,也就是只有一个TextView,也可以显示成如上左图的显示一个声音图片和文件名,点击每个Item的时候触发事件。也可以如上右图显示图片,文件名,歌唱家,声音的时长,同时显示一个按钮,当用户点击按钮的时候触发相关事件。
2.2. Grid显示图片列表

上面的左图是Android开机的界面,他显示了当前所有注册为LAUNCHER的应用。可以看到在一个网格中的每一个Item不仅显示了一个图片,还包括文字。
上面的右图是仿照左图所做的一个示例程序。同样是显示一个图片和相关的问题。把他列出来是想说我们自己也能实现这种看似复杂的界面。
2.3. Gallery显示图片

这个界面由两部分组成。一个是上面的Gallery,显示的是所有的图片的一个列表。下面是ImageView。当选择Gallery中的一个图片时,ImageView会显示其大的图片,这样来实现图片的浏览。下面的ImageView不是本文的重点,本文的重点是上面的Gallery。下面会做一些详细的探讨。
3. 界面元素的分解
下图是对ListActivity的一个界面分解。可以看到最大的一个容器是Activity。其包括一个TextView显示字符串XBtnList,一个ListView显示了文件的详细列表。其中ListView又是一个Container,他包括了一个子View。这个子View我们可以自己定制。这里包括一个ImageView,三个TextView和一个ImageButton。
上面的分析我们可以这样理解。Activity负责其子View的显示。也就是XBtnList字符串的显示和ListView的显示。ListView负责其每个Item在ListView中的显示,但具体到每个Item视图如何显示,ListView并不负责。每个子Item试图的显示是由他自己来负责。也就是ImageView显示什么样的图片,三个TextView显示什么样的文本,字体,颜色,ImageButton显示什么样的图片,以及这些View在子Item视图中的显示位置,内容由子Item试图来实现。
对Grid做同样的分析,具有同样的结果。也就是Activity负责子视图即Grid视图的在Activity中的显示。而Grid中具体显示什么样的元素由子包含的子View来负责。在上面的例子中每个子View实际上包括一个ImageView和一个TextView。
同样的对于Gallery。也是由Gallery来负责其每个子View的显示。子view负责自己的显示。可以只包含一个ImageView,也可以是只有一个TextView,还可以是一个ImageView和一个TextView的组合。
4. Adapter的由来
对于ListView,Grid和Gallery来说,其显示的是一个列表,也就是多个数据。那么这些数据从哪里来呢,数据和ListView是怎么关联起来的呢? 
一般来说,数据可以来自一个数组,一个List,或者数据库中的游标。这些是程序开发中用到的非常多的数据集合的表示方式。但是ListView和数据并没有直接的关系,主要的原因是数据可以来自不同的方式,ListView需要的数据可以是数据中的一个子集,另一个原因是ListView还需要显示每个子View。而子View如何显示数据是不负责的。否则就有违数据和显示分离的思想。
这样,我们就必须在数据和ListView中间加入一个第三者来关联数据和ListView,不仅能获取到数据,而且能把获取到的数据以自己定制的方式呈现出来,并把这个呈现转交给ListView来进行显示。
这个第三者就是Adapter,设计模式就是Adapter设计模式。Adapter的作用是把一些不兼容的,不能直接访问的适配成能兼容的,能访问的。比如说第三方提供了一个类,这个类的很多功能都是我们自己需要的,但是不全面,而且函数定义还有重复。那么可以使用类级别上的Adpater,也就是包含开发一个自己的类,其包含第三方类的一个实例,内部实现通过第三方类来代理。也可以继承上实现Adapter。
Android的很多Adapter其实就是类级别上的适配。我们下面会看一下ArrayAdapter类的实现。就会发现他就是一个类级别上的适配。
5. Adapter接口
前面讲过,Adapter能把数据适配成ListView能访问的形式。下面我们看看我们需要实现哪些函数。
public interface Adapter
{ . . .
    public abstract int getCount();

    public abstract Object getItem(int i);

    public abstract View getView(int i, View view, ViewGroup vg)
}
这是Adapter的定义的主要接口。也就是说我们自己的Adapter需要实现getCount函数来返回Item的个数,getItem来返回指定Item的数据,getView来返回一个数据初始化的子视图给ListView。
从上面的接口我们似乎可以看到,我们前面的分析是对的。也就是Adapter本身不维护数据,数据保存在数据存储区中如Array。但是Adapter适配了数据,如getCount返回数据的个数,getItem返回指定的数据。同时Adapter还维护数据的显示,也就是Item子视图的显示,表现就是需要返回一个View给ListView。
我们可以通过Android的类ArrayAdapter来看看。
6. ArrayAdapter
6.1. 成员变量
下图是ArrayAdapter的主要成员变量的定义图。我们可以清楚的看到其包括一个类型为ArrayList的原始数据mOriginalValues。还包括一个List数据类型的mObjects。也就是ArrayAdapter适配了ArrayList。很明显这是一个类级别上的适配。这里还有一个mObjects的列表主要还实现了数据的过滤。这个不在这里讨论。
第二就是大家还可以看到还包括一个类型为LayoutInflater的成员变量mInflater。这个就是用来从xml文件中建立View的一个类。也就是通过他可以建立View的实例返回给ListView。

6.2. 函数getCount
public int getCount(){
        return mObjects.size();
}
可以看到他返回了mObjects的节点个数。
6.3. 函数getItem
    public Object getItem(int position)
    {
        return mObjects.get(position);
}
可以看到他返回了指定节点的数据。
6.4. 函数getView
public View getView( int position,
View convertView,
ViewGroup parent)
    {
        return createViewFromResource(position, convertView,
parent, mResource);
    }

private View createViewFromResource(int position,
View convertView, ViewGroup parent,
int resource)
    {
        View view;
        if(convertView == null)
            view = mInflater.inflate(resource, parent, false);
        else
            view = convertView;
        TextView text;
        try
        {
            if(mFieldId == 0)
                text = (TextView)view;
            else
                text = (TextView)view.findViewById(mFieldId);
        }
        catch(ClassCastException e)
        {
            Log.e("ArrayAdapter", "You must  . . . ID for a TextView");
            throw new IllegalStateException("ArrayAdapter . . .", e);
        }
        text.setText(getItem(position).toString());
        return view;
}
可以清楚的看到,函数调用了LayoutInflater类从xml资源中创建一个视图,并获取视图中的子控件TextView,然后初始化TextView,也就是设置显示文本。最后返回这个View。
从这里我们也可以看到,Adapter维护了子Item视图的显示。
7. Adapter类图
Adapter类包含很大的一个家族。从下面的类图就可以看到其复杂程度。不过还好,一般来说我们只要使用其子类如ArrayAdapter,SimpleCursorAdapter。对于比较复杂一点的,如上面显示文件列表的ListView,就得实现BaseAdapter了。后面会由详细的例子。

8. Activity和Adapter的关系
其实到现在,基本上我们就可以知道Activity和Adapter之间的关系。Activity是一个容器窗口,ListView,Grid或者是Gallery都是一个一个的控件,不过这些控件也是容器,可以包含其他的子Item视图。而这些子视图是由Adapter来维护的。这就是Adapter和Activity之间的关系。非常松散的一个关系。可以看看下面的类图就知道了。
8.1. ListActivity
一个ListActivity其实就是一个Activity。不过他包含一个ListView。由ListView来维护List列表的显示。其包含ListAdapter,也就是Adapter。由Adapter来维护子Item视图的显示。不过在这里需要注意的是,ListActivity的ListView的Id是固定死的。你也可以自己定义自己的Activity,然后包含自己的ListView。

8.2. GridView
Activity包含GridView,Id不是象ListActivity一样固定死的,可以自己定义。然后GridView会维护自己的Adapter,Adapter维护子Item视图的显示。

9. ListView,GridView和Gallery开发步骤
 继承Activity定义自己的Activity。如果是ListView,可以是ListActivity。
 使用XML定义自己的Activity的Layout。其中可以包括ListView,GridView,Gallery。
 使用XML定义自己的子Item的视图。也可以不用XML来定义子视图,使用代码也可以。比如说就一个ImageView,那么就没有必要使用XML了。
 实现自定义Adapter。基类一般是BaseAdapter。在这个类中必须实现函数getCount,getItem,getPosition和getView函数。一般来说,数据也在这个类中初始化。可以仿照ArrayAdapter来实现。
 在Activity的onCreate函数中获取ListView,GridView或者Gallery的实例,然后设置好自己定义的Adapter。
10. 开发中的一个小技巧
一般来说ListView,GridView和Gallery只要实现点击事件就可以了,在这个callback函数中会返回点击item的序列号。根据序列号就可以获取相应的数据。但是如果存在Button或者ImageButton。点击事件不再会激发。反而会激发Button的OnClickListener事件。但是在OnClickListener回调函数中不会有Item的序列号,所以我们没有办法获取点击按钮对应的数据。怎么办?
这里需要提到View的一个函数叫getTag和setTag。这个函数使得控件可以跟随一个自定义的数据。也就是在初始化的时候调用setTag来设置控件的跟随数据,然后在需要的时候调用getTag来获取。
比如说XBtnList,我们可以把歌曲名的TextView控件实例设置给ImageButton,在OnClickListener回调函数中调红getTag来获取textView,通过TextView的getText函数就可以获取到对应的歌曲名。
其实convertView也使用了这个技巧。大家可以看代码来体会。
11. 示例代码
存在五个示例代码,分别是XVideo,XAudio,XPictures,XBtnList和XImageGrid。界面上比较复杂的是XBtnList。比较简单的,而且不牵扯到其他技术的是XImageGrid。所以我们看这个实例。其他的基本类似。
11.1. 继承Activity定义自己的Activity
public class XImageGrid extends Activity {
GridView mGrid;
//显示图片的资源名(这里是为了简单起见)
int mImages[] = {R.drawable.img01, . . ., R.drawable.img25};
//显示的文字
String mTexts[]  = {"img01",. . . "img25"};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.xgrid);
        mGrid = (GridView) findViewById(R.id.grdGrid);
        mGrid.setAdapter(new XImageTextAdapter(this));
    }
    public class XImageTextAdapter extends BaseAdapter {
    . . .                
    }//end class XImageTextAdapter
  }
11.2. 使用XML定义自己的Activity的Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
<GridView android:id="@+id/grdGrid"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
    android:verticalSpacing="10dp"
    android:horizontalSpacing="10dp"
    android:numColumns="auto_fit"
    android:columnWidth="64dp"
    android:stretchMode="columnWidth"
    android:gravity="center"/>
</LinearLayout>
11.3. 使用XML定义自己的子Item的视图
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity = "center" >
   
<ImageView
android:id="@+id/imgGrid"
android:layout_width="64dip"
android:layout_height="64dip"
android:scaleType = "fitCenter"/>

<TextView
android:id="@+id/txtGridName"
android:layout_width="64dip"
android:layout_height="24dip"
android:text = "00:03:00:00"
android:gravity = "center" />

</LinearLayout>
11.4. 实现自定义Adapter。一般基类是BaseAdapter
public class XImageTextAdapter extends BaseAdapter {
    protected Context mContext;
    protected LayoutInflater mInflater;
        public XImageTextAdapter(Context context) {
        this.mContext = context;
        mInflater = LayoutInflater.from(mContext);
        }
        public View getView( int position,
View convertView,
ViewGroup parent) {

        XImageTextData xdata = null;
            if (convertView == null) {
            convertView = mInflater.inflate(R.layout.xgrid_view,
null);
            xdata = new XImageTextData();
            xdata.mImageView =
(ImageView)convertView.findViewById(R.id.imgGrid);
            xdata.mTextView =
(TextView)convertView.findViewById(R.id.txtGridName);
            convertView.setTag(xdata);
            } else {
            xdata = (XImageTextData)convertView.getTag();
            }
           
   //初始话Image的显示图片
            xdata.mImageView.setImageResource( mImages[position] );
  //初始话TextView的显示字符串
            xdata.mTextView.setText( mTexts[position] );

            return convertView;
        }
        public final int getCount() {
            return mImages.length;//return count
        }
        public final Object getItem(int position) {
            return mImages[position];
        }
        public final long getItemId(int position) {
            return position;
}
protected class XImageTextData{
        ImageView mImageView;
       TextView  mTextView;
}
}
11.5. 在onCreate函数中获取GridView实例,设置好自己定义的Adapter
mGrid = (GridView) findViewById(R.id.grdGrid);
     mGrid.setAdapter(new XImageTextAdapter(this));
12. 总结
Android提供了非常好的设计模式让开发者可以非常方面的开发出非常复杂的界面。在ListView,GridView和Gallery方面,只要实现好自己的Adapter就可以了。而Adapter的实现主要是实现getCount,getItem和getView函数。getCount返回数据的节点个数,getItem返回指定节点的数据,getView返回子Item视图的显示。
[color=violet][/color]
分享到:
评论

相关推荐

    Android 适配器模式应用及设计原理

    适配器模式是一种重要的设计模式,在 Android 中得到了广泛的应用。适配器类似于现实世界里面的插头,通过适配器,我们可以将分属于不同类的两种不同类型的数据整合起来,而不必去根据某一需要增加或者修改类里面的...

    台湾高焕堂Android讲义

    10.布局(Layout)在Android软件开发上的重要角色.doc 11.Android的类别继承与委托之范例.doc 12.认识Android的UID(Unix user ID)与权限.doc 13.Android的IBinder接口及其安全性机制.doc 14.Activity之间的Inter-...

    Android UI组件实例集合

    1、Android显示GIF动画 GifView GifView 是一个为了解决android中现在...它被设计成通过一个API就能够很方便使用所有版本的Android操作栏的设计模式。 20、JakeWharton-Android-ViewPagerIndicator 界面切换的效果。

    深入理解Android中的建造者模式

    在Android开发过程中,我发现很多安卓源代码里应用了设计模式,比较常用的有适配器模式(各种adapter),建造者模式(Alert Dialog的构建)等等。虽然我们对大多数设计模式都有所了解,但是在应用设计模式的这个方面,...

    高焕堂关于Android讲义

    10.布局(Layout)在Android软件开发上的重要角色 11.Android的类别继承与委托之范例 12.认识Android的UID(Unix user ID)与权限 13.Android的IBinder接口及其安全性机制 14.Activity之间的Inter-process沟通 15....

    Android开发案例驱动教程 配套代码

    11.6.2 在不同的应用中调用Content Provider 277 11.6.3 重构Content Provider调用 278 本章小结 281 第12章 多媒体 282 12.1 多媒体文件介绍 282 12.1.1 音频多媒体文件介绍 282 12.1.2 视频多媒体文件介绍 ...

    Google Android SDK开发范例大全(PDF高清完整版1)(4-1)

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Google Android SDK开发范例大全(PDF完整版4)(4-4)

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    精通ANDROID 3(中文版)1/2

    2.5 剖析Android应用程序的结构  2.6 分析Notepad应用程序  2.6.1 加载和运行Notepad应用程序  2.6.2 分解应用程序  2.7 了解应用程序生命周期  2.8 调试应用程序  2.8.1 启动模拟器  2.8.2 ...

    Google Android SDK开发范例大全(PDF高清完整版3)(4-3)

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Google Android SDK开发范例大全的目录

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Google Android SDK开发范例大全(完整版附部分源码).pdf

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable...

    Google Android SDK开发范例大全(完整版)

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    精通Android 3 (中文版)2/2

    2.5 剖析Android应用程序的结构  2.6 分析Notepad应用程序  2.6.1 加载和运行Notepad应用程序  2.6.2 分解应用程序  2.7 了解应用程序生命周期  2.8 调试应用程序  2.8.1 启动模拟器  2.8.2 ...

    Google Android SDK 开发范例大全01

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Google Android SDK 开发范例大全02

    2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义颜色...

    Google+Android+SDK开发范例大全

    ) 2.3 Android应用程序架构——从此开始 2.4 可视化的界面开发工具 2.5 部署应用程序到Android手机 第3章 用户人机界面 3.1 更改与显示文字标签——TextView标签的使用 3.2 更改手机窗口画面底色——drawable定义...

Global site tag (gtag.js) - Google Analytics