如何多次使用同一个片段?

Posted

技术标签:

【中文标题】如何多次使用同一个片段?【英文标题】:How to use the same fragment more than once? 【发布时间】:2017-02-15 23:00:40 【问题描述】:

我正在创建一个应用程序,该应用程序从网站的 XML 文件中获取数据并将这些数据传递给 Fragment

我使用的是 Action Bar,我有 3 个标签,还有 ViewPager。 该片段包含一个ListView,我使用Bundle 传递数据,我可以正确地从片段中获取传递的数据。

public Fragment getItem(int i)
  switch(i)
    case 0:
      Bundle b1= new Bundle();
      b1.putString("cat", "artufonews");
      Fragment f1 = new LoadFrag();
      f1.setArguments(b1);
      return f1;
    case 1:
      Bundle b2 = new Bundle();
      b2.putString("cat", "artufo");
      Fragment f2 = new LoadFrag();
      f2.setArguments(b2);
      return f2;
    case 2:
      Bundle b3 = new Bundle();
      b3.putString("cat", "artmyst");
      Fragment f3 = new LoadFrag();
      f3.setArguments(b3);
      return f3;
    default :
      return new LoadMee();
  

LoadFrag 是我拥有的片段。我可以正确地传递数据,但是当我尝试使用mPager.getAdapter().notifyDataSetChanged() 更新列表视图时,无论我做什么,这三个选项卡都会向我显示相同的列表。

下面的代码是片段代码:

public static class LoadFrag extends Fragment
    String cat;
    boolean kl = false;
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

        //new TestAsync().execute();
        View rootView = inflater.inflate(R.layout.listfrage, container, false);
        ListView viewer;
        String[] hell = (String[])list.toArray(new String[list.size()]);

        final String[] tots = (String[])links.toArray(new String[links.size()]);
        cat = getArguments().getString("cat");
        CustomLister lister = new CustomLister(getActivity(), hell, bmps);
        viewer = (ListView)rootView.findViewById(R.id.lister);
        viewer.setAdapter(lister);
        viewer.setOnItemClickListener(new AdapterView.OnItemClickListener() 

            public void onItemClick(AdapterView<?> parent, View view, int position, long id)
                Toast.makeText(getActivity(), "You clicked: item " + tots[position], Toast.LENGTH_SHORT).show();;

                Intent i = new Intent(getActivity(), Starter.class);
                i.putExtra("link", tots[position]);
                startActivity(i);
            
        );
        return rootView;    
    

    @Override
    public void setMenuVisibility(final boolean visible) 
        super.setMenuVisibility(visible);
        if (visible && kl == false) 
            getActivity().setTitle(cat);
            new TestAsync().execute();
            kl = true;
        
    

    class TestAsync extends AsyncTask<Void, Integer, String>
    
        String TAG = getClass().getSimpleName();

        ProgressDialog dialog;
        protected void onPreExecute ()
            Log.d(TAG + " PreExceute","On pre Exceute......");

            dialog = new ProgressDialog(getActivity());
            dialog.setMessage("Loading content..");
            dialog.setIndeterminate(true);
            dialog.setCancelable(false);
            dialog.show();
        

        protected String doInBackground(Void...arg0) 
            Log.d(TAG + " DoINBackGround","On doInBackground...");


            tasker(cat);

            return "You are at PostExecute";
        

        protected void onProgressUpdate(Integer...a)
            Log.d(TAG + " onProgressUpdate", "You are in progress update ... " + a[0]);
        

        protected void onPostExecute(String result) 
            Log.d(TAG + " onPostExecute", "" + result);
            dialog.dismiss();
            mPager.getAdapter().notifyDataSetChanged();
    


    public void tasker(String tag)
    
        try
            URL url = new URL("http://www.skyovnis.com/test.xml");
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(new InputSource(url.openStream()));
            doc.getDocumentElement().normalize();

            NodeList nodeList = doc.getElementsByTagName(tag);



            for (int i = 0; i < nodeList.getLength(); i++) 

                Node node = nodeList.item(i);

                Element fstElmnt = (Element) node;
                NodeList nameList = fstElmnt.getElementsByTagName("title");
                Element nameElement = (Element) nameList.item(0);
                nameList = nameElement.getChildNodes();
                list.add(((Node)nameList.item(0)).getNodeValue());

                NodeList websiteList = fstElmnt.getElementsByTagName("link");
                Element websiteElement = (Element) websiteList.item(0);
                websiteList = websiteElement.getChildNodes();
                links.add(((Node) websiteList.item(0)).getNodeValue());

                NodeList imgLinks = fstElmnt.getElementsByTagName("linkp");
                Element imgElement = (Element) imgLinks.item(0);
                imgLinks = imgElement.getChildNodes();
                linksp.add(((Node) imgLinks.item(0)).getNodeValue());
            

            bmps = new Bitmap[linksp.size()];
            String[] imgs = (String[])linksp.toArray(new String[linksp.size()]);
            for(int i = 0; i < linksp.size(); i++)
                URL urls = new URL(imgs[i]);
                bmps[i] = BitmapFactory.decodeStream(urls.openConnection().getInputStream());
            

            org.jsoup.nodes.Document docs = Jsoup.connect("http://www.skyovnis.com/arecibo-message/").get();
            org.jsoup.select.Elements newsHeadlines =  docs.select("div.entry-content p");

            for(org.jsoup.nodes.Element ele : newsHeadlines)
                hello = hello + ele.text();
            

        
            catch(Exception e)
                System.out.println(e);
            
    
  

基本上,根据我传递的数据,AsyncTask 将运行并从 xml 文件中读取。我传递给片段的字符串是从 xml 中查找的标记。一切正常,除了每个标签都显示相同的列表。

我在这里做错了什么?


编辑

ViewPager 适配器

mAdapter = new MyAdapter(getSupportFragmentManager());

mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);

mPager.setOnPageChangeListener(
   new ViewPager.SimpleOnPageChangeListener() 
         @Override
         public void onPageSelected(int position) 
           // When swiping between pages, select the
           // corresponding tab.
           getActionBar().setSelectedNavigationItem(position);
         
);

我的 CustomLister 类,

    package com.ovnis.skybeings;

import java.net.URL;
import java.util.List;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class CustomLister extends ArrayAdapter<String>

    private final Activity context;
    private final String[] web;
    private final Bitmap[] bmps;

    public CustomLister(Activity context, String[] web, Bitmap[] bmps)

        super(context, R.layout.list_compon, web);

        this.context = context;
        this.web = web;
        this.bmps = bmps;
    

    @Override
    public View getView(int pos, View view, ViewGroup parent)
        LayoutInflater inflater = context.getLayoutInflater();
        View rowView = inflater.inflate(R.layout.list_compon, null, true);
        TextView txtTitle = (TextView)rowView.findViewById(R.id.txt);

        ImageView imageView = (ImageView)rowView.findViewById(R.id.img);
        txtTitle.setText(web[pos]);

        imageView.setImageBitmap(bmps[pos]);
        return rowView;
    

我的适配器,

public static class MyAdapter extends FragmentPagerAdapter
    public MyAdapter(FragmentManager fm)
        super(fm);
    

    @Override
    public int getCount()
        return tabs.length;
    

    @Override
    public Fragment getItem(int i)
        switch(i)
        case 0:
            return new LoadMee();
        case 1:
            return new LoadFrag().newInstance("artufonews");
        case 2:
            return new LoadFrag().newInstance("artifos");
            default :
                return new LoadMee();
        
    

    @Override
    public int getItemPosition(Object object) 
        return POSITION_NONE;
    


【问题讨论】:

那是因为你没有收到之前发送的包。 @ישואוהבאותך 不,那不可能。你能看到我的 setMenuVisibility 代码吗?每当我选择选项卡时,它都会将我的应用程序标题设置为传递给片段的字符串。它也可以正常工作,它也改变了我的标题。唯一的问题是所有标签显示我是同一个列表 好吧,我错了。我以前没有看到你的cat = getArguments().getString("cat"); 。可能是因为您没有将CustomLister lister = new CustomLister(getActivity(), hell, bmps); 设置为您的类别? 您好,您可以显示ViewPager 的适配器的代码吗? 您仍然没有显示您的ViewPager 的适配器代码。我需要看看你的MyAdapter 课程。您正在调用 mPager.getAdapter().notifyDataSetChanged(); 以通知 MyAdapter 数据集已更改。单独通知MyAdapter 不会神奇地改变您的观点。我需要知道你是如何实现MyAdapter 【参考方案1】:

您需要捕获您之前发送的Bundle。执行以下操作可能可以解决您的问题:

@Override 
public void onActivityCreated(@Nullable Bundle savedInstanceState) 
  super.onActivityCreated(savedInstanceState);

  Bundle bundle = this.getArguments();
  if (bundle != null) 
    yourStringData= bundle.getString("cat", "DEFAULT_VALUE");
    setupYourUIWithTheData(yourStringData);
   else 
      Log.d(TAG, "bundle null");
  


private void setupYourUIWithTheData(String yourData) 
  // Process the received data here.
  // Show all related with the data here.

--- 更新 ---

要将值传递给Fragment,推荐的方法是向您的Fragment 添加静态方法,例如:

public static LoadFrag newInstance(String category) 
  LoadFrag fragment = new LoadFrag ();
  Bundle bundle = new Bundle();
  bundle.putString("cat", category);
  fragment.setArguments(bundle);
  return fragment;

那么你可以简单地调用它:

LoadFrag.newInstance("your_category");

【讨论】:

我现在试试这个 我不明白如何将Fragment 重构为使用newInstance() 可以帮助解决问题。 @Egor:好吧,在我添加答案之前,我想念阅读整个代码。认为 op 还没有实现读取 Bundle。我很抱歉。 不,它也不起作用。我的列表视图在两个 3 个标签中仍然有相同的项目 @ישואוהבאותך 我尝试了您编辑的答案,但无效。【参考方案2】:

出了什么问题:

在您的MyAdaptergetItemPosition() 方法中,您将返回POSITION_NONE。这意味着每当您调用MyAdapter#notifyDataSetChanged() 时,您将重新创建ViewPager 中的所有片段。您可以通过在 LoadFrag#onCreateView() 中添加日志消息来验证此行为。

public static class LoadFrag extends Fragment
    ...
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    // Add a Log message here to verify
    Log.d("LoadFrag", "In onCreateView");

        ...
    

    ...

请注意,在您的 AsyncTask 完成后,每次都会调用 onCreateView()。在您的AsyncTask 完成并显示您的ListView 的初始状态后,您基本上是在重新创建LoadFrag

如何解决

在您的CustomLister 中添加一个方法来更新您的数据。

public class CustomLister extends ArrayAdapter<String>
    ...
    public void updateData(String[] newWeb, Bitmap[] newBmps)
        web = newWeb;
        bmps = newBmps;
        notifyDataSetChanged();
    
    ...

在您的AsyncTask 中,获取您的CustomLister 的引用并使用新数据集调用lister.updateData(),而不是调用mPager.getAdapter().notifyDataSetChanged()

【讨论】:

这也没有解决我的问题,感谢您的更正。

以上是关于如何多次使用同一个片段?的主要内容,如果未能解决你的问题,请参考以下文章

防止同一片段多次堆叠 (addToBackStack)

多次调用片段 onCreateView

在后台堆栈中多次防止相同的片段

在 ViewPager 中多次使用一个 Fragment

对 glDrawElements 的多次调用是不是比在 GLSL 中对每个片段执行相同的计算更有效?

android fragment addToBackStack(null):如何将同一个片段添加到堆栈中一次?