Android:从ViewPager / FragmentStatePagerAdapter获取片段参考

时间:2013-12-18 14:01:32

标签: android android-fragments android-viewpager

预先披露,这是一个学校项目。

我一直致力于一个项目中的活动,该活动由一系列动态操作栏选项卡和列表片段组成,并且一直困在于获取对特定选项卡片段的对象引用的前景,以便为列表视图。

我现在使用ViewPager(支持v4版本)和FragmentStatePagerAdapter(v13)进行设置。我已经尝试了几个SO问题线程中提到的方法,即这三个:

Updating fragments/views in viewpager with fragmentStatePagerAdapter

Retrieve a Fragment from a ViewPager

Android ViewPager - can't update dynamically

然而,这些尝试中的每一次都无济于事,这是我试图尝试展示我想要实现的目标的相关代码。

FragmentStatePagerAdapter的代码:

    /**
 * The Class ServerPagerAdapter.
 */
private class ServerPagerAdapter extends FragmentStatePagerAdapter
{
    SparseArray<ServerViewFragment> registeredFragments = new SparseArray<ServerViewFragment>();


    /**
     * Instantiates a new server pager adapter.
     *
     * @param fm the fragment manager
     */
    public ServerPagerAdapter(FragmentManager fm)
    {
        super(fm);
    }

    /* (non-Javadoc)
     * @see android.support.v13.app.FragmentPagerAdapter#getItem(int)
     */
    @Override
    public Fragment getItem(int position)
    {
        ServerViewFragment serverFrag = new ServerViewFragment();
        registeredFragments.put(position, serverFrag);

        return serverFrag;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) 
    {
        super.destroyItem(container, position, object);
        registeredFragments.remove(position);
    }


    /* (non-Javadoc)
     * @see android.support.v4.view.PagerAdapter#getCount()
     */
    @Override
    public int getCount()
    {
        // TODO STATIC VALUE.
        return 3;
    }



    /* (non-Javadoc)
     * @see android.support.v4.view.PagerAdapter#getPageTitle(int)
     */
    @Override
    public CharSequence getPageTitle(int position)
    {
        //TODO: HARD CODED
        switch (position)
        {
            case 0:
                return "Status";
            case 1:
                return "#chat";
            case 2:
                return "#help";
        }
        return null;
    }

    public ServerViewFragment getFragment(int position)
    {
        return registeredFragments.get(position);
    }

}

我正在尝试开展的活动中的代码:

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

            /*Code omitted (actionbar navigation)*/

    //connect pager
    servPagerAdapter = new ServerPagerAdapter(getFragmentManager());

    viewPager = (ViewPager)findViewById(R.id.server_view_pager);
    viewPager.setAdapter(servPagerAdapter);
    viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener()
    {
        @Override
        public void onPageSelected(int position)
        {
            actionBar.setSelectedNavigationItem(position);
        }
    });

    //populate tabs
    for (int i = 0; i < servPagerAdapter.getCount(); i++)
    {
        Tab tabToAdd = actionBar.newTab();
        tabToAdd.setText(servPagerAdapter.getPageTitle(i));
        tabToAdd.setTabListener(this);
        actionBar.addTab(tabToAdd);
    }

    svf = servPagerAdapter.getFragment(0);
    csvf = servPagerAdapter.getFragment(1);

    IRCConnection testConn = new IRCConnection();
    testConn.setOnIRCMessageReceivedListener(this);

            //adapters are populated in a separate method
    adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
    cadapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);

    Log.i("ADAPTER", adapter == null ? "Null" : "Not Null");
    Log.i("FRAGMENT", svf == null ? "Null" : "Not Null");


    //svf.setListAdapter(adapter);
    //csvf.setListAdapter(cadapter);

    /*code omitted (network call)*/

}

svf和csvf是我用来尝试测试让它工作的变量,它们是基于ListFragment的类,它完全是cookie切割器。最后,我希望能够从适配器添加和删除选项卡,并管理每个选项卡中的列表视图。

在这个阶段,我可能只是一个问题,我不理解适配器工作的主题/方式,我希望这只是一个基于无知的问题,并且有一个简单的解决方案。

编辑:我已经取得了一些进展,但是在可见选项卡上调用refresh会因某种原因抛出空指针。这是我的连接类的回调,回调在活动类中:

    @Override
public void onNetworkMessageReceived(String message)
{
    final String msg = message;

    this.runOnUiThread(new Runnable()
    {
        @Override
        public void run()
        {
            FragmentData data = fragmentDataMapper.getData("Network");
            ServerViewFragment visFrag = servPagerAdapter.getFragment(getActionBar().getSelectedNavigationIndex());

            if (data == null)
            {

                ArrayList<String> chatStrings = new ArrayList<String>();
                chatStrings.add(msg);
                data = new FragmentData(chatStrings);
                visFrag.refresh(data);
                fragmentDataMapper.updateData("Network", data);
            }
            else
            {
                ArrayList<String> chatStrings = data.getChatStrings();
                chatStrings.add(msg);
                data = new FragmentData(chatStrings);
                visFrag.refresh(data);

                fragmentDataMapper.updateData("Network", data);
            }   
        }
    });

    Log.i("HANDLER", message);
}

由于visFrag为null,因此会在任一visFrag.refresh行上抛出异常。我之前仍在使用相同的registeredFragments事物。

1 个答案:

答案 0 :(得分:2)

鉴于最新评论,我建议您如何能够获得所寻求的行为。

我相信您对活动,标签和片段的设置非常有意义。只有活动和片段之间的沟通才需要解决。

在我自己的项目中,我使用了registeredFragments方法,但只针对当前可见的片段,因为它总是非空的。这可以更新当前可见的信息,但我认为必须以不同的方式将更新传达给其他片段才能保持稳定。

我不会详细说明如何在您的设置中完成更新,因为我不知道您当前如何以及何时处理此更新,因此这将有点笼统。

好的,这是我的建议:

只需要一个静态类,它包含片段和要显示的信息之间的映射。该映射可以在给予每个片段的唯一名称或页面编号与它应该显示的信息的模型之间。这里概述了这个想法:

public class FragmentDataMapping {
    // FragmentId could be anything like String or Integer
    private Map<FragmentId, FragmentData> fragmentData = new HashMap<>();

    public static void updateData(FragmentId fragmentId, FragmentData fragmentData) {
        fragmentData.put(fragmentId, fragmentData);
    }

    public static FragmentData getData(FragmentId fragmentId) {
        return fragmentData.get(fragmentId);
    }
}

// FragmentData could be any kind of model object for your fragments
public class FragmentData {
    private String someData;
    private Integer someOtherData;
    private... and so on

    // constructor, setter and getter methods
}

使用某种类似的Mapping和Model数据,您现在可以在活动中,一旦从服务器获取数据,就会创建一个包含该信息的FragmentData对象并将其与片段相关联。

public void serverResponse(String someData, Integer someOtherData) {
    FragmentData newData = new FragmentData(someData, someOtherData);
    FragmentDataMapping.updateData("DetailsFragment", newData);
}

在片段的onResume中,您将检查FragmentData以获取最新数据。

// This is inside DetailsFragment
private String fragmentId = "DetailsFragment"; // <- some id assigned at creation

public void onResume() {
    FragmentData data = FragmentDataMapping.getData("DetailsFragment");
    Log.d("DetailsFragment", "My data: " + data.getSomeData() + " and " + data.getSomeOtherData());
    refresh(fragmentData)
    ....
}

public void refresh(FragmentData fragmentData) {
   // set adapters, update view or do whatever with the data.
   // the reason for creating a seperate method to handle the fragmentData follows below
}

使用此(或类似的东西),片段将在选择新标签后立即更新数据。

现在,当前可见的片段不会使用此方法更新自身,因为未调用onResume()。因此,要更新当前片段,您可以使用viewpager的registeredFragments:

public void serverResponse(String someData, Integer someOtherData) {
    FragmentData newData = new FragmentData(someData, someOtherData);
    FragmentDataMapping.updateData("DetailsFragment", newData);
    registeredFragments(getActionBar().getSelectedNavigationIndex()).refresh(newData);
}

我知道这个实现的描述含糊不清但我希望它有一些想法和一些点,你可以从调查中受益。