爲ListView添加分段標頭

在該Demo中,有一個分段標頭(section header)隨列表滾動,當前分段標頭一直顯示在屏幕頂端。在下圖中,突出顯示的字母就是分段標頭,其下方的列表項顯示首字母與分段標頭相同的國家。

這裏寫圖片描述

Android開發者通常需要創建兩種類型的列表項來達到上述效果:一個常規列表項用於顯示數據,一個特殊列表項用於顯示分段標頭。這種方式需要重寫Adapter的getViewTypeCount()方法,讓其返回2;然後修改getView()方法,在該方法中創建並返回對應類型的列表項。實踐中,這種方法會導致代碼邏輯混亂。如果原始列表包含20個列表項,使用上述方法,適配器就需要包含21到40個列表項,列表項的數目依賴於分段數目。這樣就會導致複雜的代碼邏輯:ListView中顯示的是15個可視列表項,但是該列表項在原始列表中可能是第9個列表項。

更簡單的方法是在列表項中嵌入分段標頭,然後根據需要顯示或者隱藏分段標頭。這樣便大幅度簡化了創建列表以及選擇列表項的邏輯。我們可以創建一個特殊的TextView,讓其疊加在列表的頂部,當列表滾動到一個新的分段時,就更新其內容。

創建列表佈局

我們在單獨的文件中爲分段標頭創建佈局,這樣就可以在隨列表滾動的分段標頭和列表頂部的固定分段標頭中複用這個佈局文件。源碼如下所示:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/header"
    style="@android:style/TextAppearance.Small"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#0000ff" />

然後,我們創建一個包含頂部固定分段標頭的XML佈局文件,代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <!--使用Android標準的列表ID,因此可以在ListActivity的子類中使用它。-->
    <ListView
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

    <include layout="@layout/header" />

</FrameLayout>

將分段標頭包含幀佈局中,這樣該標頭就可以與列表重疊在一起,以顯示當前所在分段。

最後要創建列表項對應的XML佈局文件。該佈局文件既包含數據項也包含分段標頭,源碼如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <include layout="@layout/header" />

    <TextView
        android:id="@+id/label"
        style="@android:style/TextAppearance.Large"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

列表項中的分段標頭會在新的分段開始時顯示,否則就會被隱藏。ID爲label的TextView用於顯示列表項中的數據項。

創建可視分段標頭

與其他創建分段列表的方式的不同之處在於:開發者只需要重寫getView()方法。我們不需要返回多種類型的視圖,也不需要在分段列表與原始列表間轉換數據項的位置(position)。源碼如下:

package com.example.huangfei.myapplication;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

/**
 * Created by huangfeihong on 2015/12/19.
 */
public class SectionAdapter extends ArrayAdapter<String> {
    private Context mContext;
    private LayoutInflater mInflater;

    public SectionAdapter(Context context, String[] objects) {
        super(context, R.layout.list_item, R.id.label, objects);
        mContext = context;
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null)
            convertView = mInflater.inflate(R.layout.list_item, parent, false);
        TextView header = (TextView) convertView.findViewById(R.id.header);
        String label = getItem(position);

        //檢查列表項起始字母是否發生改變
        if(position == 0 || getItem(position - 1).charAt(0) != label.charAt(0)){
            header.setVisibility(View.VISIBLE);
            header.setText(label.substring(0,1));
        }else{
            header.setVisibility(View.GONE);
        }

        return super.getView(position, convertView, parent);
    }
}

接下來,我們編寫一個輔助方法用於配置屏幕頂部懸浮的分段標頭,源碼如下:

  private void setTopHeader(int position){
        String text = Countries.COUNTRIES[position].substring(0, 1);
        mTopHeader.setText(text);
    }

當開始創建或者滾動列表時,會調用這個輔助方法,通過該方法找到該分段標頭對應的字母,並以此更新其文本內容。

最後一步

配置列表併爲列表設置監聽器,當列表滾動時,更新分段標頭的內容。源碼如下:

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list);

        setContentView(R.layout.list);
        mTopHeader = (TextView) findViewById(R.id.header);
        setListAdapter(new SectionAdapter(this, Countries.COUNTRIES));

        //設置滾動監聽器
        getListView().setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {

            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                if(firstVisibleItem != mTopVisiblePosition){
                    mTopVisiblePosition = firstVisibleItem;
                    setTopHeader(firstVisibleItem);
                }
            }
        });
        setTopHeader(0);//初始化第一個列表項的分段標頭
    }

概要

即便ListView並非原生支持分段標頭,但是,通過將分段標頭嵌入到列表項中,並在合適的時候令其可視或不可視,這樣,開發者仍然可以很容易的添加分段標頭。儘管這個demo適用於以字母排序的列表,但是,方法本身可以應用於任何分段類型。

代碼地址

發佈了86 篇原創文章 · 獲贊 6 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章