Respostas:
A resposta principal depende de um nome que está sendo gerado pela estrutura. Se isso mudar, não funcionará mais.
Que tal esta solução, substituindo instantiateItem()e destroyItem()sua 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);
}
}
Isso parece funcionar para mim ao lidar com fragmentos que estão disponíveis. Fragmentos que ainda não foram instanciados retornarão nulos ao chamar getRegisteredFragment. Mas eu tenho usado isso principalmente para obter a corrente Fragmentdo ViewPager: adapater.getRegisteredFragment(viewPager.getCurrentItem())e isso não retornará null.
Não conheço outras desvantagens dessa solução. Se houver, eu gostaria de saber.
MyPagerAdapteré destruído devido ao ciclo de vida (ou seja, girar), não registerdFragmentsserá perdido? OActivity / Fragmentusing MyPagerAdapterprecisará salvá-lo onSaveInstanceStatee precisará atualizá-lo com a nova FragmentManagerref?
getItemnão é chamado novamente ao rodar (para quaisquer frags que já foram criados), pois FragmentManagerrestaura os estados do Fragmentsmesmo no pager. E seinstantiateItem for chamado quando cada um Fragmentfor restaurado, essa solução será de fato mais segura e mais futura do que a minha ou a resposta aceita. Vou considerar tentar isso sozinho.
Para pegar fragmentos de um ViewPager, existem muitas respostas aqui e em outros tópicos / blogs relacionados ao SO. Todo mundo que eu vi está quebrado, e eles geralmente parecem se encaixar em um dos dois tipos listados abaixo. Existem outras soluções válidas se você quiser apenas pegar o fragmento atual, como esta outra resposta neste tópico.
Se estiver usando FragmentPagerAdapter veja abaixo. Se usar o FragmentStatePagerAdapterseu vale a pena olhar para isso . Os índices de captura que não são os atuais em um FragmentStateAdapter não são tão úteis quanto, por natureza, serão completamente reduzidos e ficaram fora de vista / fora dos limites offScreenLimit.
Errado: mantenha sua própria lista interna de fragmentos, adicionada a quando FragmentPagerAdapter.getItem()é chamada
SparseArrayouMapgetItemé chamado apenas na primeira vez em que uma página é rolada para (ou obtida se seu ViewPager.setOffscreenPageLimit(x)> 0) no ViewPager, se o host Activity/Fragment for interrompida ou reiniciada, o interno SpaseArrayserá eliminado quando o FragmentPagerActivity personalizado for recriado, mas nos bastidores o ViewPagers fragmentos internos vai ser recriada e getItemvai NÃO ser chamado para qualquer um dos índices, de modo a capacidade de obter um fragmento de índice será perdido para sempre. Você pode explicar isso salvando e restaurando essas referências de fragmentos via FragmentManager.getFragment()e, putFragmentmas isso começa a ficar IMHO bagunçado.Errado: construa seu próprio ID de tag correspondente ao que é usado sob o capô no FragmentPagerAdapter e use-o para recuperar os fragmentos de página doFragmentManager
ViewPagerque poderia alterar a qualquer momento ou para qualquer versão do sistema operacional.O método recriado para esta solução é
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
ViewPager.instantiateItem()Uma abordagem semelhante à getItem()anterior, mas que não interrompe o ciclo de vida, é a de conectar-seinstantiateItem() invés degetItem() como o primeiro será chamado toda vez que o índice for criado / acessado. Veja esta resposta
FragmentViewPagerConstrua sua própria FragmentViewPagerclasse a partir da fonte da última lib de suporte e altere o método usado internamente para gerar as tags de fragmento. Você pode substituí-lo pelo abaixo. Isso tem a vantagem de que você sabe que a criação da tag nunca será alterada e que você não depende de uma API / método particular, o que é sempre perigoso.
/**
* @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;
}
Então, como o documento diz, quando você quiser pegar um fragmento usado para um índice, chame algo como este método (que você pode colocar no costume FragmentPagerAdapterou em uma subclasse) sabendo que o resultado pode ser nulo se getItem ainda não foi chamado. essa página, ou seja, ainda não foi criada.
/**
* @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;
}
Esta é uma solução simples e resolve os problemas das outras duas soluções encontradas em todos os lugares na Web
instantiateItem()quando você deseja acessar um fragmento que ainda não foi visitado após a ocorrência de um evento do ciclo de vida e foi visitado antes do evento do ciclo de vida. Uma pequena diferença que eu conheço, mas que levou a um bug sutil no meu programa, portanto, eu estou investigando isso.
getItemId(pos)interiorFragmentStatePagerAdapter
Adicione os próximos métodos ao seu 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) tem que funcionar.
Aqui está a solução implementada no ViewPager https://gist.github.com/jacek-marchwicki/d6320ba9a910c514424d . Se algo falhar, você verá um bom log de falha.
Outra solução simples:
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()é chamado depois ViewPager.OnPageChangeListener#onPageSelected() eu não posso usá-lo :-(
Eu sei que isso tem algumas respostas, mas talvez isso ajude alguém. Eu tenho usado uma solução relativamente simples quando eu precisava para obter um Fragmentdo meu ViewPager. No seu Activityou Fragmentsegurando o ViewPager, você pode usar esse código para percorrer cada Fragmentum deles.
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 você souber a sua posição Fragmentno ViewPager, você pode simplesmente ligar getItem(knownPosition).
Se você não sabe a sua posição Fragmentno ViewPager, você pode fazer com que seus filhos Fragmentsimplementem uma interface com um método como esse getUniqueId()e use isso para diferenciá-los. Ou você pode percorrer tudo Fragmentse verificar o tipo de classe, comoif(viewPagerFragment instanceof FragmentClassYouWant)
!!! EDIT !!!
Eu descobri que getItemsó é chamado por FragmentPagerAdapterquando cada um Fragmentprecisa ser criado na primeira vez , depois disso, parece que o Fragmentssão reciclados usando o FragmentManager. Dessa forma, muitas implementações de FragmentPagerAdaptercriação de novos Fragment s getItem. Usando o meu método acima, isso significa que criaremos novos Fragments cada vez que getItemfor chamado à medida que passarmos por todos os itens no FragmentPagerAdapter. Devido a isso, eu encontrei uma abordagem melhor, usando o FragmentManagerpara obter cada um Fragment(usando a resposta aceita). Esta é uma solução mais completa e está funcionando bem para mim.
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 você precisará deste método.
private static String makeFragmentName(int viewId, int position) {
return "android:switcher:" + viewId + ":" + position;
}
ViewPagerpróprio define essas tags. Esta solução é frágil porque depende do conhecimento da convenção de nomenclatura "oculta" do ViewPager. A resposta do Streets of Boston é uma solução muito mais abrangente, que agora uso no meu projeto (em vez dessa abordagem).
Para o meu caso, nenhuma das soluções acima funcionou.
No entanto, como estou usando o Gerenciador de fragmentos filho em um fragmento, o seguinte foi usado:
Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());
Isso funcionará apenas se seus fragmentos no Gerenciador corresponderem ao item do viewpager.
Para obter o fragmento Visible atual do ViewPager . Estou usando esta declaração simples e está funcionando bem.
public Fragment getFragmentFromViewpager()
{
return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
}
Eu lidei com isso primeiro fazendo uma lista de todos os fragmentos (List<Fragment> fragments; ) que eu iria usar e os adicionei ao pager, facilitando o manuseio do fragmento atualmente exibido.
Assim:
@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);
}
então isso pode ser chamado:
public Fragment getFragment(ViewPager pager){
Fragment theFragment = fragments.get(pager.getCurrentItem());
return theFragment;
}
então eu poderia jogá-lo em uma instrução if que só seria executada se estivesse no fragmento correto
Fragment tempFragment = getFragment();
if(tempFragment == MyFragmentNo2.class){
MyFragmentNo2 theFrag = (MyFragmentNo2) tempFragment;
//then you can do whatever with the fragment
theFrag.costomFunction();
}
mas essa é apenas a minha abordagem de hack e slash, mas funcionou para mim; eu a uso para fazer alterações relevantes no fragmento exibido no momento quando o botão Voltar é pressionado.
Isso é baseado na resposta de Steven acima. Isso retornará a instância real do fragmento que já está anexado à atividade pai.
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
}
}
}
}
Não consegui encontrar uma maneira simples e limpa de fazer isso. No entanto, o widget ViewPager é apenas outro ViewGroup, que hospeda seus fragmentos. O ViewPager possui esses fragmentos como filhos imediatos. Portanto, você pode iterar sobre eles (usando .getChildCount () e .getChildAt ()) e ver se a instância de fragmento que você está procurando está atualmente carregada no ViewPager e obter uma referência a ela. Por exemplo, você pode usar algum campo de ID exclusivo estático para diferenciar os fragmentos.
Observe que o ViewPager pode não ter carregado o fragmento que você está procurando, pois é um contêiner de virtualização como o ListView.
FragmentPagerAdapter é a fábrica dos fragmentos. Para encontrar um fragmento com base em sua posição, se ainda estiver na memória, use isto:
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
Código de amostra para a API de suporte da v4.
Você não precisa chamar getItem()ou algum outro método posteriormente para obter a referência de um Fragmenthost hospedado ViewPager. Se você deseja atualizar alguns dados internos Fragment, use esta abordagem: Atualizar o ViewPager dinamicamente?
A chave é definir novos dados dentro Adapere chamar, notifyDataSetChanged()que por sua vez chamará getItemPosition(), passando uma referência sua Fragmente dando a você a chance de atualizá-la. Todas as outras formas exigem que você mantenha referência a si mesmo ou a algum outro truque, o que não é uma boa solução.
@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()é um método no seu adaptador. Você não vai ligar diretamente. Você tem uma referência do seu adaptador, para adapter.notifyDataSetChanged()ligar e ligar getItemPosition()para o seu adaptador, passando a referência dos seus Fragments. Você pode ver uma implementação completa em ação aqui stackoverflow.com/questions/19891828/…
Fragmentmas como obter uma referência é passível de debate. Outras soluções obter referência de FragmentManagerusar uma corda semelhante a "android:switcher:"+idou retornando POSITION_NONEde getItemosition()causando todos os fragmentos para recriar-se.
Deve se estender FragmentPagerAdapterà sua classe de adaptador do ViewPager.
Se você usar FragmentStatePagerAdapter, não poderá encontrar o seu Fragmentpelo seuID
public static String makeFragmentName(int viewPagerId, int index) {
return "android:switcher:" + viewPagerId + ":" + index;
}
Como usar este método: -
Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;
Ei, eu respondi a esta pergunta aqui . Basicamente, você precisa substituir
public Object instantiateItem (container ViewGroup, int position)
método de FragmentStatePagerAdapter.
A melhor solução é usar a extensão que criamos no CodePath chamada SmartFragmentStatePagerAdapter . Seguindo esse guia, isso facilita a recuperação de fragmentos e do fragmento atualmente selecionado de um ViewPager. Ele também faz um trabalho melhor de gerenciar a memória dos fragmentos incorporados no adaptador.
A maneira mais fácil e concisa. Se todos os seus fragmentos ViewPagerforem de classes diferentes, você poderá recuperá-los e diferenciá-los da seguinte maneira:
public class MyActivity extends Activity
{
@Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment.getClass() == MyFragment.class) {
mMyFragment = (MyFragment) fragment;
}
}
}
Eu implementei isso facilmente com uma abordagem um pouco diferente.
Meu método FragmentAdapter.getItem personalizado não retornou novo MyFragment (), mas a instância de MyFragment que foi criada no construtor FragmentAdapter.
Na minha atividade, obtive o fragmento do adaptador, verifique se ele é instanceOf needed Fragment e, em seguida, projete e use os métodos necessários.
Crie um ID de recurso inteiro em /values/integers.xml
<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>
Em seguida, na função getItem do 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;
}
Em atividade, escreva esta função para obter uma referência de fragmento:
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;
}
Obtenha a referência do fragmento chamando a função acima e, em seguida, faça a conversão no seu fragmento personalizado:
Fragment fragment = getFragmentByPosition(position);
if (fragment != null) {
FragmentOne fragmentOne = (FragmentOne) fragment;
}
Maneira fácil de iterar sobre fragmentos no gerenciador de fragmentos. Localize o viewpager, que possui argumento de posição de seção, colocado em público estático PlaceholderFragment newInstance (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;
}
Em Fragmento
public int getArgument(){
return mPage;
{
public void update(){
}
Em 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();
}
}
No TabLayout, existem várias guias para Fragment. você pode encontrar o fragmento por Tag usando o índice do fragmento.
Por ex. o índice para o fragmento1 é 0, portanto, no findFragmentByTag()método, passe a tag para o Viewpager.after usando fragmentTransaction que você pode adicionar, substitua o fragmento.
String tag = "android:switcher:" + R.id.viewPager + ":" + 0;
Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);
Ok para o adaptador FragmentStatePagerAdapter , financio uma solução:
em seu FragmentActivity:
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 crie um método na sua classe FragmentActivity - para que esse método lhe dê acesso ao seu fragmento, você só precisa fornecer a posição do fragmento que deseja:
public Fragment getActiveFragment(int position) {
String name = MyPagerAdapter.makeFragmentName(position);
return getSupportFragmentManager().findFragmentByTag(name);
}
no seu adaptador:
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é o lugar do fragmento no adaptador como você adicionou fragment1primeiro, então volte a fragment1passar indexcomo 0 e assim por diante
Fragmentem umWeakReferencepara garantir que você não impedem que um fragmento destruída de ser lixo coletado? Apenas parece que a coisa certa a fazer ...