2011年11月8日火曜日

AndroidのListViewにもUITableViewのsectionが欲しい


AndroidのListViewには、iPhoneのUITableViewのsectionのような概念がありません。
これをAndroidのListViewで実現するためのコンポーネントを簡単に作って見ました。

public class MultiSectionAdapter extends BaseAdapter {

    /** セルがセクションヘッダーであることを表す値 */
    public final static int TYPE_SECTION_HEADER = 0;

    /** 各セクションのValue部を保持するアダプターの集合 */
    public final Map sections = new LinkedHashMap();

    /** セクションヘッダーアダプター */
    public final ArrayAdapter headers;

    public final Context context;


    public MultiSectionAdapter(Context context) {
        super();

        this.context = context;
        headers = new ArrayAdapter(context, R.layout.list_section_header);
    }

    @Override
    public int getCount() {
        // ヘッダーを除いて、セルの数を返す
        int total = 0;
        for(Adapter adapter : this.sections.values()) {
            if (adapter == null) {
                continue;
            }
            total += adapter.getCount() + 1;
        }
        return total;
    }

    @Override
    public Object getItem(int position) {
        for(Object section : this.sections.keySet()) {
            Adapter adapter = sections.get(section);
            int size = adapter.getCount() + 1;

            if(position == 0) return section;
            if(position < size) return adapter.getItem(position - 1);

            position -= size;
        }
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getViewTypeCount() {
        int total = 1;
        for(Adapter adapter : this.sections.values()) {
            if (adapter == null) {
                continue;
            }

            total += adapter.getViewTypeCount();
        }
        return total;
    }

    @Override
    public int getItemViewType(int position) {
        int type = 1;
        for(Object section : this.sections.keySet()) {
            Adapter adapter = sections.get(section);
            int size = adapter.getCount() + 1;

            if(position == 0) return TYPE_SECTION_HEADER;
            if(position < size) return type + adapter.getItemViewType(position - 1);

            position -= size;
            type += adapter.getViewTypeCount();
        }
        return -1;
    }

    @Override
    public boolean isEnabled(int position) {
        return (getItemViewType(position) != TYPE_SECTION_HEADER);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        int sectionNum = 0;
        for(Object section : this.sections.keySet()) {
            Adapter adapter = sections.get(section);
            int size = adapter.getCount() + 1;

            if(position == 0) return headers.getView(sectionNum, null, parent);
            if(position < size) return adapter.getView(position - 1, convertView, parent);

            position -= size;
            sectionNum++;
        }
        return null;
    }

    /**
     * セクションヘッダーと対となるアダプターをセットする
     *
     * @param section セクションヘッダー
     * @param adapter セクションアダプター
     */
    public void addSection(String section, Adapter adapter) {
        this.headers.add(section);
        this.sections.put(section, adapter);
    }
}


そして色々と端折ってますが、このAdapterは以下のように利用します。

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ListView list = (ListView) findViewById(R.id.listView);

        // MultiSectionAdapterに追加するセクション その1
        @SuppressWarnings("serial")
        List names1 = new ArrayList(){{add("相田"); add("石田"); add("上田");}};
        ArrayAdapter adapter1 = new ArrayAdapter(this, android.R.layout.simple_list_item_1, names1);

        // MultiSectionAdapterに追加するセクション その2
        @SuppressWarnings("serial")
        List names2 = new ArrayList(){{add("加藤"); add("木村"); add("久保");}};
        ArrayAdapter adapter2 = new ArrayAdapter(this, android.R.layout.simple_list_item_1, names2);

        // ヘッダー部のラベルと、対応するAdapterを渡す
        UserAdapter adapter = new UserAdapter(this);
        adapter.addSection("ア行", adapter1);
        adapter.addSection("カ行", adapter2);

        list.setAdapter(adapter);
    }
}

class UserAdapter extends MultiSectionAdapter {
    public UserAdapter(Context context) {
        super(context);
    }
}

  • ヘッダー部に任意のViewを指定できない
  • UITableViewと比較して、footer部がない

などなど、幾つかの不足・問題点はあるのですが、
現実の要件としてはこれで十分事足りるのではないかと思います。

0 件のコメント:

コメントを投稿