【问题标题】:Replace one Fragment with another in ViewPager在 ViewPager 中用另一个 Fragment 替换一个 Fragment
【发布时间】: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 你介意分享一下你是怎么做到的吗!?

标签: android listview android-fragments android-viewpager


【解决方案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_width="match_parent"
        android:layout_height="match_parent"
        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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多