在 ViewPager 中用另一个 Fragment 替换一个 Fragment
Posted
技术标签:
【中文标题】在 ViewPager 中用另一个 Fragment 替换一个 Fragment【英文标题】:Replace one Fragment with another in ViewPager 【发布时间】:2013-09-06 11:47:02 【问题描述】:当我尝试用 ViewPager
中的另一个替换 Fragment
时遇到一些问题。
现状
我有一个ViewPager
,有 3 页,每一页都是一个Fragment
。在第一页,我在ListFragment
(“FacturasFragment”)中有一个ListView
。当我点击该列表中的一项时,我使用onListItemClick
方法来处理该事件。
我想要什么
单击列表项时,我想将ListFragment
(包含发票列表)替换为另一个Fragment
(“DetallesFacturaFragment”,包含发票详细信息)。
当我在“DetallesFacturaFragment”并按返回Button
时,应返回ListFragment
。
页面之间的滚动不应改变第一个页面中显示的Fragment
。如果我在第一页中显示“DetallesFacturaFragment”并滚动到第二页,则返回第一页时应继续显示“DetallesFacturaFragment”。
代码
片段活动
public class TabsFacturasActivity extends SherlockFragmentActivity
private MyAdapter mAdapter;
private ViewPager mPager;
private PageIndicator mIndicator;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_pager);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mIndicator = (TitlePageIndicator)findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
private static class MyAdapter extends FragmentPagerAdapter
private String[] titles = "VER FACTURAS", "VER CONSUMO", "INTRODUCIR LECTURA" ;
private final FragmentManager mFragmentManager;
private Fragment mFragmentAtPos0;
private Context context;
public MyAdapter(FragmentManager fragmentManager)
super(fragmentManager);
mFragmentManager = fragmentManager;
@Override
public CharSequence getPageTitle(int position)
return titles[position];
@Override
public Fragment getItem(int position)
switch (position)
case 0: // Fragment # 0
return new FacturasFragment();
case 1: // Fragment # 1
return new ConsumoFragment();
case 2:// Fragment # 2
return new LecturaFragment();
return null;
@Override
public int getCount()
return titles.length;
@Override
public int getItemPosition(Object object)
if (object instanceof FacturasFragment && mFragmentAtPos0 instanceof DetallesFacturaFragment)
return POSITION_NONE;
return POSITION_UNCHANGED;
列表片段
public class FacturasFragment extends ListFragment
private ListView lista;
private ArrayList<TuplaFacturaWS> facturas;
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setRetainInstance(true);
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
View view = inflater.inflate(R.layout.activity_facturas, container, false);
facturas = myApplication.getFacturas();
lista = (ListView) view.findViewById(id.list);
MyAdapter myAdaptador = new MyAdapter(this, facturas);
setListAdapter(myAdaptador);
return view;
public void onListItemClick (ListView l, View v, int position, long id)
myApplication.setFacturaActual(position);
mostrarDatosFactura();
private void mostrarDatosFactura()
final DetallesFacturaFragment fragment = new DetallesFacturaFragment();
FragmentTransaction transaction = null;
transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.pager, fragment); //id of ViewPager
transaction.addToBackStack(null);
transaction.commit();
private class MyAdapter extends BaseAdapter
private final FacturasFragment actividad;
private final ArrayList<TuplaFacturaWS> facturas;
public MyAdapter(FacturasFragment facturasActivity, ArrayList<TuplaFacturaWS> facturas)
super();
this.actividad = facturasActivity;
this.facturas = facturas;
@Override
public View getView(int position, View convertView,
ViewGroup parent)
LayoutInflater inflater = actividad.getLayoutInflater(null);
View view = inflater.inflate(R.layout.list_row, null, true);
//Set data to view
return view;
@Override
public int getCount()
return facturas.size();
@Override
public Object getItem(int pos)
return facturas.get(pos);
@Override
public long getItemId(int position)
return position;
private OnClickListener checkListener = new OnClickListener()
@Override
public void onClick(DialogInterface dialog, int which)
// TODO Auto-generated method stub
;
片段
public class DetallesFacturaFragment extends SherlockFragment
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setRetainInstance(true);
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
View view = inflater.inflate(R.layout.activity_factura, container, false);
//Set data to view
return view;
目前,当我单击列表项时,第一页出现白色视图。我已经验证并执行了“DetallesFacturaFragment”的onCreateView
方法,但该页面上没有显示任何内容。
当我第一次点击列表项时,它会显示那个白屏。但是回到列表后,我必须单击两次才能显示白屏。
我一直在谷歌上搜索并查看了很多问题,但找不到任何人用完整的代码解决了问题。
【问题讨论】:
使用嵌套片段。第一个页面将是一个持有者片段,当您创建它时,您将在其中最初添加当前的ListFragment
。当您单击列表项时,然后将嵌套的 ListFragment
替换为同一持有者片段中的嵌套详细信息片段。不要在那个细节片段上使用setRetainInstance()
。
为什么要使用嵌套片段?我不知道。
因为您不能像您那样简单地将片段放在ViewPager
上。阅读嵌套片段,它们并不难使用。
我终于实现了我的目标,我可以替换Fragments
。
@Lyd 你介意分享一下你是怎么做到的吗!?
【参考方案1】:
花了这么多小时后,我在that answer 中找到了修改代码的正确解决方案。
它将ViewPager
第一页中的Fragment
替换为另一个,如果您从第二个Fragment
返回,则正确显示第一个Fragment
。在第一页显示Fragment
无关紧要,如果您从一个页面滑动到另一个页面,它不会更改其Fragment
。
这是我的代码:
片段活动
public class TabsFacturasActivity extends SherlockFragmentActivity
public void onBackPressed()
if(mPager.getCurrentItem() == 0)
if (mAdapter.getItem(0) instanceof DetallesFacturaFragment)
((DetallesFacturaFragment) mAdapter.getItem(0)).backPressed();
else if (mAdapter.getItem(0) instanceof FacturasFragment)
finish();
private static class MyAdapter extends FragmentPagerAdapter
private final class FirstPageListener implements
FirstPageFragmentListener
public void onSwitchToNextFragment()
mFragmentManager.beginTransaction().remove(mFragmentAtPos0)
.commit();
if (mFragmentAtPos0 instanceof FacturasFragment)
mFragmentAtPos0 = new DetallesFacturaFragment(listener);
else // Instance of NextFragment
mFragmentAtPos0 = new FacturasFragment(listener);
notifyDataSetChanged();
private String[] titles = "VER FACTURAS", "VER CONSUMO", "INTRODUCIR LECTURA" ;
private final FragmentManager mFragmentManager;
public Fragment mFragmentAtPos0;
private Context context;
FirstPageListener listener = new FirstPageListener();
public MyAdapter(FragmentManager fragmentManager)
super(fragmentManager);
mFragmentManager = fragmentManager;
@Override
public CharSequence getPageTitle(int position)
return titles[position];
@Override
public Fragment getItem(int position)
switch (position)
case 0: // Fragment # 0
if (mFragmentAtPos0 == null)
mFragmentAtPos0 = new FacturasFragment(listener);
return mFragmentAtPos0;
case 1: // Fragment # 1
return new ConsumoFragment();
case 2:// Fragment # 2
return new LecturaFragment();
return null;
@Override
public int getCount()
return titles.length;
@Override
public int getItemPosition(Object object)
if (object instanceof FacturasFragment &&
mFragmentAtPos0 instanceof DetallesFacturaFragment)
return POSITION_NONE;
if (object instanceof DetallesFacturaFragment &&
mFragmentAtPos0 instanceof FacturasFragment)
return POSITION_NONE;
return POSITION_UNCHANGED;
FirstPageFragmentListener
public interface FirstPageFragmentListener
void onSwitchToNextFragment();
FacturasFragment (FirstFragment)
public class FacturasFragment extends ListFragment implements FirstPageFragmentListener
static FirstPageFragmentListener firstPageListener;
public FacturasFragment()
public FacturasFragment(FirstPageFragmentListener listener)
firstPageListener = listener;
public void onListItemClick (ListView l, View v, int position, long id)
firstPageListener.onSwitchToNextFragment();
DetallesFacturaFragment (SecondFragment)
public class DetallesFacturaFragment extends SherlockFragment
static FirstPageFragmentListener firstPageListener;
public DetallesFacturaFragment()
public DetallesFacturaFragment(FirstPageFragmentListener listener)
firstPageListener = listener;
public void backPressed()
firstPageListener.onSwitchToNextFragment();
【讨论】:
你可以在github或其他@Lyd上发布教程或示例 你摇滚..!!感谢您的解决方案.. (+1).. bt 我有一个问题,为什么在使用 viewpager 时会出现这样的行为?? 我真的不知道...我花了好几个小时才发现它有效! 此解决方案存在一些问题。旋转设备时,将重新实例化 FragmentPagerAdapter 并将 mFragmentAtPos0 设置为 null。如果您在查看详细信息页面时点击后退按钮,adapter.getItem(0) 将创建第一个片段的新实例,测试类型和完成()。必须在这里完成一个更健壮的方式来实现 getItem()。 直到我在 FragmentRemove 上将commit
更改为 commitNow()
后,它才起作用,这里是 mFragmentManager.beginTransaction().remove(mFragmentAtPos0) .commit();
【参考方案2】:
解决 android 建议不要对片段使用非默认构造函数的小问题。您应该将构造函数更改为以下内容:
在 FacturasFragment 中:
public static FacturasFragment createInstance(FirstPageFragmentListener listener)
FacturasFragment facturasFragment = new FacturasFragment();
facturasFragment.firstPageListener = listener;
return facturasFragment;
然后你可以像这样从 getItem 函数调用它:
mFragmentAtPos0 = FacturasFragment.createInstance(listener);
在 DetallesFacturaFragment 中:
public static DetallesFacturaFragment createInstance(FirstPageFragmentListener listener)
DetallesFacturaFragment detallesFacturaFragment= new DetallesFacturaFragment();
detallesFacturaFragment.firstPageListener = listener;
return detallesFacturaFragment;
然后你可以像这样从 FirstPageListener.onSwitchToNextFragment() 函数调用它:
mFragmentAtPos0 = DetallesFacturaFragment.createInstance(listener);
【讨论】:
【参考方案3】:我找到了两种方法来替换 viewpager 中的片段。
方式一:通过更新 ViewPagerAdapter 方式2:使用根片段
方式1: 这样,我们需要从 viewpager 适配器的片段列表中更新片段,并通知片段有关更改。 例如:
private void changefragment(int position)
Fragment newFragment = CategoryPostFragment.newInstance();
adapter.fragments.set(position, newFragment);
adapter.notifyDataSetChanged();
viewPager.setCurrentItem(position);
这是视图页面适配器代码
import java.util.List;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.PagerAdapter;
import com.trickbd.trickbdapp.ui.activity.homeactivity.fragments.FavFragment;
import com.trickbd.trickbdapp.ui.activity.homeactivity.fragments.HomePostFragment;
public class ViewPagerAdapter extends FragmentStatePagerAdapter
public List<Fragment> fragments;
public ViewPagerAdapter(FragmentManager manager, List<Fragment> fragments)
super(manager);
this.fragments = fragments;
@NonNull
@Override
public Fragment getItem(int position)
if (fragments.get(position)!=null)
return fragments.get(position);
else
if (position==0)
return new HomePostFragment();
else
return new FavFragment();
@Override
public int getCount()
return fragments.size();
//method to add fragment
public void addFragment(Fragment fragment)
fragments.add(fragment);
@Override
public int getItemPosition(@NonNull Object object)
if (object instanceof FavFragment)
return POSITION_UNCHANGED; // don't force a reload
else
// POSITION_NONE means something like: this fragment is no longer valid
// triggering the ViewPager to re-build the instance of this fragment.
return POSITION_NONE;
这段代码我做得很好。当 FragmentStatePagerAdapter 的 super(manager) 方法在 api 28 中被弃用时,我开始担心。 当我使用“super(manager,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);”时以及在运行时替换应用程序崩溃的第一种方法。
另外,当我们从 viewpager 适配器调用 adapter.notifydatasetchanged() 时,还有一个缺点是效率不高。当我们尝试仅更改特定片段时,它正在重新创建整个 viewpager。
所以,方法 2 可以解决所有问题。
方式2: 在不将实际片段添加到 viewpager 的情况下,我们应该将根片段添加到 viewpager。在根片段中会添加实际的片段(之前我们在 viewpager 中添加。然后我们可以在没有 FragmentStatePagerAdapter 的帮助下轻松替换任何片段。 根片段代码:
Rootfragment.java
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentTransaction;
import com.trickbd.trickbdapp.R;
import dagger.android.support.DaggerFragment;
public class RootFragment extends DaggerFragment
public RootFragment()
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.root_layout, container, false);
if (getFragmentManager() != null)
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
transaction.replace(R.id.root_frame, new HomePostFragment());
transaction.commit();
return view;
root_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:id="@+id/root_frame">
</FrameLayout>
所以之前的替换代码将更新如下
private void changefragment(int position)
Fragment newFragment = CategoryPostFragment.newInstance();
replacefragment(newFragment);
viewPager.setCurrentItem(position);
public void replacefragment(Fragment fragment)
getSupportFragmentManager();
FragmentTransaction trans = getSupportFragmentManager()
.beginTransaction();
trans.replace(R.id.root_frame, fragment);
trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
trans.addToBackStack(null);
trans.commit();
通过这种方式,我们可以更有效地替换片段。
我们可以使用这段代码来获取当前在rootfragment中添加的片段。
if (getSupportFragmentManager().findFragmentById(R.id.root_frame) instanceof HomePostFragment)
HomePostFragment postFragment = (HomePostFragment) getSupportFragmentManager().findFragmentById(R.id.root_frame);
你可以了解更多关于这个here
【讨论】:
以上是关于在 ViewPager 中用另一个 Fragment 替换一个 Fragment的主要内容,如果未能解决你的问题,请参考以下文章
Fragment嵌套ViewPager切换后数据消失ViewPager空白问题
删除 viewPager2 中的片段使用 FragmentStateAdapter,但仍显示
如何通过在 ViewPager 中用手指滑动来禁用分页但仍然能够以编程方式滑动?