【问题标题】:change the items in a ListView from another fragment从另一个片段更改 ListView 中的项目
【发布时间】:2013-07-19 10:03:44
【问题描述】:

我有两个片段,我们称它们为片段 A 和片段 B,它们是 NavigationDrawer 的一部分(这是它们绑定到的活动)。在片段 A 中,我有一个按钮。当按下此按钮时,我想在 Fragment B 的 ListView 中添加另一个项目。

最好的方法是什么?使用 Intents、SavedPreferences、将某些内容公开(?)或其他内容?

EDIT 5: 20/7/13 这是最新的代码

这是我用来启动片段的NavigationDrawer

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class Navigation_Drawer extends FragmentActivity {

    public DrawerLayout mDrawerLayout; // Creates a DrawerLayout called_.
    public ListView mDrawerList;
    public ActionBarDrawerToggle mDrawerToggle;

    private CharSequence mDrawerTitle;
    private CharSequence mTitle;
    private String[] mNoterActivities; // This creates a string array called _.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub

// Just setting up the navigation drawer

    } // End of onCreate

// Removed the menu


        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                long id) {
            selectItem(position);
        }
    }

    private void selectItem(int position) { 

        FragmentManager fragmentManager = getSupportFragmentManager();

        if (position == 0) {
            Fragment qnfragment = new QuickNoteFragment(); 
            ((FragmentBase) qnfragment).setContext(this);
            Bundle args = new Bundle(); // Creates a bundle called args
            args.putInt(QuickNoteFragment.ARG_nOTERACTIVITY_NUMBER, position); 

            qnfragment.setArguments(args);

            fragmentManager.beginTransaction()
                    .replace(R.id.content_frame, qnfragment).commit();



        } else if (position == 3) {
            Fragment pendViewPager = new PendViewPager(); // This is a ViewPager that includes HistoryFragment
            ((FragmentBase) pendViewPager).setContext(this);
            Bundle args = new Bundle();

            pendViewPager.setArguments(args);
            fragmentManager.beginTransaction()
                    .replace(R.id.content_frame, pendViewPager).commit();
        }

    // Update title etc
    }


    @Override
    protected void onPostCreate(Bundle savedInstanceState) { // Used for the NavDrawer toggle
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) { // Used for the NavDrawer toggle
        super.onConfigurationChanged(newConfig);
        // Pass any configuration change to the drawer toggles
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

}

这是 QuickNoteFragment:

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class QuickNoteFragment extends FragmentBase implements OnClickListener {

    public static final String ARG_nOTERACTIVITY_NUMBER = "noter_activity";

    Button b_create;
// removed other defining stuff

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.quicknote, container, false);
        int i = getArguments().getInt(ARG_nOTERACTIVITY_NUMBER);
        String noter_activity = getResources().getStringArray(
                R.array.noter_array)[i];
        FragmentManager fm = getFragmentManager();
        setRetainInstance(true);

        b_create = (Button) rootView.findViewById(R.id.qn_b_create);

        // Removed other stuff

        getActivity().setTitle(noter_activity);
        return rootView;
    }

    @Override
    public void onClick(View v) {

        // TODO Auto-generated method stub

        switch (v.getId()) {

        case R.id.qn_b_create:

                              String data = "String data";
                DataModel.getInstance().addItem(data);

            break;
        }
    }

@Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        // TODO Auto-generated method stub
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection

        }
    }

    public interface OnItemAddedHandler { // Srains code
        public void onItemAdded(Object data);
    }
}

这是 HistoryFragment(记住它是 ViewPager 的一部分):

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.RiThBo.noter.QuickNoteFragment.OnItemAddedHandler;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;

public class HistoryFragment extends FragmentBase implements OnItemAddedHandler {

    ListView lv;
    List<Map<String, String>> planetsList = new ArrayList<Map<String, String>>();
    SimpleAdapter simpleAdpt;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        View view = inflater.inflate(R.layout.history, container, false);
        initList();


        ListView lv = (ListView) view.findViewById(R.id.listView);
        simpleAdpt = new SimpleAdapter(getActivity(), planetsList,
                android.R.layout.simple_list_item_1, new String[] { "planet" },
                new int[] { android.R.id.text1 });

        lv.setAdapter(simpleAdpt);

        lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            public void onItemClick(AdapterView<?> parentAdapter, View view,
                    int position, long id) {

                // We know the View is a TextView so we can cast it

                TextView clickedView = (TextView) view;

                Toast.makeText(
                        getActivity(),
                        "Item with id [" + id + "]  - Position [" + position
                                + "] - Planet [" + clickedView.getText() + "]",
                        Toast.LENGTH_SHORT).show();
            }

        });
        registerForContextMenu(lv);

        return view;

    }

    private void initList() {

        // We populate the planets

        planetsList.add(createPlanet("planet", "Mercury"));
        planetsList.add(createPlanet("planet", "Venus"));
        planetsList.add(createPlanet("planet", "Mars"));
        planetsList.add(createPlanet("planet", "Jupiter"));
        planetsList.add(createPlanet("planet", "Saturn"));
        planetsList.add(createPlanet("planet", "Uranus"));
        planetsList.add(createPlanet("planet", "Neptune"));

    }

    private HashMap<String, String> createPlanet(String key, String name) {

        HashMap<String, String> planet = new HashMap<String, String>();

        planet.put(key, name);

        return planet;

    }

    // We want to create a context Menu when the user long click on an item

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,

    ContextMenuInfo menuInfo) {

        super.onCreateContextMenu(menu, v, menuInfo);

        AdapterContextMenuInfo aInfo = (AdapterContextMenuInfo) menuInfo;

        // We know that each row in the adapter is a Map

        HashMap map = (HashMap) simpleAdpt.getItem(aInfo.position);

        menu.setHeaderTitle("Options for " + map.get("planet"));
        menu.add(1, 1, 1, "Details");
        menu.add(1, 2, 2, "Delete");

    }
     @Override
      public void onItemAdded(Object data) {
          // to add item
          String string = String.valueOf(data);
          Toast.makeText(getContext(), "Data: " + string, Toast.LENGTH_SHORT).show();
          planetsList.add(createPlanet("planet", string));
      }
      @Override
    public void onStart() {
          super.onStart();

          DataModel.getInstance().setOnItemAddedHandler(this);
      }
}

这是 FragmentBase:

   import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentActivity;

      public class FragmentBase extends Fragment {

            private FragmentActivity mActivity; // I changed it to FragmentActivity because Activity was not working, and my `NavDrawer` is a FragmentActivity.

            public void setContext(FragmentActivity activity) {
                mActivity = mActivity;
            }

            public FragmentActivity getContext() {
                return mActivity;
            }
    }

这是数据模型:

  import android.util.Log;
    import com.xxx.xxx.QuickNoteFragment.OnItemAddedHandler;

public class DataModel {

    private static DataModel instance;
    private static OnItemAddedHandler mOnItemAddHandler;

    public static DataModel getInstance() {
        if (null == instance) {
            instance = new DataModel();
        }
        return instance;
    }

    public void setOnItemAddedHandler(OnItemAddedHandler handler) {
        mOnItemAddHandler = handler;
    }

    public void addItem(Object data) {
        if (null != mOnItemAddHandler)
            mOnItemAddHandler.onItemAdded(data);
        else {
            Log.i("is context null?", "yes!");
        }  
    }
}

谢谢

【问题讨论】:

  • 由于属于同一个Activity的片段聚集在一起,你可以安全地使用静态字段。类似FragmentB.adapter.add(x); FragmentBadapter.notifyDataSetChanges();
  • @Archie.bpgc 请你再扩展一点 - 不太明白
  • 只需将 FragmentBListView Adapter 设为 static 并在 FragmentA 的 按钮单击中像 FragmentB.adapter.add(new item); 一样使用它。
  • 谢谢 - 我现在明白了。我会试试的

标签: android listview android-listview android-fragments


【解决方案1】:

我建议您使用interfaceMVC,这将使您的代码更易于维护。

首先你需要一个接口:

    public interface OnItemAddedHandler {
        public void onItemAdded(Object data);
    }

然后,您将需要一个数据模型:

    public class DataModel {

        private static DataModel instance;
        private static OnItemAddedHandler mOnItemAddHandler;

        public static DataModel getInstance() {
            if (null == instance) {
                instance = new DataModel();
            }
            return instance;
        }

        public void setOnItemAddedHandler(OnItemAddedHandler handler) {
            mOnItemAddHandler = handler;
        }

        public void addItem(Object data) {
            if (null != mOnItemAddHandler)
                mOnItemAddHandler.onItemAdded(data);
        }
    }

当您点击按钮时,您可以将数据添加到数据模型中:

    Object data = null;
    DataModel.getInstance().addItem(data);

然后,FragmentB 实现接口OnItemAddedHandler 添加项目

    public class FragmentB implements OnItemAddedHandler {

        @Override
        public void onItemAdded(Object data) {
            // to add item
        }
    }

另外,当FragmentB 启动时,您应该将自己注册到DataModel:

    public class FragmentB implements OnItemAddedHandler {

        @Override
        public void onItemAdded(Object data) {
            // to add item
        }

        @Override
        protected void onStart() {
            super.onStart();

            DataModel.getInstance().setOnItemAddedHandler(this);
        }
    }

您也可以在FragmentBonCreate方法中添加DataModel.getInstance().setOnItemAddedHandler(this);

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    DataModel.getInstance().setOnItemAddedHandler(this);

    // do other things
}

更新

你可以简单地发送字符串:

    String data = "some string";
    DataModel.getInstance().addItem(data);

然后在FragementB

    public class FragmentB implements OnItemAddedHandler {

        @Override
        public void onItemAdded(Object data) {
            // get what you send into method DataModel.getInstance().addItem(data);
            String string = String.valueOf(data);
        }
    }

更新

好的。您已经在onCreateView 方法中添加了DataModel.getInstance().setOnItemAddedHandler(this),因此无需在onStart() 方法中添加它。您可以删除整个onStart 方法。

onStart 将在片段开始时调用,我们不需要在onCreateView 中调用它。并且在onItemAdded 上调用DataModel.getInstance().addItem(data) 方法时会被调用,我们也不需要在onCreateView 中调用它。

所以,您可以从onCreateView 方法中删除以下代码:

   DataModel.getInstance().setOnItemAddedHandler(this);
   // remove the methods below
   // onItemAdded(getView());
   // onStart();

你有另一个片段,有一个按钮,你可以在clickhandler函数中添加下面的代码:

String data = "some string";
DataModel.getInstance().addItem(data);

更新3

我想HistoryFragment在你到QuickNoteFragment之后已经分离了 您可以添加代码到HistoryFragment 进行检查:

@Override
public void onDetach() {
    super.onDetach();
    Log.i("test", String.format("onDetach! %s", getActivity() == null));
}

更新4

我认为HistoryFragmentQuickNoteFragment 应该有一个父类,命名为FragmentBase

    public class FragmentBase extends Fragment {

        private Activity mActivity;

        public void setContext(Activity activity) {
            mActivity = mActivity;
        }

        public Activity getContext() {
            return mActivity;
        }
    }

HistoryFragmentQuickNoteFragment 扩展 FragmentBase。那么当你在它们之间切换时,你可以调用setContext来设置一个Activity,比如:

private void selectItem(int position) {

    FragmentManager fragmentManager = getSupportFragmentManager();

    if (position == 0) {
        Fragment qnfragment = new QuickNoteFragment();
        qnfragment.setContext(this);

        // ...

    } else if (position == 1) {
        Fragment pagerFragment = new RemViewPager();
        pagerFragment.setContext(this);

        // ...
    }
}

现在,我们可以通过调用getContext 获得HistoryFragment 中的非空活动,因此我们可以将onItemAdded 方法更改为:

  @Override
  public void onItemAdded(Object data) {
      // to add item
      String string = String.valueOf(data);
      Toast.makeText(getContext(), "Data: " + string, Toast.LENGTH_SHORT).show();
      planetsList.add(createPlanet("planet", string));
  }

我希望这会奏效。

【讨论】:

  • 哇!非常感谢您提供的综合指南 - 我也会试试这个
  • 好的,我的一切运行都没有错误,但是对象没有被添加到列表视图中(我尝试了一个字符串和文本视图)
  • 好的。如果仍有任何问题,请随时在此处发表评论。不要忘记删除onStart 方法并且不要在onCreateView 方法中调用onStart。祝你好运。
  • 不,我不这么认为。我正在使用 NavigationDrawer api,每次单击片段时,都会为该片段调用 onCreateView。
  • update 回答并添加了update4。如果方便的话,你可以把你的代码放在github上,我可以fork你的项目并做一些代码来解决这个问题。如果您愿意,您也可以将您的代码发送到我的电子邮件。
【解决方案2】:

一些好的设计原则:

  • Activity 可以知道关于它包含的任何 Fragment 的所有公共信息。
  • 片段不应该知道包含它的特定活动。
  • 片段永远不应该知道父活动中可能包含或不包含的其他片段。

基于这些原则的建议方法(非正式设计模式)。

每个片段都应该声明一个由其父活动实现的接口:

public class MyFragment extends Fragment
{
   public interface Parent
   {
      void onMyFragmentSomeAction();
   }
   private Parent mParent;
   public onAttach(Activity activity)
   {
      mParent = (Parent) activity;
   }

   // This would actually be in a listener.  Simplifying to save typing.
   void onSomeButtonClick(View button)
   {
      mParent.onMyFragmentSomeAction();
   }
}

并且活动应该为它包含的所有片段实现适当的接口。

public class MyActivity extends Activity 
                        implements MyFragment.Parent,
                                   YourFragment.Parent,
                                   HisFragment.Parent
{
   [usual Activity code]
   void onMyFragmentSomeAction()
   {
     if yourFragment is showing
     {
        yourFragment.reactToSomeAction();
     }
     if hisFragment is showing
     {
        hisFragment.observeThatSomeActionHappened();
     }
     [etc]
}

广播方法也不错,但它相当重量级,它需要目标 Fragment 知道源 Fragment 将发送哪些广播。

【讨论】:

  • 感谢代码和信息 :-),如果我无法让 srains 代码工作,我会试试这个
【解决方案3】:
  1. 使用广播。从 A 发送一个广播,B 将接收并处理它。
  2. 给B添加一个公共方法,例如public void addListItem(),这将向B中的listview添加数据。片段A尝试使用FragmentMananger.findFragmentByTag()找到片段B的实例并调用该方法。

【讨论】:

  • 你会保存哪个“更好”。按钮会被按下多次,会不会导致大量广播导致电池耗尽?
  • 在 FragmentA 我有 FragmentManager fm = getFragmentManager(); fm.findFragmentByTag("FragmentB");如何从中调用公共方法。
  • 使用FragmentManager.findFragmentByTag(),获取Fragment B实例的引用,在Fragment B中写一个公共方法addListItem(),从A调用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多