I'm using a ViewPager with a FragmentPagerAdapter to allow swiping between 2 fragments. I'm not using FragmentStatePagerAdapter because it destroys and recreates new fragment instances when swiping between them, and I don't want operations in one of the Fragments executing more than once). I need a way to get references to the 2 created fragments to pass to the FragmentPagerAdapter, but the following code is producing an error (see below):
public class MyActivity extends FragmentActivity {
private ViewPager viewPager;
private MyPagerAdapter pagerAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
initViewPager();
}
private void initViewPager() {
List<Fragment> fragments = new ArrayList<Fragment>();
FragmentManager fragManager = getSupportFragmentManager();
fragments.add(fragManager.findFragmentById(R.id.fragment_1));
fragments.add(fragManager.findFragmentById(R.id.fragment_2));
pagerAdapter = new MyPagerAdapter(fragManager, fragments);
viewPager = (ViewPager) findViewById(R.id.fragment_pager);
viewPager.setAdapter(pagerAdapter);
}
...
...
private class MyPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public MyPagerAdapter(FragmentManager fragmentManager, List<Fragment> fragments) {
super(fragmentManager);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
...
...
This produces the following error (or similar):
java.lang.IllegalStateException: Can't change container ID of fragment Fragment1{4169f780 #1 id=0x7f0b0005 android:switcher:2131427331:0}: was 2131427333 now 2131427331
Can anybody help me with this?
Edit
I've also tried the following, but it instantiates each fragment exactly twice (understandably), which I don't want. Other than that though, it works with this code:
private void initViewPager() {
List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName()));
fragments.add(Fragment.instantiate(this, Fragment2.class.getName()));
pagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), fragments);
viewPager = (ViewPager) findViewById(R.id.fragment_pager);
viewPager.setAdapter(pagerAdapter);
}
Here is a very clear tutorial with sample code that I have used to build a viewpager layout.
As #harmanjd seems to suggest, you don't put the fragments in the XML file with the viewpager layout. Define each in its own XML and/or Java class, instantiate those like you have above (use fragments.add(Fragment.instantiate(this, CustomFragment.class.getName())) for a custom class), put them in pagerAdapter and give that to the ViewPager.
You ViewPager XML should look something like this (unless it's part of a larger layout besides the fragments):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ViewPager
android:id="#+android:id/viewpager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
Related
I have a ViewPager with 3 sliding fragments. Each of the fragment has listview which contains some images. I'm trying to show a new fragment over the viewpager (like an activity) when one of the images in the listview is clicked and also have the ability to navigate back to the viewpager when back button is pressed.
My code for the listview onClick which is inside the fragment is as follows(for testing, I'm using onClick on the entire listview and not the images)
listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.d(TAG,"Clicked");
ItemDetailFragment fragment = ItemDetailFragment.newInstance("ab","cd");
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.containerr,fragment);
transaction.addToBackStack(null);
transaction.commit();
}
});
The log statement prints out fine, but the fragment doesn't show up.
Here's my activity which has the viewpager
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:theme="#android:style/Theme.WithActionBar">
<com.firstapp.myapplication.CustomViews.SlidingTabLayout
android:id="#+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v4.view.ViewPager
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/viewPager">
</android.support.v4.view.ViewPager>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/containerr">
</FrameLayout>
</LinearLayout>
Please help.
Also, in the examples in android docs, any interaction in a fragment which leads to a fragment transaction is first passed to the parent activity which handles adding or replacing a new fragment. I am handling that here in the fragment itself. Which one is the correct way and what is the difference between the two methods?
EDIT : I need to add fragment over the viewPager and not to it. It's like having a new screen come up on clicking an element inside the viewpager's fragment.
Thanks!
I solved it by adding the viewpager in a fragment(instead of having it directly in my activity itself).
My activity now is
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
and I moved the previous activity layout (mentioned in the question) to a ViewPagerFragment
Now in my activity's onCreate I do
ViewPagerFragment viewPagerFragment = new ViewPagerFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, viewPagerFragment)
.commit();
and for showing a new fragment over the viewpager on clicking an item inside the viewpager's fragment, I do
listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mListener.onFragmentInteraction(null);
}
});
The listener is an interface implemented in the parent activity as follows
#Override
public void onFragmentInteraction(Uri uri) {
ItemDetailFragment itemDetailFragment = ItemDetailFragment.newInstance("ab","cd");
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container,itemDetailFragment)
.addToBackStack(null)
.commit();
}
I implemented this via the listener interface because as per the docs
All Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly.
And it works now! :D
Initialize your fragment in Activity class and add them to viewpager like below
List<Fragment> fragments = getFragments();
pageAdapter = new MyPageAdapter(getSupportFragmentManager(), fragments);
pager = (ViewPager) findViewById(R.id.viewpager);
pager.setAdapter(pageAdapter);
Add fragments to your viewpager
private List<Fragment> getFragments() {
List<Fragment> fList = new ArrayList<Fragment>();
fList.add(Fragment1.newInstance());
fList.add(Fragment2.newInstance());
fList.add(Fragment3.newInstance());
return fList;
}
/********************** Fragment Page adapter *************************/
private class MyPageAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public MyPageAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public int getCount() {
return this.fragments.size();
}
}
And create your fragment classes and handle the listview inside those class
I've got an app with left navigation drawer, switching between different fragments. Inside one of those fragment I want to implement SlidingTabLayout.
So, what I did is I copied the SlidingTabLayout.java and SlidingTabStrip.java from Google's iosched app. I used them inside my fragment:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<mynamespace.widget.SlidingTabLayout
android:id="#+id/home_fragment_tabs"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v4.view.ViewPager
android:id="#+id/home_fragment_pager"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_width="match_parent" />
</LinearLayout>
And then in my Fragment class, inside onCreateView method, I instantiated my pager and SlidingTabLayout like this:
mPager = (ViewPager) view.findViewById(R.id.home_fragment_pager);
mPager.setAdapter(new HomePagerAdapter(getActivity().getSupportFragmentManager()));
mTabs = (SlidingTabLayout) view.findViewById(R.id.home_fragment_tabs);
mTabs.setViewPager(mPager);
And this is my FragmentPagerAdapter:
class HomePagerAdapter extends FragmentPagerAdapter
{
private String[] tabs = { "Today", "This week", "With star" };
private Fragment[] tabFragments = {
new TodayTabFragment(),
new WeekTabFragment(),
new StarTabFragment()
};
public HomePagerAdapter(FragmentManager fm)
{
super(fm);
}
#Override
public Fragment getItem(int i)
{
return tabFragments[i];
}
#Override
public int getCount()
{
return 3;
}
#Override
public CharSequence getPageTitle(int position)
{
return tabs[position];
}
}
Also I got separate Fragment class and layout for each tab, which is nothing special. And it works, but as soon as I switch to other fragment with my navigation drawer, when I get back, content of one of tabs just dissapears. Mostly it happens to second tab. Also, if I select the HomeFragment from navigation drawer, when is already displayed, I get a nullpointerexception inside SlidingTabLayout.java at this line: mViewPager.setCurrentItem(i);
Any way of fixing it? I have no idea what to do. I guess my code would work, but inside activity. Possible to make it work inside fragment?
I also faced the same issue. Please use FragmentStatePagerAdapter instead of FragmentPagerAdapter which will solve this issue.
Solution:
What I did is instead of implementing FragmentPagerAdapter, I implemented PagerAdapter then I overriden the instantiateItem(ViewGroup container, int position) method and destroyItem() method.
Link, that helped me: http://developer.android.com/samples/SlidingTabsBasic/src/com.example.android.slidingtabsbasic/SlidingTabsBasicFragment.html
Can you try to change constructor of HomePagerAdapter to:
public HomePagerAdapter(SupportFragmentManager fm)
{
super(fm);
}
When you begin transaction instead of suuportfragmanager change it to childfragmentmanager
I'm very new to Android development and I have been trying make an application that allows users to flip either to the left or right to change fragments. I'm using a FragmentStatePagerAdapter for this.
I first, in a for loop, created each fragment separately in the main activity's OnCreate() method and added them to the FragmentManager fManager.
for(Day day: days){
ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
this.fManager.beginTransaction().add(R.id.pager, fragment).commit();
}
fManager.executePendingTransactions();
I then set up UI elements and set the ViewPager's adapter like so
this.setUpUI();
mPager.setAdapter(mPagerAdapter);
The problem is is that I keep encountering this exception java.lang.IllegalStateException: Fragment Already Added...
I am very confused as to what is going on because the only place I add fragments to the FragmentManager is in the for-loop above. Do FragmentStatePagerAdapter's do something in the background that might cause this issue or am I not implementing my ScreenSlidePagerAdapter correctly?
Here is my code for the ScreenSlidePagerAdapter class:
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public FragmentManager fm;
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
this.fm = fm;
}
#Override
public Fragment getItem(int position) {
Fragment f = fm.getFragments().get(position);
return f;
}
#Override
public int getCount() {
return fm.getFragments().size();
}
}
Input would be greatly appreciated.
You show create the fragments inside getItem(pos) method of Adapter. The adapter internally use the FragmentManager that you provide in constructor.
Some what like this:
#Override
public Fragment getItem(int position) {
Fragment f = new ScreenSlidePageFragment();
return f;
}
I think the FragmentStatePagerAdapter already adds the fragments to your Activity (in the getItem() method). Try to remove that for loop.
You are getting the Fragment that is already in the FragmentManager and when you return it, the PagerAdapter tries to add it, but it is already there.
You don't have to add the fragments to the FragmentManager yourself. That's the FragmentStatePagerAdapter's job, that's why it receives a FragmentManager reference. You only have to create and return the fragments in getItem() and the PagerAdapter will add the fragments to the FragmentManager.
I usually do what you are trying like this:
public class FragmentSliderAdapter extends FragmentStatePagerAdapter {
private List<Fragment> list = new ArrayList<Fragment>();
public FragmentSliderAdapter(FragmentManager fm) {
super(fm);
list.add(new LoginFragment());
list.add(new SignupFragment());
}
#Override
public Fragment getItem(int arg0) {
return list.get(arg0);
}
#Override
public int getCount() {
return list.size();
}
}
Using list is cleaner and a good approach, since you can control what fragments are added to your adapter.
On the activity that uses the adapter, I just do this:
viewPager = (ViewPager) findViewById(R.id.pager);
adapter = new FragmentSliderAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
And the activity xml I do:
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
See if it helps !
In my application i have a tabhost with a tabwidget that have several Tabs.
Now i need a tab that show me inside the tabcontent a schedule grid that allow swipe to right and left to move through the months. But i need that the tab stay fixes and only the schedule swipes.
The navigation type (Fixed tabs + Swipe) allow me that? From what I understand this navigation allow the swipe but the tab don't stay the same.
What i need is possible?
Thanks for your help and attention.
I would say possible, stay with your codes on tabhost and tabwidget.
So I would assume one of those tabs are calling an Activity which probably named Schedule.class, by default tabhost does not allow any swiping to change tab feature, that is fine.
So in your Schedule Activity, you would be using ViewPager, I learnt how to use it from this article: http://thepseudocoder.wordpress.com/2011/10/05/android-page-swiping-using-viewpager/
Which is pretty easy to be understood. You may try to use it, hope I answered your question
Update: Here's a sample
Schedule.class
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.schedule);
ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
List<Fragment> fragments = new ArrayList<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName()));
fragments.add(Fragment.instantiate(this, Fragment2.class.getName()));
fragments.add(Fragment.instantiate(this, Fragment3.class.getName()));
MyFragmentAdapter miscFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(), fragments);
viewPager.setAdapter(miscFragmentAdapter);
}
MyFragmentAdapter.class
public class MyFragmentAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public MiscFragmentAdapter(FragmentManager fragmentManager, List<Fragment> fragments) {
super(fragmentManager);
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
#Override
public int getCount() {
return this.fragments.size();
}
}
schedule.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
Fragment1.class or Fragment2.class or Fragment3.class
public class Fragment1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (container == null) {
// We have different layouts, and in one of them this
// fragment's containing frame doesn't exist. The fragment
// may still be created from its saved state, but there is
// no reason to try to create its view hierarchy because it
// won't be displayed. Note this is not needed -- we could
// just run the code below, where we would create and return
// the view hierarchy; it would just never be used.
return null;
}
return (LinearLayout) inflater.inflate(R.layout.fragment1, container, false);
}
}
fragment1 is a simple layout with whatever you want inside it.
I have five activities/screens that I would like to be able to swipe between, each has a different function but are interrelated hence the UI concept of swiping between each.
I have found many discussions around ViewPager and PagerAdapters etc. but cannot seem to find one where swiping switches between different activity screens.
Is this even possible? Could someone point me towards an example project of this with source code? Or show me how to adapt an existing tutorial to do what I wish?
Thanks so much, have a good one!
You can't use ViewPager to swipe between Activities. You need to convert each of you five Activities into Fragments, then combine everything in one FragmentActivity with the Adapter you use with ViewPager.
Here's a link that goes into detail on converting your current Activities info Fragments.
This is the Fragment topic on the Android Developers website, it has a lot of useful info.
Here's another example (full source) that inflates TextViews on each page.
Here's an example that I typed up:
PagerAdapter:
public class PagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragments = new ArrayList<Fragment>();
public PagerAdapter(FragmentManager manager) {
super(manager);
}
public void addFragment(Fragment fragment) {
mFragments.add(fragment);
notifyDataSetChanged();
}
#Override
public int getCount() {
return mFragments.size();
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
}
This should be called in onCreate of your FragmentActivity:
private void initPaging() {
FragmentOne fragmentOne = new FragmentOne();
FragmentTwo fragmentTwo= new FragmentTwo();
PagerAdapter pagerAdapter = new PagerAdapter(getSupportFragmentManager());
pagerAdapter.addFragment(fragmentOne);
pagerAdapter.addFragment(fragmentTwo);
ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
viewPager.setAdapter(pagerAdapter);
}
This is an example of the layout you'd use for your FragmnetActivity:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<android.support.v4.view.ViewPager
android:id="#+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
To create Fragment create a new class that extends Fragment. The first two methods you'll want to override are onActivityCreated and onCreateView.
Here's how you could do that:
public class FragmentOne extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(THE_LAYOUT_FROM_YOUR_ORIGINAL_ACTIVITY, container, false);
return view;
}
}
Fix For mPageradapter
PagerAdapter pagerAdapter = new PagerAdapter(getSupportFragmentManager());
pagerAdapter.addFragment(fragmentOne);
pagerAdapter.addFragment(fragmentEvents);
viewPager = (ViewPager) super.findViewById(R.id.pager);
viewPager.setAdapter(pagerAdapter);