区切りとラジオボタンがついたリストのダイアログフラグメント – Android

AlertDialog.BuilderでsetSingleChoiceItems()だと、多分、区切り(iOSで言うセクション?)の表示ができない感じだったので、あれこれ記事を参考にしつつ作ってみました。

ちょっと前のテーマに似せてsetSingleChoiceItems()で区切りを入れることが目標です。
ダイアログは、android.support.v4.app.DialogFragmentを使います。

参考

まずは、参考にしたありがたい記事!これだけあれば本記事要らないだろうという話かもだけど。

Android Tips #19 ListViewのアイテムでラジオボタン付きのカスタムレイアウトを使う

ListView:SimpleAdapter View再利用 getView

基本的にはこれを合わせた感じ。

リスト

player.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:paddingLeft="10dp"
    android:paddingRight="10dp" >

    <TextView
        android:id="@+id/display"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <RadioButton
        android:id="@+id/radio_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:clickable="false"
        android:focusable="false" />

</LinearLayout>

1行毎のレイアウトXMLです。
今回は区切りもアイテムも同じレイアウトファイルを使い、Java側でデザイン分けするので、ここでは共通箇所だけ定義するとよいかも。
参考記事にもある通り、ラジオボタンは見た目だけで機能は止めてます。

—–

PlayerView.java

public class PlayerView extends LinearLayout implements Checkable {
    private RadioButton radioButton;

    public PlayerView(Context context) {
        super(context);
        initialize();
    }

    public PlayerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    // レイアウトを追加する
    private void initialize() {
        addView(inflate(getContext(), R.layout.player, null));
        radioButton = (RadioButton) findViewById(R.id.radio_button);
    }

    @Override
    public boolean isChecked() {
        return radioButton.isChecked();
    }

    @Override
    public void setChecked(boolean checked) {
        // RadioButton の表示を切り替える
        radioButton.setChecked(checked);
    }

    @Override
    public void toggle() {
    }
}

player.xmlに敷くレイアウト。
Checkableを継承し、こいつをクリック対象にしてます。
setChecked()でXMLで機能を止めたラジオボタンの表示を切り替え。
setChecked()ではラジオボタン以外もカスタマイズ可能だそうな。

サンプルはFrameLayoutだけど、LinearLayoutにしてみたんだけど、
でもLinearLayoutが重なることになるからよくないかも!今気付いた。

—–

player_view.xml

<?xml version="1.0" encoding="utf-8"?>
<com.shg25.sample.PlayerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

PlayerView.javaをxmlレイアウトに読み込む。
※適宜、パッケージ名部分は変更してください。(Eclipse上ではエラー出ないからこういうのよくひっかかる印象)

ダイアログ

PlayersListDialog.java

public class PlayersListDialog extends DialogFragment implements Checkable {
    private static final String TAG = PlayersListDialog.class.getSimpleName();

    // レイアウトぱらむす
    private int MP = LinearLayout.LayoutParams.MATCH_PARENT;
    private int WC = LinearLayout.LayoutParams.WRAP_CONTENT;
    private LinearLayout.LayoutParams LP_MP_WC = new LinearLayout.LayoutParams(MP, WC);
    private LinearLayout.LayoutParams LP_WC_WC = new LinearLayout.LayoutParams(WC, WC);
    private LinearLayout.LayoutParams LIST_LP = new LinearLayout.LayoutParams(MP, 0, 1);
    private LinearLayout.LayoutParams BTN_LP = new LinearLayout.LayoutParams(0, WC, 1);

    private ListView listView;
    private ArrayList<HashMap<String, String>> listData;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final Dialog dialog = new Dialog(getActivity());
        Context c = dialog.getContext();

        // タイトル非表示・フルスクリーン・背景を透明にする
        dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);

        // ベース
        LinearLayout ll = new LinearLayout(c);
        ll.setBackgroundColor(Color.GRAY);
        ll.setOrientation(LinearLayout.VERTICAL);
        dialog.setContentView(ll);

        // ヘッダー
        LinearLayout headerLL = new LinearLayout(c);
        headerLL.setBackgroundColor(Color.DKGRAY);
        headerLL.setPadding(20, 20, 20, 20);
        ll.addView(headerLL, LP_MP_WC);

        ImageView ivInfo = new ImageView(c);
        ivInfo.setImageResource(android.R.drawable.ic_menu_info_details);
        headerLL.addView(ivInfo, LP_WC_WC);

        TextView tv = new TextView(c);
        tv.setText("MVPは?");
        tv.setTextSize(24.0f);
        tv.setTextColor(Color.WHITE);
        headerLL.addView(tv, LP_WC_WC);

        // --------------------------------------------------
        // リスト
        SimpleAdapter adapter = new PlayerListAdapter(c, getPlayerList(c), R.layout.player_view, null, null);

        listView = new ListView(c);
        ll.addView(listView, LIST_LP);
        listView.setAdapter(adapter);

        // 単一選択モードにする
        listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);

        // デフォルト値セット(-1で未選択)
        listView.setItemChecked(-1, true);
        listView.setSelection(-1);

        // --------------------------------------------------
        // フッター
        LinearLayout footerLL = new LinearLayout(c);
        footerLL.setBackgroundColor(Color.DKGRAY);
        footerLL.setPadding(20, 20, 20, 20);

        Button okBtn = new Button(c);
        okBtn.setText("OK");
        okBtn.setTextColor(Color.WHITE);
        okBtn.setOnClickListener(clickOK());

        Button cancelBtn = new Button(c);
        cancelBtn.setText("キャンセル");
        cancelBtn.setTextColor(Color.WHITE);
        cancelBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });

        footerLL.addView(okBtn, BTN_LP);
        footerLL.addView(cancelBtn, BTN_LP);
        ll.addView(footerLL, LP_MP_WC);
        return dialog;
    }

    // OKボタンをタップして会社を選択
    private OnClickListener clickOK() {
        return new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = listView.getCheckedItemPosition(); // 選択位置
                if (position == -1) {
                    Log.i(TAG, "未選択");
                    dismiss();
                    return;
                }

                String type = ((HashMap<?, ?>) listData.get(position)).get("type").toString();
                if (type.equals("player")) {
                    String display = ((HashMap<?, ?>) listData.get(position)).get("display").toString();
                    MainActivity.btn.setText(display);
                } else {
                    Log.w(TAG, "section"); // 選択できないはず
                }
                dismiss();
            }
        };
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        MainActivity.btn.setClickable(true);
        MainActivity.btn = null;
    }

    @Override
    public void onPause() {
        super.onPause();
        dismiss();
    }

    @Override
    public boolean isChecked() {
        return false;
    }

    @Override
    public void setChecked(boolean checked) {
    }

    @Override
    public void toggle() {
    }

    // リスト
    private ArrayList<HashMap<String, String>> getPlayerList(Context c) {
        listData = new ArrayList<HashMap<String, String>>();

        HashMap<String, String> position1 = new HashMap<String, String>();
        position1.put("display", "GK");
        position1.put("type", "position");
        listData.add(position1);

        HashMap<String, String> player1 = new HashMap<String, String>();
        player1.put("display", "山岸範宏");
        player1.put("type", "player");
        listData.add(player1);

        HashMap<String, String> position2 = new HashMap<String, String>();
        position2.put("display", "DF");
        position2.put("type", "position");
        listData.add(position2);

        HashMap<String, String> player2 = new HashMap<String, String>();
        player2.put("display", "森脇良太");
        player2.put("type", "player");
        listData.add(player2);

        HashMap<String, String> player3 = new HashMap<String, String>();
        player3.put("display", "那須大亮");
        player3.put("type", "player");
        listData.add(player3);

        HashMap<String, String> player4 = new HashMap<String, String>();
        player4.put("display", "槙野智章");
        player4.put("type", "player");
        listData.add(player4);

        HashMap<String, String> position3 = new HashMap<String, String>();
        position3.put("display", "MF");
        position3.put("type", "position");
        listData.add(position3);

        HashMap<String, String> player5 = new HashMap<String, String>();
        player5.put("display", "平川忠亮");
        player5.put("type", "player");
        listData.add(player5);

        HashMap<String, String> player6 = new HashMap<String, String>();
        player6.put("display", "阿部勇樹");
        player6.put("type", "player");
        listData.add(player6);

        HashMap<String, String> player7 = new HashMap<String, String>();
        player7.put("display", "鈴木啓太");
        player7.put("type", "player");
        listData.add(player7);

        HashMap<String, String> player8 = new HashMap<String, String>();
        player8.put("display", "宇賀神友弥");
        player8.put("type", "player");
        listData.add(player8);

        HashMap<String, String> player9 = new HashMap<String, String>();
        player9.put("display", "柏木陽介");
        player9.put("type", "player");
        listData.add(player9);

        HashMap<String, String> player10 = new HashMap<String, String>();
        player10.put("display", "原口元気");
        player10.put("type", "player");
        listData.add(player10);

        HashMap<String, String> position4 = new HashMap<String, String>();
        position4.put("display", "FW");
        position4.put("type", "position");
        listData.add(position4);

        HashMap<String, String> player11 = new HashMap<String, String>();
        player11.put("display", "興梠慎三");
        player11.put("type", "player");
        listData.add(player11);

        HashMap<String, String> position5 = new HashMap<String, String>();
        position5.put("display", "その他");
        position5.put("type", "position");
        listData.add(position5);

        HashMap<String, String> player12 = new HashMap<String, String>();
        player12.put("display", "おらん");
        player12.put("type", "player");
        listData.add(player12);

        HashMap<String, String> player13 = new HashMap<String, String>();
        player13.put("display", "山田暢久");
        player13.put("type", "player");
        listData.add(player13);

        return listData;
    }
}

長くなった。
onCreateDialog()で、Java側でレイアウト作ってるけど、XMLでもいいと思います。

でもリストデータを作ってるgetPlayerList()は、DBから呼び出しならCursorでループとかだと思うので、こんなことにはならないはず。場合によっては別ファイルで。

この2つの対応次第で短くなるでしょう。

OK押したときに、選択されているpositionからデータを拾って、元のボタンに値をセットするような形にしています。
今回は対応してないですが、ViewなりPreferenceなりに選択した値を残しておけば、再度選択した時にsetItemChecked()で前回選択したアイテムを選択した状態で開けますね。

アダプター部分

PlayerListAdapter.java

public class PlayerListAdapter extends SimpleAdapter {
    private LayoutInflater inflater;
    private List<? extends Map<String, ?>> listData;
    private int resource;

    public class ViewHolder {
        TextView displayTV;
    }

    public PlayerListAdapter(Context context, List<? extends Map<String, ?>> listData, int resource, String[] from, int[] to) {
        super(context, listData, resource, from, to);
        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.listData = listData;
        this.resource = resource;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        View view = convertView; // ビューを受け取る

        if (view == null) {
            view = inflater.inflate(resource, parent, false);
            holder = new ViewHolder();
            holder.displayTV = (TextView) view.findViewById(R.id.display);
            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }

        String display = ((HashMap<?, ?>) listData.get(position)).get("display").toString();
        String type = ((HashMap<?, ?>) listData.get(position)).get("type").toString();
        holder.displayTV.setText(display);

        if (type.equals("position")) {
            view.setClickable(true);
            view.findViewById(R.id.radio_button).setVisibility(View.INVISIBLE);
            view.setBackgroundColor(Color.RED);
            holder.displayTV.setTextColor(Color.WHITE);
        } else {
            view.setClickable(false);
            view.findViewById(R.id.radio_button).setVisibility(View.VISIBLE);
            view.setBackgroundColor(Color.WHITE);
            holder.displayTV.setTextColor(Color.BLACK);
        }
        return view;
    }
}

普通のリストで区切りを作るのと同じですね。
参考サイトを確認いただいたほうが分かりやすいと思いますが、getViewでその行が区切りかアイテムか判定してデザインの処理をしてます。
区切りの場合はクリックできないようにするのも忘れず。

Activity

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="MVPは?" />

</RelativeLayout>

ボタンだけ置いてる。

—–

MainActivity.java

public class MainActivity extends FragmentActivity implements OnClickListener {
    public static Button btn;

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

        Button btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        v.setClickable(false); // ダブルタップ防止
        btn = (Button) v; // 後でタップ防止解除のためとっとく

        FragmentManager fm = getSupportFragmentManager();
        DialogFragment dlg = new PlayersDialog();
        dlg.show(fm, "tag_players_dialog");
    }
}

起動時のActivity。
v4のFragmentを使ってるため、FragmentActivityを使っています。

以上!

たかだか区切り入れるのに、ほんと面倒な作業をしました。
もっといい方法があれば教えてください。だれか。

 

listview

 

 

おすすめ


Effective Android