Android自定义适配器---实现简单文件管理器

时间:2021-01-19 21:08:40

一、适配器Adapter

        现实生活中的适配器就是一种“转化器”,将两个不兼容的事物做一个连接。Android在视图显示和后台数据上使用适配器,顾名思义,就是把一些数据给变得适当,适合以便于在View上显示。可以看作是界面数据绑定的一种理解。它所操纵的数据一般都是一些比较复杂的数据,如数组,链表,数据库,集合等。

        适配器在视图与数据之间扮演了一个桥梁的作用,它将数据中的每一项数据转化为适配器视图可以使用的每一个视图项。

适配器类图:

Android自定义适配器---实现简单文件管理器

        该图是关于适配器Adapter的类图,可以看到BaseAdapter抽象类实现了ListAdapterSpinnerAdapter这两个接口,在我们自定义的适配器中,就是要继承BaseAdapter这个类。它有3个具体子类,分别是:ArrayAdapterSimpleAdapter和SimpleCursorAdapter这也是系统定义好、我们可以直接使用的适配器。

        在开发中,我们经常使用到ListView这个控件。Android的API也提供了许多创建ListView适配器的多种方式。例如ArrayAdapter、SimpleAdapter和SimpleCursorAdapter等。但你是否发现,如果采用这些系统自带的适配器,对于事件的响应只能局限在一个行单位。假设一行里面有一个按钮和一个图片控件,它们之间的响应操作是不一样的。若采用系统自带的适配器,就不能精确到每个控件的响应事件。这时,我们一般采取自定义适配器来实现这个比较精确地请求。

        所以我们在实际开发中如果需要实现各种各样的适配器样式,则需要自定义适合自己的适配器,也就是说需要继承BaseAdapter。而如果获取的json数据是千变万化的,可以使用android代码进行方便的调用相应的数据,就可以达到图文混排等好看的样式效果,具体的使用方法如下。

二、BaseAdapter类中的重要方法

BaseAdapter类是一个抽象类,实现了ListAdapterSpinnerAdapter这两个接口,在我们自定义的适配器中,就是要继承BaseAdapter这个类。类中的重要方法如下:

public int getCount() //得到数据的行数

public Object getItem(intposition) //根据position得到某一行的记录

public long getItemId(int  position) //的到某一条记录的ID

publicView getView(int position,View convertView, ViewGroup parent)

//相比于其它几个方法这个方法是最重要的,它显式的定义了适配器将要以什么样的方式去显示我们所填充的数据,在自定义的适配器里面我们通常会给它写个布局文件。第一个参数position指定位置,即要获得哪一个数据项的View,第二个参数convertView可复用的视图项,第三个参数指定父元素视图组。

三、自定义适配器

通过一个完整实例,逐步探讨自定义适配器的实现过程。实现的功能如下图,类似于一个文件管理器,一页屏幕上显示了七八个文件项:

Android自定义适配器---实现简单文件管理器

实例分析

(1)主活动的布局文件里其实就是一个ListView,ListView里面每一行都是一个“自定义控件集”列表项。这个“自定义控件集”列表项其实就是一个模板,模板显示样式见下图,这个模板定义了将要以什么样的方式去显示我们所填充的数据。它由多个控件构成:最左边是一个ImageView用于显示文件图标,旁边有两个TextView用于显示文件信息,最右边是一个ImageButton用于实现对文件操作事件处理。适配器将“视图”和“数据”做关联,将SD卡外部存储读取到的一个个文件[数据],按照此布局文件指定的样式显示出来。

注意:一个文件[数据]对应一个视图项[就是这个模板],让所有文件都按此样式显示;文件可能有千万条,但一页屏幕只能显示七八个视图项;在 ListView 上下滚动时,可以重复使用消失的列表项装载新的数据出现的底部或顶部,所以这里可做性能优化,后面细讲。

Android自定义适配器---实现简单文件管理器

该模板对应的布局文件需要自己新建,命名为file_item.xml

代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   
android:layout_width="match_parent"
   
android:layout_height="wrap_content"
   
android:paddingBottom="8dp"
   
android:paddingTop="8dp"
   
android:paddingLeft="16dp"
   
android:paddingRight="16dp"
   
android:descendantFocusability="blocksDescendants"
>

    <ImageView
       
android:layout_width="48dp"
       
android:layout_height="48dp"
       
android:id="@+id/imageView_icon"
       
android:layout_centerVertical="true"
       
android:layout_alignParentLeft="true"
       
android:layout_alignParentStart="true"
       
android:src="@drawable/ic_folder"
/>

    <TextView
       
android:layout_width="wrap_content"
       
android:layout_height="wrap_content"
       
android:text="文件名称"
       
android:id="@+id/textView_name"
       
android:layout_alignTop="@+id/imageView_icon"
       
android:layout_toRightOf="@+id/imageView_icon"
       
android:layout_toEndOf="@+id/imageView_icon"
       
android:layout_marginLeft="16dp"
       
android:layout_marginStart="16dp"
       
android:textSize="18sp"
/>

    <TextView
       
android:layout_width="wrap_content"
       
android:layout_height="wrap_content"
       
android:text="文件类型及大小"
       
android:id="@+id/textView_info"
       
android:layout_alignBottom="@+id/imageView_icon"
       
android:layout_alignLeft="@+id/textView_name"
       
android:layout_alignStart="@+id/textView_name"
       
android:textSize="12sp"
/>

    <ImageButton
       
android:layout_width="wrap_content"
       
android:layout_height="wrap_content"
       
android:id="@+id/imageButton_action"
       
android:layout_centerVertical="true"
       
android:layout_alignParentRight="true"
       
android:layout_alignParentEnd="true"
       
android:src="@drawable/ic_more_vert_black_24dp"
       
style="@android:style/Widget.DeviceDefault.Button.Borderless.Small"
/>

</RelativeLayout>

(2)数据都是从手机SD卡外部存储读取到的,使用自定义的FileAdapter(extends BaseAdapter)文件适配器将“视图”和“数据”做关联。从手机SD卡外部存储读取数据,需要权限设定,在清单文件中加入2行代码(加在<application 标签上面):

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
自定义适配器

1.继承BaseAdapter

        新建一个Java类FileAdapter进程,继承BaseAdapter类,重写如下四个方法及构造方法。

Android自定义适配器---实现简单文件管理器

2.所有方法的具体实现

Android自定义适配器---实现简单文件管理器

Android自定义适配器---实现简单文件管理器

Android自定义适配器---实现简单文件管理器

Android自定义适配器---实现简单文件管理器

3.创建视图项

        重写的4个方法中getView()最重要,也最复杂。要让“数据”按照我们自己定义的“模板”显示,就需要加载我们定义的布局文件(file_item.xml)。

        在FileAdapter类中定义一个LayoutInflater  inflater布局加载对象,用于加载和解析XML布局文件,并在FileAdapter的构造方法中对其初始化(具体代码实现上面有)。

                convertView = inflater.inflate(R.layout.file_item,parent,fause);这行使用布局加载对象,加载我们定义的视图模板file_item.xml,使数据按照模板指定样式显示。getView()方法实现如下:

 Android自定义适配器---实现简单文件管理器

4.存在的问题

        ListView 中的数据可能有成千上万条,每条的数据的显示都会调用 getView() 方法获得一个视图项:使用 LayoutInflater 实例化 XML 布局文件[视图项模板]及通过findViewById 查询获得控件引用,这两个操作的性能开销很大;而当视图项[就是我们定义的视图模板]从底部或顶部消失后(没有重用),又需要不停调用 GC 回收视图项。

四、解决方案----使用ViewHolder 模式

1.再看convertView参数

        ListView 中的数据可能有成千上万条,但显示在屏幕上的条目(列表项)是非常有限的,一般也就七八个;在ListView 上下滚动时,可以重复使用消失的列表项装载新的数据出现的底部或顶部;getView() 方法中的 convertView 参数就是可重复使用的列表项(file_item.xml 文件的实例)。

2.第一步:利用convertView 参数

        convertView 参数为null时才加载新的视图项,按视图项模板指定样式显示。不为null时,说明有可复用的视图项,上下滚动时,可以重复使用消失的列表项装载新的数据。

Android自定义适配器---实现简单文件管理器

3.第二步:使用---视图的 tag

View 类有两个方法:

方法

描述

public void setTag(Object tag)

给视图绑定一个对象(数据、视图的结构)

public Object getTag()

获得与视图关联的对象(数据、视图的结构)

4.列表项模版

        就是最前面新建的file_item.xml文件。

Android自定义适配器---实现简单文件管理器

5.ViewHolder 类

        因为file_item.xml布局文件[视图项模板]指定了每一个数据[文件]要显示的样式,convertView 参数为null时,就加载该布局文件,且通过findViewById()方法获得该布局文件中声明控件的引用,并在事件处理中,改变这些控件的属性。但前面说过,该布局文件[视图项模板]是一个“自定义控件集”,它包含多个控件,所以这里使用ViewHolder 类将他们封装起来,统一处理。

Android自定义适配器---实现简单文件管理器

6.在getView()方法中使用 ViewHolder

Android自定义适配器---实现简单文件管理器

7.在 ViewHolder 中加载数据

Android自定义适配器---实现简单文件管理器

此处演示的代码,是使用通过ViewHolder类名直接访问字段属性,然后通过findViewById()方法获得布局文件中声明控件的引用,其实下面的方法更通用一些。

/**
 *
创建视图项
 
*
 * @param
i             位置
 
* @param
convertView   可复用的视图,可能 null (需要新建)
 * @param
viewGroup     适配器视图
 
* @return
 
*/
@Override
public View getView(
        int i,
        View convertView,
        ViewGroup viewGroup) {

    ViewHolder holder;

    if (convertView == null) {
        // 没有可复用,需要创建
       
// 开销很大 加载文件、XML 解析 控件和布局的
       
convertView = layoutInflater.inflate(R.layout.file_item, null);

        // 每个视图项需要一个 viewHolder
        //
构造ViewHolderView convertView传给它
       
holder = new ViewHolder(convertView);

        ……………………………..
    return convertView;
}

/**
 * ViewHolder
模式
 
*/
static class ViewHolder {

   ImageView icon;
    TextView title;
    TextView info;
    ImageButton action;

    /**
     *
构造方法
    
* 得到View v   通过findViewById获得布局引用
    
* @param
v
    
*/
   
public ViewHolder(View v) {
        icon = (ImageView) v.findViewById(R.id.imageView_icon);
        title = (TextView) v.findViewById(R.id.textView_name);
        info = (TextView) v.findViewById(R.id.textView_info);
        action = (ImageButton) v.findViewById(R.id.imageButton_action);
    }
       ……………………………….
    }

8.业务功能实现---列表项中的监听器

定义监听器

Android自定义适配器---实现简单文件管理器

创建监听器

Android自定义适配器---实现简单文件管理器

设置监听器(或重置数据)

Android自定义适配器---实现简单文件管理器

点击事件(弹出菜单)

Android自定义适配器---实现简单文件管理器

定义弹出菜单

PopupMenu 菜单res/menu/popup.xml

Android自定义适配器---实现简单文件管理器

 

完整工程:https://github.com/ljheee/FilesAdapter

文章推荐---适配器视图与适配器AdapterView& Adapter

http://blog.csdn.net/ljheee/article/details/52302591