421


お世話になっております。

現在ListFragmentを使用し、アプリを作っているのですが、
ListFragmentの各リストをタップするとダイアログを出し、ダイアログの値をそのリストに入れたいのですが、
動的に変更する方法がいまいち思いつきません。

どなたかいい方法をご存知の方はいらっしゃらないでしょうか？

よろしくお願いいたします。


CC BY-SA 3.0
2014-12-05T13:14:56.140
378
2014-12-09T06:17:54.687
2014-12-05T14:48:05.200
30
297
question
android android-listfragment
ListFragmentの各リストの動的な変更について


ダイアログから何かを変更するならカスタムダイアログを作って初期化時にインターフェースを渡します。

インターフェースの例

[code]

    public interface setInterface{
        public void setter( input );
    }
    
[/code]

ダイアログの例

[code]

    public static class TestDialogFragment extends DialogFragment {
        setInterface target;
    
        //コンストラクタでinterfaceをセット
        TestDialogFragment( setInterface target ){
            this.target = target;
        }
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setMessage("ダイアログ")
                    .setPositiveButton("はい", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            target.setter( YES ); 書込み
                        }
                    })            
            return builder.create();
        }
    }
    
[/code]

setterの定義は、フラグメント自体に書いてしまうのが一番楽です。Listの各要素がデータクラスなりViewクラスなりを持っているならそちらに書きましょう。  
例えばフラグメント自体に書くなら,

[code]

    public class ItemListFragment extends ListFragment implements setInterface {
         @Override
         public void setter( String input ){ 
             TextView title = this.targetView.findViewWithTag("title");
             title.setText(input); 
         };
    
         @Override
         public void onListItemClick (ListView l, View v, int position, long id){
             this.targetView = l;//イベント元を保持
             DialogFragment newFragment = new TestDialogFragment(this);//インターフェースを渡す
             newFragment.show(mActivity.getFragmentManager(), "test");
         }
         //省略
    }
    
[/code]


CC BY-SA 3.0
2014-12-05T19:00:39.140
386
2014-12-06T14:30:13.387
2014-12-06T14:30:13.387
525
525
378
answer


ダイアログは DialogFragment を使うという前提とすると、Fragment 間でどのように結果を受け渡すかというのが問題になります。

ポイントは以下の通りです。

  1. 通知を受け取りたいフラグメントは、対象のフラグメントに対して `setTargetFragment()` を呼んで呼び出し元を知らせる。
  2. 通知を送るフラグメントは、`getTargetFragment()` と `getTargetRequestCode()` を使い、 `onActivityResult()` で通知する。
  3. 通知を受け取ったら、ArrayAdapter の中のデータを更新し、`notifyDataSetChanged()` で更新を反映する(ArrayAdapterの場合)。

以下の様な感じで書きます。上記のポイントと関連する部分に日本語のコメントを入れておきました。

<https://github.com/zaki50/DynamicListItemUpdateSample>
に、動作するアプリが作れるプロジェクトを置きました。

[code]

    package com.example.dynamiclistitemupdate;
    
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.app.Dialog;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v4.app.DialogFragment;
    import android.support.v4.app.ListFragment;
    import android.support.v7.app.ActionBarActivity;
    import android.view.View;
    import android.widget.ArrayAdapter;
    import android.widget.ListView;
    
    
    public class MainActivity extends ActionBarActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (savedInstanceState == null) {
                getSupportFragmentManager().beginTransaction()
                        .add(R.id.container, new MyListFragment())
                        .commit();
            }
        }
    
        public static class MyListFragment extends ListFragment {
    
            private static final int REQ_DIALOG = 1;
    
            private int mItemIndex = -1;
    
            private String[] mItemData = {"-", "-", "-", "-", "-"};
    
            public MyListFragment() {
            }
    
            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
    
                setListAdapter(new ArrayAdapter<>(getActivity(),
                        android.R.layout.simple_list_item_1, mItemData));
            }
    
            @Override
            public void onListItemClick(ListView l, View v, int position, long id) {
                super.onListItemClick(l, v, position, id);
    
                mItemIndex = position - l.getHeaderViewsCount();
    
                DialogFragment newFragment = new MyDialogFragment();
                // ダイアログフラグメントに対して、結果の通知先を指定。結果は onActivityResult を呼び出すことで通知される
                newFragment.setTargetFragment(this, REQ_DIALOG);
                newFragment.show(getChildFragmentManager(), "dialog");
            }
    
            @Override
            public void onActivityResult(int requestCode, int resultCode, Intent data) {
                super.onActivityResult(requestCode, resultCode, data);
    
                if (requestCode != REQ_DIALOG || resultCode != RESULT_OK) {
                    return;
                }
    
                if (mItemIndex < 0 || data == null
                        || data.getStringExtra(MyDialogFragment.RESULT_EXTRA_KEY_TEXT) == null) {
                    return;
                }
    
                // ArrayAdapter の中の配列を更新し、notifyDataSetChanged() で更新を通知する
                mItemData[mItemIndex] = data.getStringExtra(MyDialogFragment.RESULT_EXTRA_KEY_TEXT);
                final ArrayAdapter<?> adapter = (ArrayAdapter<?>) getListAdapter();
                adapter.notifyDataSetChanged();
            }
        }
    
        public static class MyDialogFragment extends DialogFragment {
            public static final String RESULT_EXTRA_KEY_TEXT = "text";
    
            public MyDialogFragment() {
            }
    
            @Override
            public Dialog onCreateDialog(Bundle savedInstanceState) {
                AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                builder.setMessage("ダイアログ")
                        .setPositiveButton("はい", new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int id) {
                                sendResult(Activity.RESULT_OK, "yes");
                            }
                        })
                        .setNegativeButton("いいえ", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                sendResult(Activity.RESULT_OK, "no");
                            }
                        });
                return builder.create();
            }
    
            /**
             * 結果をダイアログ表示元へ通知するためのメソッド。
             *
             * targetFragment に通知元がセットされているので、onActivityResult を呼び出すことで通知を行う。
             */
            private void sendResult(int resultCode, String str) {
                final Intent data = new Intent();
    
                if (str != null) {
                    data.putExtra(RESULT_EXTRA_KEY_TEXT, str);
                }
    
                // 結果を通知する
                getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, data);
            }
        }
    }
    
[/code]

別解として、event bus 系のライブラリを使うという方法もあります。いくつか有名所を上げておきます。

  * [greenrobot EventBus](https://github.com/greenrobot/EventBus)
  * [Otto](http://square.github.io/otto/)
  * [Guava](https://code.google.com/p/guava-libraries/wiki/EventBusExplained)

私は greenrobot EventBus を使うことが多いですが、 Otto もよく使われているようです。event bus
についてはQiitaにも[エントリ](http://qiita.com/KeithYokoma/items/793aaac6994c9242808f)がありました。


CC BY-SA 3.0
2014-12-07T06:21:01.013
421
2014-12-09T06:17:54.687
2014-12-09T06:17:54.687
319
319
378
answer