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

And*_*oid 4 java android android-fragments android-viewpager

我想保持我的应用程序薄.

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

我有一个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;
}
Run Code Online (Sandbox Code Playgroud)

我有一个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;
}
Run Code Online (Sandbox Code Playgroud)

Jaw*_*wad 7

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

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

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

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

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

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

  • ViewPager [有点 with我想要做的所有不同的事情,包括从FragmentStatePagerAdapter扩展而不实际使用StatePagerAdapter的任何特定功能.换句话说,我仍然需要处理各地的生命周期实现]
  • 片段 [也可能有点草率但显示模式仍然]
  • 使用ViewPager 的对象(实际上是另一个片段)[它实际上是来自库的"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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)