java 在 ViewPager 中使用相同的片段,但片段每次都会有不同的布局
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27371565/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Use same fragment in ViewPager but fragment will have different layout each time
提问by AndyRoid
I want to keep my application thin.
我想让我的应用程序保持精简。
Problem:I would like to reuse my Fragment
class code to create 3 different instances in the ViewPager
which will have 3 pages. Each Fragment
will have a different ImageView
or background Drawable
. What are best practices regarding this? I noticed that using factory methods like hereseem to be good, any other alternatives?
问题:我想重用我的Fragment
类代码来创建 3 个不同的实例,ViewPager
其中有 3 个页面。每个人Fragment
都会有不同的ImageView
或背景Drawable
。这方面的最佳做法是什么?我注意到使用像这里这样的工厂方法似乎很好,还有其他选择吗?
I have one Fragment
which has the following methods:
我有一个Fragment
有以下方法:
Fragment.java
片段.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;
}
I have a ViewPagerAdapter class which has the following methods:
我有一个 ViewPagerAdapter 类,它具有以下方法:
ViewPagerAdapter.java
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;
}
回答by Jawad
What I've found is the "best" way to do it (in my opinion, of course) is to do the following:
我发现做这件事的“最佳”方法(当然在我看来)是执行以下操作:
- Have the fragment contain methods to set the customizable data (background, text, etc)
- Note: Be careful of trying to load the data in when first creating the fragment. You may be able to set the data before
onCreateView()
even runs, or at other times it may run after onCreateView(). I personally use a boolean to check if the data has been set. InsideonCreateView()
[oronActivityCreated()
], I check if the data has been set already. If it has, load in the data. Alternatively, while setting the data, I check if the views have been created/cached already. This is done by simply having variables to cache the data, sayprivate ImageView mBackgroundView
. If the view is not null, then I safely set the data on the views. - The above is also an alternative to using newInstance, although both methods work pretty well. However, for more flexibility, I only use newInstance if a) the data is already known before the fragment has to be inserted and b) the data doesn't need to change according to input from elsewhere much.
- Note: Be careful of trying to load the data in when first creating the fragment. You may be able to set the data before
- Let the ViewPager handle all the data
- Pass in all the data - a list of ImageViews, a array of Strings, define where all the data is in Resources, etc - at the very beginning [say, in the constructor]
- Have the ViewPager create an ArrayList of the fragments- set up each fragment as early as possible (say when first getting all the data) and add it to the list
- Let
getCount()
just use the size of the list - Let
getItem()
just get the item in the list at the position- Note: If you have any dynamic data, set it up in the getItem() method. Furthermore, you can always add more data+fragments during runtime as well [just notify the adapter that the dataset has been changed]
- 让片段包含设置可自定义数据(背景、文本等)的方法
- 注意:第一次创建片段时要小心尝试加载数据。您
onCreateView()
甚至可以在运行之前设置数据,或者在其他时候它可以在 onCreateView() 之后运行。我个人使用布尔值来检查数据是否已设置。在onCreateView()
[oronActivityCreated()
] 中,我检查数据是否已经设置。如果有,加载数据。或者,在设置数据时,我会检查是否已经创建/缓存了视图。这是通过简单地使用变量来缓存数据来完成的,比如private ImageView mBackgroundView
。如果视图不为空,那么我可以安全地在视图上设置数据。 - 以上也是使用 newInstance 的替代方法,尽管这两种方法都运行良好。但是,为了获得更大的灵活性,我只在以下情况下使用 newInstance:a) 在必须插入片段之前已经知道数据,并且 b) 数据不需要根据来自其他地方的输入进行太多更改。
- 注意:第一次创建片段时要小心尝试加载数据。您
- 让 ViewPager 处理所有数据
- 传入所有数据 - 一个 ImageView 列表,一个字符串数组,定义所有数据在资源中的位置等 - 在最开始时[例如,在构造函数中]
- 让 ViewPager 创建片段的 ArrayList - 尽早设置每个片段(比如第一次获取所有数据时)并将其添加到列表中
- 让我们
getCount()
使用列表的大小 - 让我们
getItem()
在该位置获取列表中的项目- 注意:如果您有任何动态数据,请在 getItem() 方法中进行设置。此外,您始终可以在运行时添加更多数据+片段[只需通知适配器数据集已更改]
Essentially, the fragment is like a simple servant- it does simply the least work necessary. If it doesn't have to handle choosing the data, all the better. It'll thus be far more flexible. Just give methods to set the data/views appropriately on the fragment. Now, the ArrayAdapter can do all the grimy hard work with managing the data and giving it to the appropriate fragment. Take advantage of that.
本质上,片段就像一个简单的仆人——它只做最不需要的工作。如果它不必处理选择数据,那就更好了。因此它会更加灵活。只需给出在片段上适当设置数据/视图的方法。现在,ArrayAdapter 可以完成所有繁重的工作,包括管理数据并将其提供给适当的片段。好好利用这一点。
Now, note that this is assuming you want to use a single layout but want to change different aspects of that layout (texts, background, etc). If you want to make a master fragment class that can use any sort of defined layout, you can but note that it decreases the runtime flexibility (how can you change the text or background to something you get from the internet? You simply can't if you only can define and choose from pre-set layouts).
现在,请注意,这是假设您想要使用单个布局但想要更改该布局的不同方面(文本、背景等)。如果你想制作一个可以使用任何类型的定义布局的主片段类,你可以但要注意它会降低运行时的灵活性(你如何将文本或背景更改为你从互联网上获得的东西?你根本不能如果您只能定义和选择预设布局)。
Either way, the ArrayAdapter should take care of all the different data while the fragment simply does as it's designed to do, in a more flexible manner preferably.
无论哪种方式,ArrayAdapter 都应该处理所有不同的数据,而片段只是按照其设计的方式执行,最好以更灵活的方式执行。
Edit:Here is the project where I most recently implemented this sort of pattern. Note that it has far more to it, so I'll replace it with some not-so-pseudo pseudo-code in the morning/afternoon.
编辑:这是我最近实现这种模式的项目。请注意,它还有更多内容,因此我将在早上/下午用一些不那么伪的伪代码替换它。
- ViewPager[a bit sloppy with all the different things I was trying to do, including extending from a FragmentStatePagerAdapter without actually using any of the specific features of a StatePagerAdapter. In other words, I still need to work on the lifecycle implementations everywhere]
- Fragment[Also may be a bit sloppy but shows the pattern still]
- The object(actually another fragment) that uses the ViewPager [it's actually a "VerticalViewpager" from a library, but other than the animations and direction to change the current fragment, it's exactly the same- particularly code-wise]
- ViewPager[对我尝试做的所有不同的事情都有些草率,包括从 FragmentStatePagerAdapter 扩展而不实际使用 StatePagerAdapter 的任何特定功能。换句话说,我仍然需要在各处进行生命周期实现]
- Fragment[也可能有点草率但仍然显示模式]
- 使用 ViewPager的对象(实际上是另一个片段)[它实际上是一个库中的“VerticalViewpager”,但除了动画和方向来改变当前片段之外,它是完全相同的——尤其是代码方面]
Edit2:Here is a more (if overly) simplified example of the pattern described above.
Edit2:这是上述模式的更(如果过于)简化的示例。
Disclaimer: The following code has absolutely no lifecycle management implementations and is older code that has been untouched since around August '14
免责声明:以下代码绝对没有生命周期管理实现,并且是自 14 年 8 月左右以来未受影响的旧代码
- Fragment simply allows the user of the fragment to set the background color and the text of the single TextView
- The adapter creates three instances of the fragment and sets the background color and text of each. Each fragment's text, color, and total fragments is hard coded.
- Fragment 只是允许片段的用户设置单个 TextView 的背景颜色和文本
- 适配器创建片段的三个实例并设置每个实例的背景颜色和文本。每个片段的文本、颜色和总片段都是硬编码的。
Now, here are the exact relevant portions of the code:
现在,这里是代码的确切相关部分:
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;
}
}
回答by VVB
You need to pass some sort of id along with newInstance()
while creating instance. And according to that id you can use if..else
to choose layout file.
您需要newInstance()
在创建实例时传递某种 id 。根据该 ID,您可以使用它if..else
来选择布局文件。
See my reference code below:
请参阅下面的参考代码:
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;
}
回答by JASON G PETERSON
Can't you just introduce fields to the Fragment class to account for the variances in background, etc. and add them to its constructor? Then in getItem
instantiate the Fragment class with different values depending on the value of position
.
难道您不能只向 Fragment 类引入字段以说明背景等中的差异并将它们添加到其构造函数中吗?然后getItem
根据 的值实例化具有不同值的 Fragment 类position
。