Domanda

Sto usando V4 ViewPager di compatibilità in Android. La mia frammentazione ha un sacco di dati che devono essere visualizzati in modi diversi su diverse pagine nel mio mirino. Finora ho solo 3 istanze dello stesso elenco, ma in futuro avrò 3 istanze di diversi frammenti di elenchi. ViewPager si trova su una schermata del telefono verticale, gli elenchi non sono fianco a fianco.

Ora un pulsante su ListFragment avvia un'attività separata a tutta pagina (tramite la frammentazione), che restituisce e la frammentazione modifica i dati, li salva, quindi tenta di aggiornare tutte le viste nel suo mirino. È qui, dove sono bloccato.

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;
        }
   ...

Ora, come puoi vedere, il mio primo tentativo è stato quello di notificaredatasetchange su tutto il frammentpageradapter, e questo ha mostrato di aggiornare i dati a volte, ma altri ho ottenuto unexception illegal -

Il mio secondo tentativo ha comportato il tentativo di chiamare una funzione di aggiornamento nel mio listfragment, ma getid in getItem è tornato 0. Secondo i documenti che ho provato da

Acquisizione di un riferimento al frammento di FragmentManager, usando findFragmentById () o findFragmentByTag ()

Ma non conosco il tag o l'ID dei miei frammenti! Ho un Android: Id = "@+Id/ViewPager" per ViewPager e un Android: id = "@Android: Id/List" per la miaview nel layout ListFragment, ma non credo che siano utili.

Quindi, come posso: a) aggiornare l'intero mirino in modo sicuro in una sola go (idealmente restituendo l'utente alla pagina su cui era prima) - va bene che l'utente veda la modifica della vista. O preferibilmente, b) Chiama una funzione in ciascun frammento elenco interessato per aggiornare manualmente ListView.

Qualsiasi aiuto sarebbe accettato con gratitudine!

È stato utile?

Soluzione

Prova a registrare il tag ogni volta che viene istanziata una fruizione.

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);
    }
}

Altri suggerimenti

La risposta di Barkside funziona con FragmentPagerAdapter Ma non funziona con FragmentStatePagerAdapter, perché non imposta i tag sui frammenti a cui passa FragmentManager.

Insieme a FragmentStatePagerAdapter Sembra che possiamo cavarsela, usando il suo instantiateItem(ViewGroup container, int position) chiamata. Restituisce riferimento al frammento in posizione position. Se FragmentStatePagerAdapter ha già riferimento al frammento in questione, instantiateItem restituisce solo riferimento a quel frammento e non chiama getItem() per istanziarlo di nuovo.

Quindi, supponiamo, sto attualmente guardando il frammento #50 e voglio accedere al frammento n. 49. Dal momento che sono vicini, ci sono buone probabilità che il numero 49 sia già istanziato. Così,

ViewPager pager = findViewById(R.id.viewpager);
FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) pager.getAdapter();
MyFragment f49 = (MyFragment) a.instantiateItem(pager, 49)

Ok, penso di aver trovato un modo per eseguire la richiesta b) nella mia domanda, quindi condividerò a beneficio degli altri. Il tag dei frammenti all'interno di un mirino è nella forma "android:switcher:VIEWPAGER_ID:INDEX", dove VIEWPAGER_ID è il R.id.viewpager Nel layout XML e l'indice è la posizione nel ViewPager. Quindi, se la posizione è nota (ad esempio 0), posso esibirmi in updateFragments():

      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
         }
      }

Non ho idea se questo è un modo valido di farlo, ma lo farà fino a quando non viene suggerito qualcosa di meglio.

Se me lo chiedi, la seconda soluzione nella pagina seguente, tenere traccia di tutte le pagine dei frammenti "attivi", è meglio: http://tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part-ii.html

La risposta di Barkside è troppo hacky per me.

Tieni traccia di tutte le pagine dei frammenti "attivi". In questo caso, si tiene traccia delle pagine dei frammenti nel frammentstatepageradapter, che viene utilizzato dal ViewPager.

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

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

Per evitare di tenere un riferimento a pagine di frammenti "inattive", dobbiamo implementare il metodo DestroyItem (...) di FragmentStatePageradapter:

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

... e quando devi accedere alla pagina attualmente visibile, quindi chiami:

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

... dove il metodo GetFragment (int) del myadapter assomiglia a questo:

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

"

Ok, dopo aver testato il metodo di @Barkside sopra, non sono riuscito a farlo funzionare con la mia applicazione. Poi mi sono ricordato che il App iosched2012 usa un viewpager Inoltre, ed è lì che ho trovato la mia soluzione. Non utilizza ID o tag frammenti in quanto non sono archiviati da viewpager in modo facilmente accessibile.

Ecco il parti importanti Dalla casa delle app iosched. Prestare particolare attenzione a commento, come in essa sta la chiave.:

@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");
    }
}

E memorizza istanze di te Fragments nel FragmentPagerAdapter così:

    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;
    }

Inoltre, ricorda di proteggere il tuo Fragment Chiamate come così:

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

Puoi copiare FragmentPagerAdapter e modifica un po 'di codice sorgente, aggiungi getTag() metodo

Per esempio

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);
}

Quindi estenderlo, sostituire questo metodo astratto, non è necessario avere paura del cambiamento di gruppo Android

FragmentPageAdapter Codice sorgente in futuro

 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();
    }


}

Funziona anche senza problemi:

Da qualche parte nella pagina del frammento di pagina:

<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 Framment'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;

e metodo per restituire il frammento da ViewPager, se esiste:

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();
}

In alternativa puoi sovrascrivere setPrimaryItem metodo da FragmentPagerAdapter così:

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;
}

Voglio dare il mio approccio nel caso in cui possa aiutare chiunque altro:

Questo è il mio adattatore di cercapersoni:

 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;
    }

}

Nella mia attività ho:

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);
          ...
         }

}

Quindi per ottenere il frammento attuale quello che faccio è:

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

Questa è la mia soluzione poiché non ho bisogno di tenere traccia delle mie schede e devo comunque rinfrescarle.

    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);
        }
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top