在ViewPager中使用相同的片段,但片段每次都会有不同的布局

时间:2014-12-09 04:43:11

标签: java android android-fragments android-viewpager

我想保持我的应用程序很薄。

问题:我想重用我的Fragment类代码,在ViewPager中创建3个不同的实例,这些实例将有3个页面。每个Fragment都有不同的ImageView或背景Drawable。关于此的最佳做法是什么?我注意到使用像here这样的工厂方法似乎很好,还有其他选择吗?

我有一个Fragment,其中包含以下方法:

Fragment.java

public static Fragment newInstance(Context context) {
    FragmentTutorial f = new FragmentTutorial();
    Bundle args = new Bundle();

    return f;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment, null);
    return root;
}

我有一个ViewPagerAdapter类,它有以下几种方法:

ViewPagerAdapter.java

public ViewPagerAdapter(Context context, FragmentManager fm) {
    super(fm);
    mContext = context;
}

@Override
public Fragment getItem(int position) {
    return new FragmentTutorial().newInstance(mContext);
}

@Override
public int getCount() {
    return totalPage;
}

3 个答案:

答案 0 :(得分:7)

我发现“最好”的方式(我认为当然)是做以下事情:

  • 让片段包含设置可自定义数据的方法(背景,文本等)
    • 注意:首次创建片段时,请注意尝试加载数据。您可以在onCreateView()甚至运行之前设置数据,或者在其他时候可以在onCreateView()之后运行。我个人使用布尔值来检查数据是否已设置。在onCreateView() [或onActivityCreated()]内,我检查数据是否已经设置。如果有,则加载数据。或者,在设置数据时,我会检查是否已经创建/缓存了视图。这是通过简单地使用变量来缓存数据来完成的,比如private ImageView mBackgroundView。如果视图不为null,那么我可以安全地在视图上设置数据。
    • 以上也是使用newInstance的替代方法,尽管这两种方法都运行良好。但是,为了获得更大的灵活性,我只使用newInstance,如果a)数据在片段必须插入之前已经知道,b)数据不需要根据来自其他地方的输入进行更改。
  • 让ViewPager处理所有数据
    • 传递所有数据 - 一个ImageViews列表,一个字符串数组,定义所有数据在资源中的位置等 - 一开始[比如说,在构造函数中]
    • 让ViewPager创建片段的ArrayList - 尽可能早地设置每个片段(比如首先获取所有数据)并将其添加到列表中
    • getCount()只使用列表的大小
    • getItem()只获取该位置列表中的项目
      • 注意:如果您有任何动态数据,请在getItem()方法中进行设置。此外,您还可以在运行时添加更多数据+片段[只需通知适配器数据集已更改]

基本上,片段就像一个简单的仆人 - 它只是最不必要的工作。如果它不必处理选择数据,那就更好了。因此它会更加灵活。只需给出在片段上适当设置数据/视图的方法。现在,ArrayAdapter可以完成所有肮脏的工作,管理数据并将其提供给适当的片段。利用这一点。

现在,请注意,这假设您要使用单个布局,但希望更改该布局的不同方面(文本,背景等)。如果你想制作一个可以使用任何类型的定义布局的主片段类,你可以注意到它会降低运行时灵活性(你如何将文本或背景更改为你从互联网上获得的东西?你根本不能如果你只能定义并从预先设定的布局中选择。)

无论哪种方式,ArrayAdapter都应该处理所有不同的数据,而片段只是按照设计的方式进行,最好是以更灵活的方式。

修改 这是我最近实现这种模式的项目。请注意,它有更多,所以我会在早上/下午用一些不那么伪的伪代码替换它。

  • ViewPager [对我尝试做的所有不同事情有点草率,包括从FragmentStatePagerAdapter扩展而不实际使用StatePagerAdapter的任何特定功能。换句话说,我仍然需要处理所有生命周期实现]
  • Fragment [也可能有点草率但仍显示模式]
  • 使用ViewPager的object(实际上是另一个片段)[它实际上是来自库的“VerticalViewpager”,但除了动画和方向以更改当前片段之外,它完全相同 - 特别是代码 - 明智]

<强> EDIT2: 这是上述模式的更多(如果过度)简化示例。

免责声明:以下代码绝对没有生命周期管理实施,并且是自14年8月左右以来未被触及的旧代码

现在,以下是代码的确切相关部分:

BaseFragment

// Note: Found out later can extend normal Fragments but must use v13 adapter
public class BaseFragment extends android.support.v4.app.Fragment {
    FrameLayout mMainLayout; // The parent layout
    int mNewColor = 0; // The new bg color, set from activity
    String mNewText = ""; // The new text, set from activity
    TextView mMainText;  // The only textview in this fragment

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the fragment's layout
        View view = inflater.inflate(R.layout.fragment_base,container,false);
        // Save the textview for further editing
        mMainText = (TextView) view.findViewById(R.id.textView);
        // Save the framelayout to change background color later
        mMainLayout = (FrameLayout) view.findViewById(R.id.mainLayout);

        return view;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // If there is new text or color assigned, set em
        if(mNewText != ""){
            mMainText.setText(mNewText);
        }
        if(mNewColor != 0){
            mMainLayout.setBackgroundColor(mNewColor);
        }
    }

    @Override
    public void onStart(){
        super.onStart();
    }

    // Simply indicate to change the text of the fragment
    public void changeText(String newText){
        mNewText=newText;
    }

    // Simply indicate to change the background color of the fragment
    public void changeBG(int color) {
        // If no color was passed, then set background to white
        if(color == 0)
        {
            mNewColor=getResources().getColor(R.color.white);
        }
        // else set the color to what was passed in
        else{
            mNewColor=color;
        }
    }
}

MyAdapter

class MyAdapter extends FragmentPagerAdapter{

    // Three simple fragments
    BaseFragment fragA;
    BaseFragment fragB;
    BaseFragment fragC;

    public MyAdapter(FragmentManager fm) {
        super(fm);
    }

    public void setFragments(Context c){

        // Set up the simple base fragments
        fragA = new BaseFragment();
        fragB = new BaseFragment();
        fragC = new BaseFragment();

        Resources res = c.getResources();

        fragA.changeText("This is Fragment A!");
        fragB.changeText("This is Fragment B!");
        fragC.changeText("This is Fragment C!");

        fragA.changeBG(res.getColor(R.color.dev_blue));
        fragB.changeBG(res.getColor(R.color.dev_green));
        fragC.changeBG(res.getColor(R.color.dev_orange));

    }

    @Override
    public Fragment getItem(int position) {
        // TODO: Make this more efficient, use a list or such, also comment more
        Fragment frag = null;
        if(position == 0){
            frag = fragA;
        }
        else if(position == 1){
            frag = fragB;
        }
        else if(position == 2){
            frag = fragC;
        }

        return frag;
    }

    @Override
    public int getCount() {
        return 3;
    }
}

答案 1 :(得分:3)

创建实例时,您需要传递某种id和newInstance()。根据该ID,您可以使用if..else来选择布局文件。

请参阅下面的参考码:

int id;
public static Fragment newInstance(Context context, int id) {
FragmentTutorial f = new FragmentTutorial();
Bundle args = new Bundle();
this.id = id;
return f;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(id == 1)
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment1, null);
else
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment2, null);
return root;
}

答案 2 :(得分:0)

您是否只是将字段引入Fragment类以解释背景中的差异等,并将它们添加到其构造函数中?然后在getItem中根据position的值实例化具有不同值的Fragment类。