wake-up-neo.com

Aktualisieren Sie die Daten in ListFragment als Teil von ViewPager

Ich verwende den v4-kompatiblen ViewPager in Android. Meine FragmentActivity enthält eine Reihe von Daten, die auf verschiedenen Seiten in meinem ViewPager auf unterschiedliche Weise angezeigt werden sollen. Bisher habe ich nur 3 Instanzen desselben ListFragments, aber in Zukunft werde ich 3 Instanzen verschiedener ListFragments haben. Der ViewPager befindet sich auf einem vertikalen Telefonbildschirm. Die Listen werden nicht nebeneinander angezeigt.

Jetzt startet eine Schaltfläche auf dem ListFragment eine separate Ganzseitenaktivität (über die FragmentActivity), die zurückgibt und FragmentActivity ändert die Daten, speichert sie und versucht dann, alle Ansichten in ihrem ViewPager zu aktualisieren. Hier stecke ich fest.

public class ProgressMainActivity extends FragmentActivity
{
    MyAdapter mAdapter;
    ViewPager mPager;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
    ...
        mAdapter = new MyAdapter(getSupportFragmentManager());

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

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        ...
        updateFragments();
        ...
    }
    public void updateFragments()
    {
        //Attempt 1:
        //mAdapter.notifyDataSetChanged();
        //mPager.setAdapter(mAdapter);

        //Attempt 2:
        //HomeListFragment fragment = (HomeListFragment) getSupportFragmentManager().findFragmentById(mAdapter.fragId[0]);
        //fragment.updateDisplay();
    }

    public static class MyAdapter extends FragmentPagerAdapter implements
         TitleProvider
    {
      int[] fragId = {0,0,0,0,0};
      public MyAdapter(FragmentManager fm)
      {
         super(fm);
      }
      @Override
      public String getTitle(int position){
         return titles[position];
      }
      @Override
      public int getCount(){
         return titles.length;
      }

      @Override
      public Fragment getItem(int position)
      {

         Fragment frag = HomeListFragment.newInstance(position);
         //Attempt 2:
         //fragId[position] = frag.getId();
         return frag;
      }

      @Override
      public int getItemPosition(Object object) {
         return POSITION_NONE; //To make notifyDataSetChanged() do something
     }
   }

    public class HomeListFragment extends ListFragment
    {
    ...
        public static HomeListFragment newInstance(int num)
        {
            HomeListFragment f = new HomeListFragment();
            ...
            return f;
        }
   ...

Wie Sie sehen, war mein erster Versuch, DataSetChanged für das gesamte FragmentPagerAdapter-Objekt zu benachrichtigen, und dies zeigte, dass die Daten manchmal aktualisiert wurden. Bei anderen wurde jedoch eine IllegalStateException angezeigt: Diese Aktion kann nach onSaveInstanceState nicht ausgeführt werden.

Bei meinem zweiten Versuch habe ich versucht, eine Update-Funktion in meinem ListFragment aufzurufen, aber getId in getItem hat 0 zurückgegeben

abrufen eines Verweises auf das Fragment von FragmentManager mithilfe von findFragmentById () oder findFragmentByTag ()

aber ich kenne den Tag oder die ID meiner Fragmente nicht! Ich habe ein Android: id = "@ + id/viewpager" für ViewPager und ein Android: id = "@ Android: id/list" für meine ListView im ListFragment-Layout, aber ich denke nicht, dass diese nützlich sind.

Wie kann ich also entweder: a) den gesamten ViewPager sicher auf einmal aktualisieren (im Idealfall den Benutzer zu der Seite zurückführen, auf der er zuvor war) - es ist in Ordnung, dass der Benutzer die Ansichtsänderung sieht. Oder rufen Sie vorzugsweise b) eine Funktion in jedem betroffenen ListFragment auf, um die ListView manuell zu aktualisieren.

Jede Hilfe wäre dankbar!

140
barkside

Versuchen Sie, das Tag jedes Mal aufzuzeichnen, wenn ein Fragment instanziiert wird.

public class MPagerAdapter extends FragmentPagerAdapter {
    private Map<Integer, String> mFragmentTags;
    private FragmentManager mFragmentManager;

    public MPagerAdapter(FragmentManager fm) {
        super(fm);
        mFragmentManager = fm;
        mFragmentTags = new HashMap<Integer, String>();
    }

    @Override
    public int getCount() {
        return 10;
    }

    @Override
    public Fragment getItem(int position) {
        return Fragment.instantiate(mContext, AFragment.class.getName(), null);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object obj = super.instantiateItem(container, position);
        if (obj instanceof Fragment) {
            // record the fragment tag here.
            Fragment f = (Fragment) obj;
            String tag = f.getTag();
            mFragmentTags.put(position, tag);
        }
        return obj;
    }

    public Fragment getFragment(int position) {
        String tag = mFragmentTags.get(position);
        if (tag == null)
            return null;
        return mFragmentManager.findFragmentByTag(tag);
    }
}
57
faylon

Die Antwort von Barkside funktioniert mit FragmentPagerAdapter, aber nicht mit FragmentStatePagerAdapter, da es keine Tags für Fragmente festlegt, die an FragmentManager übergeben werden.

Mit FragmentStatePagerAdapter können wir anscheinend mit dem instantiateItem(ViewGroup container, int position) -Aufruf auskommen. Es wird ein Verweis auf das Fragment an Position position zurückgegeben. Wenn FragmentStatePagerAdapter bereits einen Verweis auf das fragliche Fragment enthält, gibt instantiateItem nur einen Verweis auf dieses Fragment zurück und ruft getItem() nicht auf, um es erneut zu instanziieren.

Nehmen wir an, ich schaue gerade Fragment Nr. 50 an und möchte auf Fragment Nr. 49 zugreifen. Da sie nahe beieinander liegen, besteht eine gute Chance, dass die Nummer 49 bereits instanziiert ist. So,

ViewPager pager = findViewById(R.id.viewpager);
FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) pager.getAdapter();
MyFragment f49 = (MyFragment) a.instantiateItem(pager, 49)
252
Pēteris Caune

OK, ich glaube, ich habe in meiner eigenen Frage einen Weg gefunden, die Anfrage b) auszuführen, damit ich sie zum Nutzen anderer weitergeben kann. Das Fragment-Tag in einem ViewPager hat die Form "Android:switcher:VIEWPAGER_ID:INDEX", Wobei VIEWPAGER_ID Das R.id.viewpager Im XML-Layout und INDEX die Position im ViewPager ist. Wenn also die Position bekannt ist (zB 0), kann ich in updateFragments() ausführen:

      HomeListFragment fragment = 
          (HomeListFragment) getSupportFragmentManager().findFragmentByTag(
                       "Android:switcher:"+R.id.viewpager+":0");
      if(fragment != null)  // could be null if not instantiated yet
      {
         if(fragment.getView() != null) 
         {
            // no need to call if fragment's onDestroyView() 
            //has since been called.
            fragment.updateDisplay(); // do what updates are required
         }
      }

Ich habe keine Ahnung, ob dies eine gültige Methode ist, aber es wird funktionieren, bis etwas Besseres vorgeschlagen wird.

152
barkside

Wenn Sie mich fragen, ist die zweite Lösung auf der folgenden Seite, mit der Sie alle "aktiven" Fragmentseiten nachverfolgen können, besser: http://tamsler.blogspot.nl/2011/11/Android-viewpager-and -Fragmente-Teil-ii.html

Die Antwort von Barkside ist zu hacky für mich.

sie behalten alle "aktiven" Fragmentseiten im Auge. In diesem Fall verfolgen Sie die Fragmentseiten im FragmentStatePagerAdapter, der vom ViewPager verwendet wird.

private final SparseArray<Fragment> mPageReferences = new SparseArray<Fragment>();

public Fragment getItem(int index) {
    Fragment myFragment = MyFragment.newInstance();
    mPageReferences.put(index, myFragment);
    return myFragment;
}

Um zu vermeiden, dass ein Verweis auf "inaktive" Fragmentseiten beibehalten wird, müssen Sie die destroyItem (...) -Methode von FragmentStatePagerAdapter implementieren:

public void destroyItem(View container, int position, Object object) {
    super.destroyItem(container, position, object);
    mPageReferences.remove(position);
}

... und wenn Sie auf die aktuell sichtbare Seite zugreifen möchten, rufen Sie an:

int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);

... wo die getFragment (int) -Methode von MyAdapter so aussieht:

public MyFragment getFragment(int key) {
    return mPageReferences.get(key);
}

"

35
Frank

Okay, nachdem ich die Methode von @barkside oben getestet hatte, konnte ich sie nicht für meine Anwendung verwenden. Dann fiel mir ein, dass die IOSched2012-App auch ein viewpager verwendet, und hier habe ich meine Lösung gefunden. Es werden keine Fragment-IDs oder Tags verwendet, da diese von viewpager nicht auf leicht zugängliche Weise gespeichert werden.

Hier ist das wichtige Teile der IOSched Apps HomeActivity. Achten Sie besonders auf den Kommentar , da darin der Schlüssel liegt:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    // Since the pager fragments don't have known tags or IDs, the only way to persist the
    // reference is to use putFragment/getFragment. Remember, we're not persisting the exact
    // Fragment instance. This mechanism simply gives us a way to persist access to the
    // 'current' fragment instance for the given fragment (which changes across orientation
    // changes).
    //
    // The outcome of all this is that the "Refresh" menu button refreshes the stream across
    // orientation changes.
    if (mSocialStreamFragment != null) {
        getSupportFragmentManager().putFragment(outState, "stream_fragment",
                mSocialStreamFragment);
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    if (mSocialStreamFragment == null) {
        mSocialStreamFragment = (SocialStreamFragment) getSupportFragmentManager()
                .getFragment(savedInstanceState, "stream_fragment");
    }
}

Und speichere Instanzen von dir Fragments in FragmentPagerAdapter wie folgt:

    private class HomePagerAdapter extends FragmentPagerAdapter {
    public HomePagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return (mMyScheduleFragment = new MyScheduleFragment());

            case 1:
                return (mExploreFragment = new ExploreFragment());

            case 2:
                return (mSocialStreamFragment = new SocialStreamFragment());
        }
        return null;
    }

Denken Sie auch daran, Ihre Fragment Aufrufe wie folgt zu schützen:

    if (mSocialStreamFragment != null) {
        mSocialStreamFragment.refresh();
    }
13
Ryan R

Sie können FragmentPagerAdapter kopieren und einige Quellcodes ändern, indem Sie die Methode getTag() hinzufügen

beispielsweise

public abstract class AppFragmentPagerAdapter extends PagerAdapter {
private static final String TAG = "FragmentPagerAdapter";
private static final boolean DEBUG = false;

private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private Fragment mCurrentPrimaryItem = null;

public AppFragmentPagerAdapter(FragmentManager fm) {
    mFragmentManager = fm;
}


public abstract Fragment getItem(int position);

@Override
public void startUpdate(ViewGroup container) {
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }

    final long itemId = getItemId(position);


    String name = getTag(position);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);

        mCurTransaction.add(container.getId(), fragment,
                getTag(position));
    }
    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
    }

    return fragment;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
            + " v=" + ((Fragment) object).getView());
    mCurTransaction.detach((Fragment) object);
}

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    Fragment fragment = (Fragment) object;
    if (fragment != mCurrentPrimaryItem) {
        if (mCurrentPrimaryItem != null) {
            mCurrentPrimaryItem.setMenuVisibility(false);
            mCurrentPrimaryItem.setUserVisibleHint(false);
        }
        if (fragment != null) {
            fragment.setMenuVisibility(true);
            fragment.setUserVisibleHint(true);
        }
        mCurrentPrimaryItem = fragment;
    }
}

@Override
public void finishUpdate(ViewGroup container) {
    if (mCurTransaction != null) {
        mCurTransaction.commitAllowingStateLoss();
        mCurTransaction = null;
        mFragmentManager.executePendingTransactions();
    }
}

@Override
public boolean isViewFromObject(View view, Object object) {
    return ((Fragment) object).getView() == view;
}

@Override
public Parcelable saveState() {
    return null;
}

@Override
public void restoreState(Parcelable state, ClassLoader loader) {
}


public long getItemId(int position) {
    return position;
}

private static String makeFragmentName(int viewId, long id) {
    return "Android:switcher:" + viewId + ":" + id;
}

protected abstract String getTag(int position);
}

dann erweitern Sie es, überschreiben Sie diese abstrakte Methode, müssen keine Angst vor Android Gruppenwechsel

FragmentPageAdapter zukünftiger Quellcode

 class TimeLinePagerAdapter extends AppFragmentPagerAdapter {


    List<Fragment> list = new ArrayList<Fragment>();


    public TimeLinePagerAdapter(FragmentManager fm) {
        super(fm);
        list.add(new FriendsTimeLineFragment());
        list.add(new MentionsTimeLineFragment());
        list.add(new CommentsTimeLineFragment());
    }


    public Fragment getItem(int position) {
        return list.get(position);
    }

    @Override
    protected String getTag(int position) {
        List<String> tagList = new ArrayList<String>();
        tagList.add(FriendsTimeLineFragment.class.getName());
        tagList.add(MentionsTimeLineFragment.class.getName());
        tagList.add(CommentsTimeLineFragment.class.getName());
        return tagList.get(position);
    }


    @Override
    public int getCount() {
        return list.size();
    }


}
2
Jiang Qi

Funktioniert auch ohne Probleme:

irgendwo im Layout des Seitenfragments:

<FrameLayout Android:layout_width="0dp" Android:layout_height="0dp" Android:visibility="gone" Android:id="@+id/fragment_reference">
     <View Android:layout_width="0dp" Android:layout_height="0dp" Android:visibility="gone"/>
</FrameLayout>

in fragment's onCreateView ():

...
View root = inflater.inflate(R.layout.fragment_page, container, false);
ViewGroup ref = (ViewGroup)root.findViewById(R.id.fragment_reference);
ref.setTag(this);
ref.getChildAt(0).setTag("fragment:" + pageIndex);
return root;

und Methode, um Fragment von ViewPager zurückzugeben, falls vorhanden:

public Fragment getFragment(int pageIndex) {        
        View w = mViewPager.findViewWithTag("fragment:" + pageIndex);
        if (w == null) return null;
        View r = (View) w.getParent();
        return (Fragment) r.getTag();
}
1
Hox

Alternativ können Sie die setPrimaryItem -Methode von FragmentPagerAdapter wie folgt überschreiben:

public void setPrimaryItem(ViewGroup container, int position, Object object) { 
    if (mCurrentFragment != object) {
        mCurrentFragment = (Fragment) object; //Keep reference to object
        ((MyInterface)mCurrentFragment).viewDidAppear();//Or call a method on the fragment
    }

    super.setPrimaryItem(container, position, object);
}

public Fragment getCurrentFragment(){
    return mCurrentFragment;
}
1
Vlad Spreys

Ich möchte meinen Ansatz angeben, falls er jemand anderem helfen kann:

Dies ist mein Pager-Adapter:

 public class CustomPagerAdapter extends FragmentStatePagerAdapter{
    private Fragment[] fragments;

    public CustomPagerAdapter(FragmentManager fm) {
        super(fm);
        fragments = new Fragment[]{
                new FragmentA(),
                new FragmentB()
        };
    }

    @Override
    public Fragment getItem(int arg0) {
        return fragments[arg0];
    }

    @Override
    public int getCount() {
        return fragments.length;
    }

}

In meiner Tätigkeit habe ich:

public class MainActivity {
        private ViewPager view_pager;
        private CustomPagerAdapter adapter;


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

        adapter = new CustomPagerAdapter(getSupportFragmentManager());
        view_pager = (ViewPager) findViewById(R.id.pager);
        view_pager.setAdapter(adapter);
        view_pager.setOnPageChangeListener(this);
          ...
         }

}

Um das aktuelle Fragment zu erhalten, mache ich Folgendes:

int index = view_pager.getCurrentItem();
Fragment currentFragment = adapter.getItem(index);
0
kiduxa

Dies ist meine Lösung, da ich meine Registerkarten nicht im Auge behalten und sie sowieso alle aktualisieren muss.

    Bundle b = new Bundle();
    b.putInt(Constants.SharedPreferenceKeys.NUM_QUERY_DAYS,numQueryDays);
    for(Android.support.v4.app.Fragment f:getSupportFragmentManager().getFragments()){
        if(f instanceof HomeTermFragment){
            ((HomeTermFragment) f).restartLoader(b);
        }
    }
0
abhi08638