替换 ViewPager 内的 Fragment
我正在尝试使用 FragmentPagerAdapter
将 Fragment 与 ViewPager
结合使用。 我想要实现的目标是将位于 ViewPager 第一页上的片段替换为另一个片段。
寻呼机由两个页面组成。第一个是 FirstPagerFragment
,第二个是 SecondPagerFragment
。单击第一页的按钮。我想用 NextFragment 替换 FirstPagerFragment
。
下面是我的代码。
public class FragmentPagerActivity extends FragmentActivity {
static final int NUM_ITEMS = 2;
MyAdapter mAdapter;
ViewPager mPager;
@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);
}
/**
* Pager Adapter
*/
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public Fragment getItem(int position) {
if(position == 0) {
return FirstPageFragment.newInstance();
} else {
return SecondPageFragment.newInstance();
}
}
}
/**
* Second Page FRAGMENT
*/
public static class SecondPageFragment extends Fragment {
public static SecondPageFragment newInstance() {
SecondPageFragment f = new SecondPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.second, container, false);
}
}
/**
* FIRST PAGE FRAGMENT
*/
public static class FirstPageFragment extends Fragment {
Button button;
public static FirstPageFragment newInstance() {
FirstPageFragment f = new FirstPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
View root = inflater.inflate(R.layout.first, container, false);
button = (Button) root.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
trans.addToBackStack(null);
trans.commit();
}
});
return root;
}
/**
* Next Page FRAGMENT in the First Page
*/
public static class NextFragment extends Fragment {
public static NextFragment newInstance() {
NextFragment f = new NextFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.next, container, false);
}
}
}
...这里是 xml 文件
fragment_pager.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</android.support.v4.view.ViewPager>
</LinearLayout>
first.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/first_fragment_root_id"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:id="@+id/button"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="to next"/>
</LinearLayout>
现在的问题...我应该使用哪个 ID
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
?
如果我使用 R.id.first_fragment_root_id,替换有效,但层次结构查看器显示奇怪的行为,如下所示。
一开始的情况是
更换后情况是
正如你所看到的,有一些问题,我希望在替换后找到与第一张图片所示相同的状态分段。
I'm trying to use Fragment with a ViewPager
using the FragmentPagerAdapter
.
What I'm looking for to achieve is to replace a fragment, positioned on the first page of the ViewPager
, with another one.
The pager is composed of two pages. The first one is the FirstPagerFragment
, the second one is the SecondPagerFragment
. Clicking on a button of the first page. I'd like to replace the FirstPagerFragment
with the NextFragment.
There is my code below.
public class FragmentPagerActivity extends FragmentActivity {
static final int NUM_ITEMS = 2;
MyAdapter mAdapter;
ViewPager mPager;
@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);
}
/**
* Pager Adapter
*/
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public Fragment getItem(int position) {
if(position == 0) {
return FirstPageFragment.newInstance();
} else {
return SecondPageFragment.newInstance();
}
}
}
/**
* Second Page FRAGMENT
*/
public static class SecondPageFragment extends Fragment {
public static SecondPageFragment newInstance() {
SecondPageFragment f = new SecondPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.second, container, false);
}
}
/**
* FIRST PAGE FRAGMENT
*/
public static class FirstPageFragment extends Fragment {
Button button;
public static FirstPageFragment newInstance() {
FirstPageFragment f = new FirstPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
View root = inflater.inflate(R.layout.first, container, false);
button = (Button) root.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
trans.addToBackStack(null);
trans.commit();
}
});
return root;
}
/**
* Next Page FRAGMENT in the First Page
*/
public static class NextFragment extends Fragment {
public static NextFragment newInstance() {
NextFragment f = new NextFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.next, container, false);
}
}
}
...and here the xml files
fragment_pager.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</android.support.v4.view.ViewPager>
</LinearLayout>
first.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/first_fragment_root_id"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:id="@+id/button"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="to next"/>
</LinearLayout>
Now the problem... which ID should I use in
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
?
If I use R.id.first_fragment_root_id
, the replacement works, but Hierarchy Viewer shows a strange behavior, as below.
At the beginning the situation is
after the replacement the situation is
As you can see there is something wrong, I expect to find the same state shown as in the first picture after I replace the fragment.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(18)
还有一种解决方案不需要修改ViewPager和FragmentStatePagerAdapter的源代码,并且与作者使用的FragmentPagerAdapter基类配合使用。
我想首先回答作者关于他应该使用哪个ID的问题;它是容器的ID,即视图寻呼机本身的ID。然而,正如您自己可能注意到的那样,在代码中使用该 ID 不会发生任何事情。我将解释原因:
首先,要使
ViewPager
重新填充页面,您需要调用驻留在适配器基类中的notifyDataSetChanged()
。其次,
ViewPager
使用getItemPosition()
抽象方法来检查哪些页面应该被销毁,哪些页面应该保留。此函数的默认实现始终返回POSITION_UNCHANGED
,这会导致ViewPager
保留所有当前页面,因此不会附加新页面。因此,为了使片段替换工作,需要在适配器中重写getItemPosition()
,并且在使用旧的、要隐藏的片段作为参数调用时必须返回POSITION_NONE
。这也意味着您的适配器始终需要知道哪个片段应显示在位置 0、FirstPageFragment 或 NextFragment。实现此目的的一种方法是在创建
FirstPageFragment
时提供一个侦听器,当需要切换片段时将调用该侦听器。我认为这是一件好事,让您的片段适配器处理所有片段开关以及对 ViewPager 和 FragmentManager 的调用。第三,FragmentPagerAdapter通过从位置派生的名称来缓存使用的片段,因此如果位置0处有片段,即使该类是新的,它也不会被替换。有两种解决方案,但最简单的是使用
FragmentTransaction
的remove()
函数,该函数也会删除其标签。这是很多文字,下面是适合您的情况的代码:
There is another solution that does not need modifying source code of
ViewPager
andFragmentStatePagerAdapter
, and it works with theFragmentPagerAdapter
base class used by the author.I'd like to start by answering the author's question about which ID he should use; it is ID of the container, i.e. ID of the view pager itself. However, as you probably noticed yourself, using that ID in your code causes nothing to happen. I will explain why:
First of all, to make
ViewPager
repopulate the pages, you need to callnotifyDataSetChanged()
that resides in the base class of your adapter.Second,
ViewPager
uses thegetItemPosition()
abstract method to check which pages should be destroyed and which should be kept. The default implementation of this function always returnsPOSITION_UNCHANGED
, which causesViewPager
to keep all current pages, and consequently not attaching your new page. Thus, to make fragment replacement work,getItemPosition()
needs to be overridden in your adapter and must returnPOSITION_NONE
when called with an old, to be hidden, fragment as argument.This also means that your adapter always needs to be aware of which fragment that should be displayed in position 0,
FirstPageFragment
orNextFragment
. One way of doing this is supplying a listener when creatingFirstPageFragment
, which will be called when it is time to switch fragments. I think this is a good thing though, to let your fragment adapter handle all fragment switches and calls toViewPager
andFragmentManager
.Third,
FragmentPagerAdapter
caches the used fragments by a name which is derived from the position, so if there was a fragment at position 0, it will not be replaced even though the class is new. There are two solutions, but the simplest is to use theremove()
function ofFragmentTransaction
, which will remove its tag as well.That was a lot of text, here is code that should work in your case:
截至 2012 年 11 月 13 日,在 ViewPager 中重新调整片段似乎变得容易多了。 Google 发布了支持嵌套片段的 Android 4.2,新的 Android 也支持它支持库 v11,因此这将一直工作到 1.6
。除了使用 getChildFragmentManager 之外,它与替换片段的正常方法非常相似。它似乎有效,除了当用户时 嵌套片段后堆栈未弹出单击后退按钮。根据该链接问题中的解决方案,您需要在片段的子管理器上手动调用 popBackStackImmediate() 。因此,您需要重写 ViewPager 活动的 onBackPressed() ,您将在其中获取 ViewPager 的当前片段并对其调用 getChildFragmentManager().popBackStackImmediate() 。
获取当前显示的片段也有点棘手,我使用了这个 肮脏的“android:switcher:VIEWPAGER_ID:INDEX”解决方案 但您也可以自己跟踪 ViewPager 的所有片段,如第二个解决方案中所述 在此页面。
下面是我的 ViewPager 代码,其中包含 4 个 ListView,当用户单击一行时,ViewPager 中会显示详细信息视图,并且后退按钮可以工作。为了简洁起见,我尝试仅包含相关代码,因此如果您希望将完整的应用程序上传到 GitHub,请留下评论。
HomeActivity.java
ListProductsFragment.java
As of November 13th 2012, repacing fragments in a ViewPager seems to have become a lot easier. Google released Android 4.2 with support for nested fragments, and it's also supported in the new Android Support Library v11 so this will work all the way back to 1.6
It's very similiar to the normal way of replacing a fragment except you use getChildFragmentManager. It seems to work except the nested fragment backstack isn't popped when the user clicks the back button. As per the solution in that linked question, you need to manually call the popBackStackImmediate() on the child manager of the fragment. So you need to override onBackPressed() of the ViewPager activity where you'll get the current fragment of the ViewPager and call getChildFragmentManager().popBackStackImmediate() on it.
Getting the Fragment currently being displayed is a bit hacky as well, I used this dirty "android:switcher:VIEWPAGER_ID:INDEX" solution but you can also keep track of all fragments of the ViewPager yourself as explained in the second solution on this page.
So here's my code for a ViewPager with 4 ListViews with a detail view shown in the ViewPager when the user clicks a row, and with the back button working. I tried to include just the relevant code for the sake of brevity so leave a comment if you want the full app uploaded to GitHub.
HomeActivity.java
ListProductsFragment.java
基于 @wize 的答案,我发现它很有帮助且优雅,我可以部分实现我想要的,因为我希望能够在替换后返回到第一个片段。我稍微修改了他的代码就实现了它。
这将是 FragmentPagerAdapter:
要执行替换,只需定义一个
CalendarPageFragmentListener
类型的静态字段,并通过相应片段的newInstance
方法进行初始化,然后调用FirstFragment.pageListener.onSwitchToNextFragment()
或NextFragment.pageListener.onSwitchToNextFragment()
分别地。Based on @wize 's answer, which I found helpful and elegant, I could achieve what I wanted partially, cause I wanted the cability to go back to the first Fragment once replaced. I achieved it bit modifying a bit his code.
This would be the FragmentPagerAdapter:
To perfom the replacement, simply define a static field, of the type
CalendarPageFragmentListener
and initialized through thenewInstance
methods of the corresponding fragments and callFirstFragment.pageListener.onSwitchToNextFragment()
orNextFragment.pageListener.onSwitchToNextFragment()
respictevely.我已经实现了以下解决方案:
实现此目的的技巧如下:
适配器代码如下:
第一次添加所有选项卡时,我们需要调用方法 createHistory(),以创建初始历史记录
。想要更换一个片段到您调用的特定选项卡:
Replace(final intposition,finalClassfragmentClass,finalBundleargs)
按下后退时,您需要调用 back() 方法:
该解决方案适用于 sherlock 操作栏和滑动手势。
I have implemented a solution for:
The tricks to achieve this are the following:
The adapter code is the following:
The very first time you add all tabs, we need to call the method createHistory(), to create the initial history
Every time you want to replace a fragment to a specific tab you call:
replace(final int position, final Class fragmentClass, final Bundle args)
On back pressed you need to call the back() method:
The solution works with sherlock action bar and with swipe gesture.
tl;dr: 使用负责替换其托管内容并跟踪后退导航历史记录的主机片段(就像在浏览器中一样)。
由于您的用例由固定数量的选项卡组成,我的解决方案效果很好:我们的想法是用自定义类
HostFragment
的实例填充 ViewPager,该类能够替换其托管内容并保留其自己的内容返回导航历史记录。要替换托管片段,请调用hostfragment.replaceFragment()
方法:该方法所做的就是用 id
R.id.hosted_fragment
替换框架布局以及提供给该方法的片段。查看我关于此主题的 教程 了解更多详细信息和 GitHub 上的完整工作示例!
tl;dr: Use a host fragment that is responsible for replacing its hosted content and keeps track of a back navigation history (like in a browser).
As your use case consists of a fixed amount of tabs my solution works well: The idea is to fill the ViewPager with instances of a custom class
HostFragment
, that is able to replace its hosted content and keeps its own back navigation history. To replace the hosted fragment you make a call to the methodhostfragment.replaceFragment()
:All that method does is to replace the frame layout with the id
R.id.hosted_fragment
with the fragment provided to the method.Check my tutorial on this topic for further details and a complete working example on GitHub!
所提出的一些解决方案帮助我部分解决了问题,但解决方案中仍然缺少一件重要的事情,这在某些情况下产生了意外的异常和黑页内容而不是片段内容。
问题是,FragmentPagerAdapter 类使用项目 ID 将缓存的片段存储到FragmentManager。因此,您还需要重写 getItemId(intposition) 方法,以便它返回顶级页面的 position 和 100 + 位置 em> 查看详细信息页面。否则,将从缓存中返回先前创建的顶级片段而不是细节级片段。
此外,我在这里分享一个完整的示例,如何使用 ViewPager 实现类似于 Fragment 页面的选项卡式活动,并使用 RadioGroup 实现选项卡按钮,该活动允许用详细页面替换顶级页面,并且还支持后退按钮。此实现仅支持一级返回堆栈(项目列表 - 项目详细信息),但多级返回堆栈实现很简单。这个例子在正常情况下工作得很好,除了它抛出一个 NullPointerException 以防万一你切换到第二页,更改第一页的片段(虽然不可见)并返回到第一页页。一旦我弄清楚了这个问题,我会发布一个解决方案:
Some of the presented solutions helped me a lot to partially solve the problem but there is still one important thing missing in the solutions which has produced unexpected exceptions and black page content instead of fragment content in some cases.
The thing is that FragmentPagerAdapter class is using item ID to store cached fragments to FragmentManager. For this reason, you need to override also the getItemId(int position) method so that it returns e. g. position for top-level pages and 100 + position for details pages. Otherwise the previously created top-level fragment would be returned from the cache instead of detail-level fragment.
Furthermore, I'm sharing here a complete example how to implement tabs-like activity with Fragment pages using ViewPager and tab buttons using RadioGroup that allows replacement of top-level pages with detailed pages and also supports back button. This implementation supports only one level of back stacking (item list - item details) but multi-level back stacking implementation is straightforward. This example works pretty well in normal cases except of it is throwing a NullPointerException in case when you switch to e. g. second page, change the fragment of the first page (while not visible) and return back to the first page. I'll post a solution to this issue once I'll figure it out:
我创建了一个 ViewPager,其中包含 3 个元素和 2 个子元素(索引 2 和 3),这里是我想要做的。
我在 StackOverFlow 之前的问题和答案的帮助下实现了这一点,这里是链接。
ViewPagerChildFragments
I have created a ViewPager with 3 elements and 2 sub elements for index 2 and 3 and here what I wanted to do..
I have implemented this with the help from previous questions and answers from StackOverFlow and here is the link.
ViewPagerChildFragments
要替换
ViewPager
中的片段,您可以将ViewPager
、PagerAdapter
和FragmentStatePagerAdapter
类的源代码移动到您的项目中并添加以下代码。into
ViewPager
:into FragmentStatePagerAdapter:
handleGetItemInvalidated()
确保在下次调用getItem()
后返回 newFragmentgetFragmentPosition()
返回片段在适配器中的位置。现在,要替换片段调用,
如果您对示例项目感兴趣,请向我索取源代码。
To replace a fragment inside a
ViewPager
you can move source codes ofViewPager
,PagerAdapter
andFragmentStatePagerAdapter
classes into your project and add following code.into
ViewPager
:into FragmentStatePagerAdapter:
handleGetItemInvalidated()
ensures that after next call ofgetItem()
it return newFragmentgetFragmentPosition()
returns position of the fragment in your adapter.Now, to replace fragments call
If you interested in an example project ask me for the sources.
与 AndroidTeam 的解决方案配合得很好,但是我发现我需要像
FrgmentTransaction.addToBackStack(null)
那样返回的能力,但仅添加此功能只会导致 Fragment 被替换而不通知 ViewPager。将提供的解决方案与此较小的增强功能相结合,您只需重写活动的onBackPressed()
方法即可返回到之前的状态。最大的缺点是它一次只能返回一个,这可能会导致多次返回点击希望这对某人有帮助。
另外,就
getFragmentPosition()
而言,它几乎与getItem()
相反。您知道哪些片段去了哪里,只需确保返回其所在的正确位置即可。这是一个示例:Works Great with AndroidTeam's solution, however I found that I needed the ability to go back much like
FrgmentTransaction.addToBackStack(null)
But merely adding this will only cause the Fragment to be replaced without notifying the ViewPager. Combining the provided solution with this minor enhancement will allow you to return to the previous state by merely overriding the activity'sonBackPressed()
method. The biggest drawback is that it will only go back one at a time which may result in multiple back clicksHope this helps someone.
Also as far as
getFragmentPosition()
goes it's pretty muchgetItem()
in reverse. You know which fragments go where, just make sure you return the correct position it will be in. Here's an example:在您的
onCreateView
方法中,container
实际上是一个ViewPager
实例。因此,只需调用
就会更改 ViewPager 中的当前片段。
In your
onCreateView
method,container
is actually aViewPager
instance.So, just calling
will change current fragment in your
ViewPager
.这是我对这个问题的相对简单的解决方案。此解决方案的关键是使用
FragmentStatePagerAdapter
而不是FragmentPagerAdapter
,因为前者将为您删除未使用的片段,而后者仍保留其实例。第二个是在 getItem() 中使用POSITION_NONE
。我使用了一个简单的列表来跟踪我的片段。我的要求是立即用新列表替换整个片段列表,但可以轻松修改以下内容以替换单个片段:Here's my relatively simple solution to this problem. The keys to this solution are to use
FragmentStatePagerAdapter
instead ofFragmentPagerAdapter
as the former will remove unused fragments for you while the later still retains their instances. The second is the use ofPOSITION_NONE
in getItem(). I've used a simple List to keep track of my fragments. My requirement was to replace the entire list of fragments at once with a new list, but the below could be easily modified to replace individual fragments:我还提出了一个解决方案,它与Stacks一起使用。这是一种更加模块化的方法,因此您不必在
FragmentPagerAdapter
中指定每个片段和详细信息片段。它建立在 ActionbarSherlock 的示例之上,如果我是从 Google 演示应用程序中导出的,则该示例是正确的。在 MainActivity 中添加此后退按钮功能:
如果您想在删除片段时保存片段状态。让您的 Fragment 实现接口 SaveStateBundle 在函数中返回包含保存状态的包。通过
this.getArguments()
实例化后获取包。您可以实例化这样的选项卡:
如果您想在选项卡堆栈顶部添加片段,则效果类似。
重要:我认为,如果您想在两个选项卡上有两个同一类的实例,这是行不通的。
我很快就完成了这个解决方案,所以我只能分享它,而不提供任何经验。
I also made a solution, which is working with Stacks. It's a more modular approach so u don't have to specify each Fragment and Detail Fragment in your
FragmentPagerAdapter
. It's build on top of the Example from ActionbarSherlock which derives if I'm right from the Google Demo App.Add this for back button functionality in your MainActivity:
If u like to save the Fragment State when it get's removed. Let your Fragment implement the interface
SaveStateBundle
return in the function a bundle with your save state. Get the bundle after instantiation bythis.getArguments()
.You can instantiate a tab like this:
works similiar if u want to add a Fragment on top of a Tab Stack.
Important: I think, it won't work if u want to have 2 instances of same class on top of two Tabs.
I did this solution quick together, so I can only share it without providing any experience with it.
替换视图分页器中的片段非常复杂,但很有可能,而且看起来非常流畅。首先,您需要让 viewpager 本身处理片段的删除和添加。发生的情况是,当您替换 SearchFragment 内部的片段时,您的 viewpager 会保留其片段视图。因此,您最终会得到一个空白页面,因为当您尝试替换 SearchFragment 时,它会被删除。
解决方案是在 viewpager 内部创建一个侦听器,该侦听器将处理在其外部所做的更改,因此首先将此代码添加到适配器的底部。
然后,您需要在 viewpager 中创建一个私有类,当您想要更改片段时,该类将成为侦听器。例如,您可以添加类似这样的内容。请注意,它实现了刚刚创建的接口。因此,每当您调用此方法时,它都会运行下面类中的代码。
这里有两点主要需要指出:
注意放置在“newInstance(listener)”构造函数中的侦听器。这些是您将如何调用fragment0Changed(String newFragmentIdentification)`。以下代码显示了如何在片段内创建侦听器。
静态nextFragmentListener监听器搜索;
您可以在
onPostExecute
内部调用更改,这将触发 viewpager 内部的代码将片段切换到位置零 fragAt0 处,以成为新的 searchResultFragment。在 viewpager 发挥作用之前,您还需要添加两个小部分。
其中之一是 viewpager 的 getItem 重写方法。
现在,如果没有这最后一块,您仍然会得到一张空白页。有点蹩脚,但它是 viewPager 的重要组成部分。您必须重写 viewpager 的 getItemPosition 方法。通常此方法将返回 POSITION_UNCHANGED ,它告诉 viewpager 保持所有内容相同,因此永远不会调用 getItem 将新片段放置在页面上。这是您可以执行的操作的示例
正如我所说,代码非常复杂,但您基本上必须根据您的情况创建自定义适配器。我提到的事情将使更改片段成为可能。可能需要很长时间才能吸收所有内容,所以我会耐心等待,但这一切都会有意义。花时间是完全值得的,因为它可以制作出一个非常漂亮的应用程序。
这是处理后退按钮的核心。您将其放入 MainActivity 中。
您将需要在 FragmentSearchResults 中创建一个名为 backPressed() 的方法,该方法调用fragment0changed。这与我之前展示的代码一起将处理按下后退按钮。祝您更改 viewpager 的代码好运。这需要大量的工作,而且据我发现,没有任何快速的适应。就像我说的,您基本上是在创建一个自定义 viewpager 适配器,并让它使用侦听器处理所有必要的更改
Replacing fragments in a viewpager is quite involved but is very possible and can look super slick. First, you need to let the viewpager itself handle the removing and adding of the fragments. What is happening is when you replace the fragment inside of SearchFragment, your viewpager retains its fragment views. So you end up with a blank page because the SearchFragment gets removed when you try to replace it.
The solution is to create a listener inside of your viewpager that will handle changes made outside of it so first add this code to the bottom of your adapter.
Then you need to create a private class in your viewpager that becomes a listener for when you want to change your fragment. For example you could add something like this. Notice that it implements the interface that was just created. So whenever you call this method, it will run the code inside of the class below.
There are two main things to point out here:
Notice the listeners that are placed in the 'newInstance(listener)constructor. These are how you will callfragment0Changed(String newFragmentIdentification)`. The following code shows how you create the listener inside of your fragment.
static nextFragmentListener listenerSearch;
You could call the change inside of your
onPostExecute
This would trigger the code inside of your viewpager to switch your fragment at position zero fragAt0 to become a new searchResultFragment. There are two more small pieces you would need to add to the viewpager before it became functional.
One would be in the getItem override method of the viewpager.
Now without this final piece you would still get a blank page. Kind of lame, but it is an essential part of the viewPager. You must override the getItemPosition method of the viewpager. Ordinarily this method will return POSITION_UNCHANGED which tells the viewpager to keep everything the same and so getItem will never get called to place the new fragment on the page. Here's an example of something you could do
Like I said, the code gets very involved, but you basically have to create a custom adapter for your situation. The things I mentioned will make it possible to change the fragment. It will likely take a long time to soak everything in so I would be patient, but it will all make sense. It is totally worth taking the time because it can make a really slick looking application.
Here's the nugget for handling the back button. You put this inside your MainActivity
You will need to create a method called backPressed() inside of FragmentSearchResults that calls fragment0changed. This in tandem with the code I showed before will handle pressing the back button. Good luck with your code to change the viewpager. It takes a lot of work, and as far as I have found, there aren't any quick adaptations. Like I said, you are basically creating a custom viewpager adapter, and letting it handle all of the necessary changes using listeners
这是我实现这一目标的方法。
首先,在要实现按钮点击
fragment
事件的viewPager
选项卡中添加Root_fragment
。例子;首先,
RootTabFragment
应该包含FragmentLayout
以进行片段更改。然后,在
RootTabFragment
onCreateView
中,为您的FirstPagerFragment
实现fragmentChange
之后,实现
onClick
> FirstPagerFragment 内的按钮事件,并再次像这样进行片段更改。希望这会对你有所帮助。
This is my way to achieve that.
First of all add
Root_fragment
insideviewPager
tab in which you want to implement button clickfragment
event. Example;First of all,
RootTabFragment
should be includeFragmentLayout
for fragment change.Then, inside
RootTabFragment
onCreateView
, implementfragmentChange
for yourFirstPagerFragment
After that, implement
onClick
event for your button insideFirstPagerFragment
and make fragment change like that again.Hope this will help you guy.
我找到了简单的解决方案,即使您想在中间添加新片段或替换当前片段,它也能正常工作。在我的解决方案中,您应该覆盖
getItemId()
,它应该为每个片段返回唯一的 id。默认情况下不定位。就是这样:
注意:在此示例中,
FirstFragment
和SecondFragment
扩展了抽象类 PageFragment,它具有方法getPage()
。I found simple solution, which works fine even if you want add new fragments in the middle or replace current fragment. In my solution you should override
getItemId()
which should return unique id for each fragment. Not position as by default.There is it:
Notice: In this example
FirstFragment
andSecondFragment
extends abstract class PageFragment, which has methodgetPage()
.我做了一些类似于 wize 的事情,但在我的回答中,你可以随时在两个片段之间进行更改。通过明智的回答,我在更改屏幕方向之类的事情时遇到了一些问题。这是 PagerAdapter 的样子:
我在适配器容器活动中实现的侦听器,以便在附加它时将其放入片段,这是活动:
然后在附加调用它时将侦听器放入片段中:
最后是侦听器:
I doing something to similar to wize but in my answer yo can change between the two fragments whenever you want. And with the wize answer I have some problems when changing the orientation of the screen an things like that. This is the PagerAdapter looks like:
The listener I implemented in the adapter container activity to put it to the fragment when attaching it, this is the activity:
Then in the fragment putting the listener when attach an calling it:
And finally the listener:
我按照 @wize 和 @mdelolmo 的答案,得到了解决方案。谢谢吨。但是,我对这些解决方案进行了一些调整以改善内存消耗。
我观察到的问题:
它们保存被替换的
Fragment
实例。就我而言,它是一个包含 MapView 的 Fragment,我认为它的成本很高。因此,我维护的是FragmentPagerPositionChanged(POSITION_NONE 或 POSITION_UNCHANGED)
而不是Fragment
本身。这是我的实现。
演示链接在这里.. https://youtu.be/l_62uhKkLyM
出于演示目的,使用了 2 个片段
TabReplaceFragment
和DemoTab2Fragment
位于第二个位置。在所有其他情况下,我都使用DemoTabFragment
实例。说明:
我将
Switch
从 Activity 传递到DemoCollectionPagerAdapter
。根据此开关的状态,我们将显示正确的片段。当开关检查更改时,我将调用 SwitchFragListener 的 onSwitchToNextFragment 方法,其中将 pagerAdapterPosChanged 变量的值更改为POSITION_NONE
。查看有关 POSITION_NONE。这将使 getItem 无效,并且我有逻辑来实例化那里的正确片段。抱歉,如果解释有点混乱。再次非常感谢@wize 和@mdelolmo 的原创想法。
希望这有帮助。 :)
让我知道这个实现是否有任何缺陷。这将对我的项目有很大帮助。
I followed the answers by @wize and @mdelolmo and I got the solution. Thanks Tons. But, I tuned these solutions a little bit to improve the memory consumption.
Problems I observed:
They save the instance of
Fragment
which is replaced. In my case, it is a Fragment which holdsMapView
and I thought its costly. So, I am maintaining theFragmentPagerPositionChanged (POSITION_NONE or POSITION_UNCHANGED)
instead ofFragment
itself.Here is my implementation.
Demo link here.. https://youtu.be/l_62uhKkLyM
For demo purpose, used 2 fragments
TabReplaceFragment
andDemoTab2Fragment
at position two. In all the other cases I'm usingDemoTabFragment
instances.Explanation:
I'm passing
Switch
from Activity to theDemoCollectionPagerAdapter
. Based on the state of this switch we will display correct fragment. When the switch check is changed, I'm calling theSwitchFragListener
'sonSwitchToNextFragment
method, where I'm changing the value ofpagerAdapterPosChanged
variable toPOSITION_NONE
. Check out more about POSITION_NONE. This will invalidate the getItem and I have logics to instantiate the right fragment over there. Sorry, if the explanation is a bit messy.Once again big thanks to @wize and @mdelolmo for the original idea.
Hope this is helpful. :)
Let me know if this implementation has any flaws. That will be greatly helpful for my project.
经过研究,我找到了简短代码的解决方案。
首先在片段上创建一个公共实例,如果片段没有在方向更改时重新创建,则只需在 onSaveInstanceState 上删除片段。
after research i found solution with short code.
first of all create a public instance on fragment and just remove your fragment on onSaveInstanceState if fragment not recreating on orientation change.