Risposte:
La risposta principale si basa su un nome generato dal framework. Se questo dovesse mai cambiare, allora non funzionerà più.
Che dire di questa soluzione, di priorità instantiateItem()
e destroyItem()
del tuo Fragment(State)PagerAdapter
:
public class MyPagerAdapter extends FragmentStatePagerAdapter {
SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return ...;
}
@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(...);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
}
Questo sembra funzionare per me quando ho a che fare con frammenti disponibili. I frammenti che non sono stati ancora istanziati, restituiranno null quando si chiama getRegisteredFragment
. Ma lo sto usando principalmente per Fragment
estrarre la corrente da ViewPager
: adapater.getRegisteredFragment(viewPager.getCurrentItem())
e questo non ritornerà null
.
Non sono a conoscenza di altri svantaggi di questa soluzione. Se ce ne sono, mi piacerebbe saperlo.
MyPagerAdapter
viene distrutto a causa del ciclo di vita (cioè ruota), non registerdFragments
andrà perso? Il Activity
/ Fragment
using MyPagerAdapter
dovrà salvarlo in onSaveInstanceState
, e quindi dovrà aggiornarlo con il nuovo riferimento FragmentManager
?
getItem
non viene più richiamato su rotate (per eventuali frags che sono già stati creati), poiché FragmentManager
ripristina gli stati di Fragments
esso contenuti nel cercapersone. Se instantiateItem
viene chiamato quando ciascuno Fragment
viene ripristinato, questa soluzione è in realtà più sicura e più a prova di futuro della mia o della risposta accettata. Prenderò in considerazione di provare questo da solo.
Per estrarre frammenti da un ViewPager ci sono molte risposte qui e su altri thread / blog SO correlati. Tutti quelli che ho visto sono rotti e generalmente sembrano rientrare in uno dei due tipi elencati di seguito. Ci sono alcune altre soluzioni valide se vuoi solo afferrare il frammento corrente, come questa altra risposta su questo thread.
Se si utilizza FragmentPagerAdapter
vedere di seguito. Se ne FragmentStatePagerAdapter
vale la pena guardare questo . Afferrare gli indici che non sono quelli attuali in un FragmentStateAdapter non è così utile in quanto per loro natura questi saranno completamente abbattuti, fuori dalla vista / fuori dai limiti offScreenLimit.
Sbagliato: mantenere il proprio elenco interno di frammenti, aggiunto a quando FragmentPagerAdapter.getItem()
viene chiamato
SparseArray
oMap
getItem
viene chiamato solo la prima volta che una pagina viene spostata (o ottenuta se ViewPager.setOffscreenPageLimit(x)
> 0) in ViewPager
, se l'hosting Activity
/ Fragment
viene ucciso o riavviato, l'interno SpaseArray
verrà cancellato quando viene ricreata la FragmentPagerActivity personalizzata, ma dietro le quinte il ViewPagers frammenti interni saranno ricreati, e getItem
saranno non essere chiamati per qualsiasi degli indici, così la possibilità di ottenere un frammento di indice verranno persi per sempre. Puoi renderlo conto salvando e ripristinando questi riferimenti a frammenti tramite FragmentManager.getFragment()
e putFragment
ma questo inizia a diventare IMHO disordinato.Sbagliato: costruisci il tuo tag id corrispondente a quello che viene usato sotto il cofanoFragmentPagerAdapter
e usalo per recuperare i frammenti di pagina dalFragmentManager
ViewPager
quello che potrebbe cambiare in qualsiasi momento o per qualsiasi versione del sistema operativo.Il metodo ricreato per questa soluzione è
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
ViewPager.instantiateItem()
Un approccio simile a quello getItem()
sopra, ma non interruzione del ciclo di vita, è quello di agganciare instantiateItem()
invece di getItem()
come il primo verrà chiamato ogni volta che l'indice viene creato / accessibile. Vedi questa risposta
FragmentViewPager
Costruisci la tua FragmentViewPager
classe dall'origine della più recente libreria di supporto e modifica il metodo utilizzato internamente per generare i tag di frammento. Puoi sostituirlo con il seguente. Questo ha il vantaggio di sapere che la creazione del tag non cambierà mai e di non fare affidamento su un metodo / API privato, che è sempre pericoloso.
/**
* @param containerViewId the ViewPager this adapter is being supplied to
* @param id pass in getItemId(position) as this is whats used internally in this class
* @return the tag used for this pages fragment
*/
public static String makeFragmentName(int containerViewId, long id) {
return "android:switcher:" + containerViewId + ":" + id;
}
Quindi, come dice il documento, quando si desidera afferrare un frammento utilizzato per un indice, chiamare semplicemente qualcosa come questo metodo (che è possibile inserire nella FragmentPagerAdapter
sottoclasse personalizzata o)) tenendo presente che il risultato potrebbe essere nullo se getItem non è stato ancora richiesto quella pagina cioè non è stata ancora creata.
/**
* @return may return null if the fragment has not been instantiated yet for that position - this depends on if the fragment has been viewed
* yet OR is a sibling covered by {@link android.support.v4.view.ViewPager#setOffscreenPageLimit(int)}. Can use this to call methods on
* the current positions fragment.
*/
public @Nullable Fragment getFragmentForPosition(int position)
{
String tag = makeFragmentName(mViewPager.getId(), getItemId(position));
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
return fragment;
}
Questa è una soluzione semplice e risolve i problemi nelle altre due soluzioni presenti ovunque sul Web
instantiateItem()
che è quando si desidera accedere a un frammento che non è stato ancora visitato dopo che si è verificato un evento del ciclo di vita e che è stato visitato prima dell'evento del ciclo di vita. Una piccola differenza che conosco, ma è quella che ha portato a un bug sottile nel mio programma, quindi mi sto guardando in questo.
getItemId(pos)
dentroFragmentStatePagerAdapter
Aggiungi i seguenti metodi al tuo FragmentPagerAdapter:
public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return mFragmentManager.findFragmentByTag(name);
}
private static String makeFragmentName(int viewId, int index) {
return "android:switcher:" + viewId + ":" + index;
}
getActiveFragment (0) deve funzionare.
Ecco la soluzione implementata in ViewPager https://gist.github.com/jacek-marchwicki/d6320ba9a910c514424d . Se qualcosa fallisce vedrai un buon registro degli arresti anomali.
Un'altra soluzione semplice:
public class MyPagerAdapter extends FragmentPagerAdapter {
private Fragment mCurrentFragment;
public Fragment getCurrentFragment() {
return mCurrentFragment;
}
//...
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
if (getCurrentFragment() != object) {
mCurrentFragment = ((Fragment) object);
}
super.setPrimaryItem(container, position, object);
}
}
setPrimaryItem()
viene chiamato dopo ViewPager.OnPageChangeListener#onPageSelected()
non posso usarlo :-(
So che questo ha alcune risposte, ma forse questo aiuterà qualcuno. Ho usato una soluzione relativamente semplice quando avevo bisogno di ottenere un Fragment
mio ViewPager
. Nel tuo Activity
o Fragment
tenendo premuto il ViewPager
, puoi usare questo codice per scorrere tutto Fragment
ciò che contiene.
FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
Fragment viewPagerFragment = fragmentPagerAdapter.getItem(i);
if(viewPagerFragment != null) {
// Do something with your Fragment
// Check viewPagerFragment.isResumed() if you intend on interacting with any views.
}
}
Se conosci la tua posizione Fragment
in ViewPager
, puoi semplicemente chiamare getItem(knownPosition)
.
Se non conosci la posizione del tuo Fragment
in ViewPager
, puoi avere i tuoi figli Fragments
implementare un'interfaccia con un metodo simile getUniqueId()
e usarlo per differenziarli. Oppure puoi scorrere tutto Fragments
e controllare il tipo di classe, ad esempioif(viewPagerFragment instanceof FragmentClassYouWant)
!!! MODIFICARE !!!
Ho scoperto che getItem
viene chiamato da un solo FragmentPagerAdapter
quando ognuno Fragment
deve essere creato la prima volta , dopodiché, sembra che Fragments
vengano riciclati usando il FragmentManager
. In questo modo, molte implementazioni diFragmentPagerAdapter
creare nuove Fragment
s in getItem
. Usando il mio metodo sopra, questo significa che creeremo nuove Fragment
s ogni volta che getItem
viene chiamato mentre esaminiamo tutti gli elementi in FragmentPagerAdapter
. A causa di ciò, ho trovato un approccio migliore, usando invece FragmentManager
per ottenere ciascuno Fragment
(usando la risposta accettata). Questa è una soluzione più completa e ha funzionato bene per me.
FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
String name = makeFragmentName(mViewPager.getId(), i);
Fragment viewPagerFragment = getChildFragmentManager().findFragmentByTag(name);
// OR Fragment viewPagerFragment = getFragmentManager().findFragmentByTag(name);
if(viewPagerFragment != null) {
// Do something with your Fragment
if (viewPagerFragment.isResumed()) {
// Interact with any views/data that must be alive
}
else {
// Flag something for update later, when this viewPagerFragment
// returns to onResume
}
}
}
E avrai bisogno di questo metodo.
private static String makeFragmentName(int viewId, int position) {
return "android:switcher:" + viewId + ":" + position;
}
ViewPager
stesso imposta quei tag. Questa soluzione è fragile perché si basa sulla conoscenza della convenzione di denominazione "nascosta" di ViewPager
. La risposta di Streets of Boston è una soluzione molto più completa, che ora uso nel mio progetto (anziché in questo approccio).
Nel mio caso, nessuna delle soluzioni di cui sopra ha funzionato.
Tuttavia, poiché sto utilizzando Child Fragment Manager in un frammento, è stato utilizzato quanto segue:
Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());
Funzionerà solo se i tuoi frammenti in Manager corrispondono all'elemento viewpager.
Per ottenere l' attuale frammento visibile da ViewPager . Sto usando questa semplice affermazione e funziona benissimo.
public Fragment getFragmentFromViewpager()
{
return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
}
L'ho gestito facendo prima un elenco di tutti i frammenti ( List<Fragment> fragments;
) che stavo per usare, quindi li ho aggiunti al cercapersone per semplificare la gestione del frammento attualmente visualizzato.
Così:
@Override
onCreate(){
//initialise the list of fragments
fragments = new Vector<Fragment>();
//fill up the list with out fragments
fragments.add(Fragment.instantiate(this, MainFragment.class.getName()));
fragments.add(Fragment.instantiate(this, MenuFragment.class.getName()));
fragments.add(Fragment.instantiate(this, StoresFragment.class.getName()));
fragments.add(Fragment.instantiate(this, AboutFragment.class.getName()));
fragments.add(Fragment.instantiate(this, ContactFragment.class.getName()));
//Set up the pager
pager = (ViewPager)findViewById(R.id.pager);
pager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments));
pager.setOffscreenPageLimit(4);
}
quindi questo può essere chiamato:
public Fragment getFragment(ViewPager pager){
Fragment theFragment = fragments.get(pager.getCurrentItem());
return theFragment;
}
così ho potuto scriverlo in un'istruzione if che sarebbe stata eseguita solo se fosse sul frammento corretto
Fragment tempFragment = getFragment();
if(tempFragment == MyFragmentNo2.class){
MyFragmentNo2 theFrag = (MyFragmentNo2) tempFragment;
//then you can do whatever with the fragment
theFrag.costomFunction();
}
ma questo è solo il mio approccio di hack e slash ma ha funzionato per me, lo uso faccio modifiche rilevanti al mio frammento attualmente visualizzato quando viene premuto il pulsante Indietro.
Questo si basa sulla risposta di Steven sopra. Ciò restituirà l'istanza effettiva del frammento che è già associato all'attività padre.
FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
Fragment viewPagerFragment = (Fragment) mViewPager.getAdapter().instantiateItem(mViewPager, i);
if(viewPagerFragment != null && viewPagerFragment.isAdded()) {
if (viewPagerFragment instanceof FragmentOne){
FragmentOne oneFragment = (FragmentOne) viewPagerFragment;
if (oneFragment != null){
oneFragment.update(); // your custom method
}
} else if (viewPagerFragment instanceof FragmentTwo){
FragmentTwo twoFragment = (FragmentTwo) viewPagerFragment;
if (twoFragment != null){
twoFragment.update(); // your custom method
}
}
}
}
Non sono riuscito a trovare un modo semplice e pulito per farlo. Tuttavia, il widget ViewPager è solo un altro ViewGroup, che ospita i tuoi frammenti. ViewPager ha questi frammenti come figli immediati. Quindi potresti semplicemente scorrere su di essi (usando .getChildCount () e .getChildAt ()), e vedere se l'istanza di frammento che stai cercando è attualmente caricata nel ViewPager e ottenere un riferimento ad esso. Ad esempio, è possibile utilizzare un campo ID univoco statico per distinguere i frammenti.
Nota che ViewPager potrebbe non aver caricato il frammento che stai cercando poiché è un contenitore di virtualizzazione come ListView.
FragmentPagerAdapter è la fabbrica dei frammenti. Per trovare un frammento basato sulla sua posizione se è ancora in memoria, usa questo:
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
Codice di esempio per API di supporto v4.
Non è necessario chiamare getItem()
o altri metodi in una fase successiva per ottenere il riferimento di un Fragment
host ospitato all'interno ViewPager
. Se desideri aggiornare alcuni dati all'interno, Fragment
utilizza questo approccio: Aggiorna ViewPager in modo dinamico?
La chiave è quella di impostare nuovi dati all'interno Adaper
e chiamare quelli notifyDataSetChanged()
che a loro volta chiameranno getItemPosition()
, passandoti un tuo riferimento Fragment
e dandoti la possibilità di aggiornarli. Tutti gli altri modi richiedono di fare riferimento a te stesso o ad altri hack che non sono una buona soluzione.
@Override
public int getItemPosition(Object object) {
if (object instanceof UpdateableFragment) {
((UpdateableFragment) object).update(xyzData);
}
//don't return POSITION_NONE, avoid fragment recreation.
return super.getItemPosition(object);
}
getItemPosition()
è un metodo nel tuo adattatore. Non lo chiamerai direttamente. Hai un riferimento del tuo adattatore, così chiami adapter.notifyDataSetChanged()
chi a sua volta chiamerà il getItemPosition()
tuo adattatore passando il riferimento dei tuoi Fragment
. Potete vedere una piena attuazione in azione qui stackoverflow.com/questions/19891828/...
Fragment
ma come ottenere un riferimento è discutibile. Altre soluzioni ottengono riferimenti FragmentManager
dall'uso di una stringa simile "android:switcher:"+id
o POSITION_NONE
dalla restituzione getItemosition()
causando la rigenerazione di tutti i frammenti.
Deve estendersi FragmentPagerAdapter
alla classe dell'adattatore ViewPager.
Se si utilizza FragmentStatePagerAdapter
allora non si sarà in grado di trovare il vostro Fragment
dal suoID
public static String makeFragmentName(int viewPagerId, int index) {
return "android:switcher:" + viewPagerId + ":" + index;
}
Come usare questo metodo: -
Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;
Ehi, ho risposto a questa domanda qui . Fondamentalmente, è necessario eseguire l'override
public Object instantiateItem (contenitore ViewGroup, posizione int)
metodo di FragmentStatePagerAdapter.
La soluzione migliore è utilizzare l'estensione che abbiamo creato in CodePath chiamato SmartFragmentStatePagerAdapter . Seguendo tale guida, ciò semplifica notevolmente il recupero di frammenti e il frammento attualmente selezionato da un ViewPager. Fa anche un lavoro migliore nella gestione della memoria dei frammenti incorporati nell'adattatore.
Il modo più semplice e più conciso. Se tutti i tuoi frammenti ViewPager
sono di classi diverse, puoi recuperarli e distinguerli come segue:
public class MyActivity extends Activity
{
@Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment.getClass() == MyFragment.class) {
mMyFragment = (MyFragment) fragment;
}
}
}
Ho implementato questo facilmente con un approccio un po 'diverso.
Il mio metodo FragmentAdapter.getItem personalizzato non ha restituito MyFragment (), ma l'istanza di MyFragment creata nel costruttore di FragmentAdapter.
Nella mia attività ho quindi ottenuto il frammento dall'adattatore, verificare se si tratta dell'istanza di frammento necessario, quindi eseguire il cast e utilizzare i metodi necessari.
Crea un ID risorsa intero in /values/integers.xml
<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>
Quindi nella funzione getItem di PagerAdapter:
public Fragment getItem(int position) {
Fragment fragment = null;
if (position == 0) {
fragment = FragmentOne.newInstance();
mViewPager.setTag(R.integer.page1,fragment);
}
else if (position == 1) {
fragment = FragmentTwo.newInstance();
mViewPager.setTag(R.integer.page2,fragment);
} else if (position == 2) {
fragment = FragmentThree.newInstance();
mViewPager.setTag(R.integer.page3,fragment);
}
return fragment;
}
Quindi nell'attività scrivi questa funzione per ottenere il riferimento al frammento:
private Fragment getFragmentByPosition(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = (Fragment) mViewPager.getTag(R.integer.page1);
break;
case 1:
fragment = (Fragment) mViewPager.getTag(R.integer.page2);
break;
case 2:
fragment = (Fragment) mViewPager.getTag(R.integer.page3);
break;
}
return fragment;
}
Ottieni il riferimento al frammento chiamando la funzione sopra e quindi esegui il cast sul frammento personalizzato:
Fragment fragment = getFragmentByPosition(position);
if (fragment != null) {
FragmentOne fragmentOne = (FragmentOne) fragment;
}
Modo semplice per scorrere i frammenti in Gestione frammenti. Trova viewpager, che ha l'argomento position position, inserito in PlaceholderFragment newInstance statico pubblico (int sectionNumber) .
public PlaceholderFragment getFragmentByPosition(Integer pos){
for(Fragment f:getChildFragmentManager().getFragments()){
if(f.getId()==R.id.viewpager && f.getArguments().getInt("SECTNUM") - 1 == pos) {
return (PlaceholderFragment) f;
}
}
return null;
}
Nel frammento
public int getArgument(){
return mPage;
{
public void update(){
}
In FragmentActivity
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for(Fragment f:fragments){
if((f instanceof PageFragment)&&(!f.isDetached())){
PageFragment pf = (PageFragment)f;
if(pf.getArgument()==pager.getCurrentItem())pf.update();
}
}
in TabLayout ci sono più schede per Frammento. puoi trovare il frammento per Tag usando l'indice del frammento.
Per es. l'indice per Fragment1 è 0, quindi nel findFragmentByTag()
metodo passa il tag per Viewpager.after usando fragmentTransaction che puoi aggiungere, sostituisci il frammento.
String tag = "android:switcher:" + R.id.viewPager + ":" + 0;
Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);
Ok per l'adattatore FragmentStatePagerAdapter
ho finanziato una soluzione:
nel tuo frammentoAttività:
ActionBar mActionBar = getSupportActionBar();
mActionBar.addTab(mActionBar.newTab().setText("TAB1").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment1.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB2").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment2.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB3").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment3.class.getName())));
viewPager = (STViewPager) super.findViewById(R.id.viewpager);
mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), mActionBar);
viewPager.setAdapter(this.mPagerAdapter);
e crea un metodo nella tua classe FragmentActivity - Quindi quel metodo ti dà accesso al tuo frammento, devi solo dargli la posizione del frammento che desideri:
public Fragment getActiveFragment(int position) {
String name = MyPagerAdapter.makeFragmentName(position);
return getSupportFragmentManager().findFragmentByTag(name);
}
nel tuo adattatore:
public class MyPagerAdapter extends FragmentStatePagerAdapter {
private final ActionBar actionBar;
private final FragmentManager fragmentManager;
public MyPagerAdapter(FragmentManager fragmentManager, com.actionbarsherlock.app.ActionBarActionBar mActionBar) {super(fragmentManager);
this.actionBar = mActionBar;
this.fragmentManager = fragmentManager;
}
@Override
public Fragment getItem(int position) {
getSupportFragmentManager().beginTransaction().add(mTchatDetailsFragment, makeFragmentName(position)).commit();
return (Fragment)this.actionBar.getTabAt(position);
}
@Override
public int getCount() {
return this.actionBar.getTabCount();
}
@Override
public CharSequence getPageTitle(int position) {
return this.actionBar.getTabAt(position).getText();
}
private static String makeFragmentName(int viewId, int index) {
return "android:fragment:" + index;
}
}
Fragment yourFragment = yourviewpageradapter.getItem(int index);
index
è il posto del frammento nell'adattatore come hai aggiunto fragment1
prima, quindi riprendi il fragment1
passaggio index
come 0 e così via per il resto
Fragment
in aWeakReference
per garantire di non impedire che un frammento distrutto venga raccolto? Sembra proprio la cosa giusta da fare ...