I have a ViewPager that shows the user 2 fragments.
Fragment 1 contains a button with an onClickListener that adds an entry to the ListView contents in fragment 2. The new entry is a number generated by a random number generator.
Fragment 2 contains a ListView with some random numbers in.
I cannot find a way to refresh the list in fragment 2 from the button listener in fragment 1. How do i do this?
I have tried:
initializing and setting the list adapter in an onResume in
fragment 2.
Calling myViewPager.notifyDataSetChanged from fragment 1.
Neither of the above methods have worked. Any ideas?
Edit: Here is my code:
layout_main.xml
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
layout_frag_one.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="#+id/main_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="Add Number" />
</RelativeLayout>
layout_frag_two
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>
MainActivity
public class SlideActivity extends FragmentActivity {
private final static int FRAGMENT_NUMBER = 2;
private ViewPager pager;
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_mainpager);
pager = (ViewPager) findViewById(R.id.mainpager);
pager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
super.onCreate(savedInstanceState);
}
private class ViewPagerAdapter extends FragmentStatePagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
public Fragment getItem(int index) {
switch(index){
case 0:
return new Fragment1();
case 1:
return new Fragment2();
default:
return new Fragment1();
}
}
public int getCount() {
return FRAGMENT_NUMBER;
}
}
}
You may need to implement some of this to get it working right:
http://developer.android.com/training/basics/fragments/communicating.html
And some more reading here:
http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
Related
I want to create an app. First page is a login page, then second page is a tabbad screen. I use BottomNavigationView to create a tab bar.
I want to give toolbar to each page. So, I put a Toolbar in fragment's layout.
But I have some problems:
If I add bottom navigation view to activity, I cannot use any transition animation from login to tab bar page.
If I add bottom navigation view to fragment and replace fragment by using childFragmentManager when bottom navigation view item is selected, childFragmentManager doesn't hide parent fragment's toolbar.
For second case, parent Fragment toolbar overlays child fragment toolbar.
getChildFragmentManager().beginTransaction().replace(R.id.rootFragmentLayout, fragment).commit();
Fragment layout is below:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:orientation="vertical"
android:id="#+id/rootFragmentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/toolBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"/>
</android.support.design.widget.AppBarLayout>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/toolBarLayout"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
</FrameLayout>
</LinearLayout>
</RelativeLayout>
If i understood it correctly, one Activity will house multiple fragments with only one toolbar. What you can do is use ViewPager to change between fragments. You'll need a ViewPager on your xml below your toolbar and then you can create a adapter that switches fragments for you.
In this example you supposedly have buttons to switch your fragments, as i call it btn1, btn2 and btn3 on the code
activity.xml
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<include layout="#layout/your_toolbar" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
Activity.java
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
public class mActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
ButterKnife.bind(this);
adapter = new MyPagerAdapter(getSupportFragmentManager());
container.setAdapter(adapter);
container.addOnPageChangeListener(this);
// This means 3 fragments, change the number as you like
container.setOffscreenPageLimit(3);
container.setCurrentItem(1);
}
// Use this method so your toolbar views switch the fragments
#OnClick({R.id.btn1, R.id.btn2, R.id.btn3})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.btn1:
container.setCurrentItem(0, true);
break;
case R.id.btn2:
container.setCurrentItem(1, true);
break;
case R.id.btn3:
container.setCurrentItem(2, true);
break;
}
}
}
PagerAdapter.java
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
public class MyPagerAdapter extends FragmentStatePagerAdapter {
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int pos) {
switch (pos) {
case 0:
return new YourFragmentClass1();
case 1:
return new YourFragmentClass2();
case 2:
return new YourFragmentClass3();
default:
return new YourFragmentClass1();
}
}
#Override
public int getCount() {
return 3;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
I have a Main Activity which uses a TabSelectedListener to show fragments for the AHBottomNavigation menu. The fragment called "FirstFragment" contains a FragmentPagerAdapter which allows the user to swipe between two tabs, each of which have its own fragment, called FirstTabInFirstFragmentFragment and SecondTabInFirstFragmentFragment (renamed for simplicity).
My issue is that:
a). When the Main Activity is launched, the "First" Bottom Navigation menu item is selected, however the "FirstFragment" is not launched. So, it shows the proper item selected with a blank screen. It only launches the First Fragment if I tap on the menu item again.
b). Once the FirstFragment has been properly launched and is being shown on screen (by the temporary fix done in a), if I select a different menu item (i.e. to navigate to SecondFragment) and then select the FirstFragment's menu item again, the two tabs within it are blank. Also, the sliding between the fragments for the two tabs does not work and gets "stuck" so you have to pull it all the way to one side or all the way to the other.
Hopefully I have explained my problem clearly - if there is anything I'm missing, I can provide more details.
Note that I am using com.aurelhubert.ahbottomnavigation.AHBottomNavigation
Here are the relevant files:
MainActivity.java:
public class MainActivity extends AppCompatActivity{
private AHBottomNavigationAdapter navigationAdapter;
private AHBottomNavigationViewPager viewPager;
private AHBottomNavigation bottomNavigation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Add menu items to bar
bottomNavigation = (AHBottomNavigation) findViewById(R.id.bottom_navigation);
this.createNavItems();
bottomNavigation.setOnTabSelectedListener(new AHBottomNavigation.OnTabSelectedListener() {
#Override
public boolean onTabSelected(int position, boolean wasSelected) {
//show fragment
if (position==0)
{
FirstFragment firstFragment=new FirstFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.content_id,firstFragment).commit();
}else if (position==1)
{
SecondFragment secondFragment=new SecondFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.content_id, secondFragment).commit();
}else if (position==2)
{
ThirdFragment thirdFragment=new ThirdFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.content_id,thirdFragment).commit();
}else{
FourthFragment fourthFragment=new FourthFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.content_id,fourthFragment).commit();
}
return true;
}
});
}
private void createNavItems(){
navigationAdapter = new AHBottomNavigationAdapter(this, R.menu.navigation);
navigationAdapter.setupWithBottomNavigation(bottomNavigation);
// set current item
bottomNavigation.setCurrentItem(0);
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/content_id"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.aurelhubert.ahbottomnavigation.AHBottomNavigation
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"/>
</android.support.design.widget.CoordinatorLayout>
FirstFragment.java:
public class FirstFragment extends Fragment {
private FragmentActivity mContext;
public FirstFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// Set the content of the activity to use the activity_main.xml layout file
//setContentView(R.layout.activity_first);
// Find the view pager that will allow the user to swipe between fragments
ViewPager viewPager = (ViewPager) getView().findViewById(R.id.viewpager);
// Create an adapter that knows which fragment should be shown on each page
// using getFragmentManager() will work too
FirstFragmentPagerAdapter adapter = new FirstFragmentPagerAdapter(mContext.getSupportFragmentManager(), mContext);
// Set the adapter onto the view pager
viewPager.setAdapter(adapter);
TabLayout tabLayout = (TabLayout) getView().findViewById(R.id.sliding_tabs);
tabLayout.setupWithViewPager(viewPager);
}
/**
* Override to set context. This context is used for getSupportFragmentManager in onCreateView
* #param activity
*/
#Override
public void onAttach(Activity activity) {
mContext=(FragmentActivity) activity;
super.onAttach(activity);
}
}
fragment_first.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="#+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabBackground="#color/firstTabBackground"
app:tabIndicatorColor="#color/firstTabIndicatorColor"/>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
FirstFragmentPagerAdapter.java:
public class FirstFragmentPagerAdapter extends FragmentPagerAdapter {
private Context context;
public FirstFragmentPagerAdapter(FragmentManager fm, Context mContext){
super(fm);
context = mContext;
}
/**
* The ViewPager asks the adapter for the fragment at a given position,
* i.e. for the 1st fragment, the ViewPager asks for the fragment at
* position 1.
* #param position
* #return
*/
#Override
public Fragment getItem(int position){
if (position == 0){
return new FirstTabInFirstFragmentFragment();
}
else{
return new SecondTabInFirstFragmentFragment();
}
}
/**
* On launch, the ViewPager asks the adapter how many pages there will be.
* Here, our adapter returns how many pages there will be.
* #return
*/
#Override
public int getCount() {return 2;}
#Override
public CharSequence getPageTitle(int position) {
switch(position){
case 0:
return context.getResources().getString(R.string.first_tab_in_first_fragment_page_title);
case 1:
return context.getResources().getString(R.string.second_tab_in_first_fragment_page_title);
default:
return null;
}
}
}
I was able to solve this in two steps. First, in my Main Activiy method createNavItems, in addition to doing a bottomNavigation.setCurrentItem(0);, I also had to manually update the CoordinatorLayout (which has ID content_id) with the default fragment:
getSupportFragmentManager().beginTransaction().replace(R.id.content_id, new FirstFragment()).commit();
Next, to solve the TabLayout issues I had to change this line:
FirstFragmentPagerAdapter adapter = new FirstFragmentPagerAdapter(mContext.getSupportFragmentManager(), mContext);
to
FirstFragmentPagerAdapter adapter = new FirstFragmentPagerAdapter(getChildFragmentManager(), mContext);
The reason for this is that when nesting Fragments inside of other Fragments with ViewPager, getFragmentManager() is for managing Fragments within a Fragment whereas mContext.getSupportFragmentManager() is used for Activities.
Firstly, Inorder to initially load the frstfragment on startup, u have to define the frstfragments view as default view to mainactivitys view.
Secondly, you need to have a framelayout to load the views for each fragment when selecting bottomnavigation menu.. here you are simply replacing the parent layout of mainactivity, which is a wrong concept, i believe.
So you need to create a child framelayout to load fragment views for each bottomnavigation menu items.
edits:
my layout which contains bottomnavigation,
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/content_home"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/app_bar_home"
android:keepScreenOn="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/ll_toolbar_home_search">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="#drawable/btn_style_white"
android:layout_marginRight="10dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_search"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="Search 18000+ products"
android:id="#+id/tv_search_home"/>
</LinearLayout>
</android.support.v7.widget.Toolbar>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/ll_toolbar_home_search"
android:layout_above="#+id/navigation"
android:id="#+id/frame_layout_fragment"
android:background="#color/white">
</FrameLayout>
<com.aurelhubert.ahbottomnavigation.AHBottomNavigation
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
</RelativeLayout>
and java class for that activity( only method that contains bottomnavigation manipulations) is follows:
private void BottomView1() {
navigation = (AHBottomNavigation) findViewById(R.id.navigation);
AHBottomNavigationItem item1 = new AHBottomNavigationItem("Home", R.drawable.ic_home, R.color.ash);
AHBottomNavigationItem item2 = new AHBottomNavigationItem("Category", R.drawable.ic_category, R.color.ash);
AHBottomNavigationItem item3 = new AHBottomNavigationItem("Search", R.drawable.ic_search, R.color.ash);
AHBottomNavigationItem item4 = new AHBottomNavigationItem("Offers", R.drawable.ic_offers, R.color.ash);
AHBottomNavigationItem item5 = new AHBottomNavigationItem("Cart", R.drawable.ic_cart, R.color.ash);
navigation.addItem(item1);
navigation.addItem(item2);
navigation.addItem(item3);
navigation.addItem(item4);
navigation.addItem(item5);
navigation.setCurrentItem(getResources().getColor(R.color.colorPrimaryDark));
navigation.setTitleState(AHBottomNavigation.TitleState.ALWAYS_SHOW);
setCartNotification();
navigation.setAccentColor(getResources().getColor(R.color.colorPrimaryDark));
navigation.setOnTabSelectedListener(new AHBottomNavigation.OnTabSelectedListener() {
#Override
public boolean onTabSelected(int position, boolean wasSelected) {
Intent ii ;
Fragment selectedFragment = null;
if(position==0){
selectedFragment = HomeFragment.newInstance();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_layout_fragment, selectedFragment);
transaction.commit();
} else if(position==1){
ii = new Intent(HomeActivity.this, ShopByCategoryActivity.class);
startActivity(ii);
} else if(position==2){
ii = new Intent(HomeActivity.this, SearchActivity.class);
startActivity(ii);
} else if(position==3){
ii = new Intent(HomeActivity.this, OffersActivity.class);
startActivity(ii);
} else if(position==4){
ii = new Intent(HomeActivity.this, MyCartActivity.class);
startActivity(ii);
}
return true;
}
});
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_layout_fragment, HomeFragment.newInstance());
transaction.commit();
}
In the middle of my layout I want to have 2 tabs to choose between viewing 2 different lists in the second half of the screen. How can I do this?
Here is an image illustrating what I wish to achieve. This image has a questions tab and an answers tab in the middle of the screen. Depending on which tab you select it shows a different listview:
I wish to achieve this exact same thing.
I tried doing it with TabHost widget, but for the life of me I couldn't get rid of the title bar (I tried selecting themes with no title bar, tried setting the theme in the manifest to a no title bar one)
I also tried make action bar tabs, but those were at the top of the screen....
How can I create tabs in the middle of my screen like in the image?
I had to do the same today! I tried with PagerTabStrip, because I thought you can't use TabLayout if it's not with a Toolbar. Turns out I was wrong, and it's even used in Google iosched. So you can put a TabLayout + ViewPager wherever you want.
I know you want to do some very custom tabs, which would be easier to customize if they were buttons, but in my opinion it's better to use TabLayout + ViewPager, since it makes things more scalable.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin">
<LinearLayout
android:id="#+id/top_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"></LinearLayout>
<RelativeLayout
android:id="#+id/bottom_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/top_layout"
android:layout_weight="1">
<android.support.design.widget.TabLayout
android:id="#+id/pager_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:minHeight="60dp"
app:tabGravity="fill"
app:tabIndicatorColor="#color/colorAccentSecondary"
app:tabMode="fixed"
app:tabSelectedTextColor="#color/colorAccentSecondary"
app:tabTextAppearance="#style/TournamentWaitingTimerTabTextAppearance" />
<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"
android:layout_below="#+id/pager_header"></android.support.v4.view.ViewPager>
</RelativeLayout>
</LinearLayout>
MainActivity class:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Locate the viewpager in activity_main.xml
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
// Set the ViewPagerAdapter into ViewPager
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(new LeftFragment(), "Players");
adapter.addFrag(new RightFragment(), "Prizes");
viewPager.setAdapter(adapter);
TabLayout mTabLayout = (TabLayout) findViewById(R.id.pager_header);
mTabLayout.setupWithViewPager(viewPager);
}
class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFrag(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
}
You can change the adapter you use, it doesn't matter.
To customize tabs, I'd take a look at this amazing tutorial.
You can try using a ViewPager with PagerTabStrip
You can add fragments as page tabs by implementing a FragmentPagerAdapter
In your layout xml file, you should be able to move around the ViewPager to anywhere in your layout like you would most other components. I haven't tried centering it, but I have placed things above and below it, and it behaves as you would expect.
<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" >
<android.support.v4.view.PagerTabStrip
android:id="#+id/pager_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
</android.support.v4.view.ViewPager>
FragmentPagerAdapter example
public class FragmentPagerAdapter extends android.support.v4.app.FragmentPagerAdapter
{
final int PAGE_COUNT = 2;
private final String[] PAGE_TITLES =
{
"Fragment1",
"Fragment2"
};
public FragmentPagerAdapter()
{
super(getSupportFragmentManager());
}
#Override
public int getCount()
{
return PAGE_COUNT;
}
#Override
public CharSequence getPageTitle(int position)
{
return PAGE_TITLES[position];
}
#Override
public Fragment getItem(int position)
{
switch(position)
{
case 0:
return new Fragment1();
case 1:
return new Fragment2();
default:
return null;
}
}
}
and in your main Acivity:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
mCustomPagerAdapter = new FragmentPagerAdapter();
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mCustomPagerAdapter);
...
}
I have simple app with fragments.
At the beginning list of fragments is empty.
And there are 2 buttons:
ADD FRAGMENT
REMOVE FRAGMENT
And following use-case:
Add fragment
Add fragment
Remove first fragment
Add fragment
As result onCreateView() on 4th step is not executed.
If I add fragment again on 5th step - onCreateView is executed again.
Why is it so? I didn't find right answer in google.
Source code:
public class MyActivity extends FragmentActivity {
private ViewPager pager;
private FragmentAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
pager = (ViewPager) findViewById(R.id.pager);
final Button addButton = (Button) findViewById(R.id.add_button);
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
adapter.addFragment(new MyFragment());
adapter.notifyDataSetChanged();
}
});
final Button removeButton = (Button) findViewById(R.id.remove_button);
removeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
adapter.removeFragment(0);
adapter.notifyDataSetChanged();
}
});
adapter = new FragmentAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
pager.setOffscreenPageLimit(10);
}
private class FragmentAdapter extends FragmentStatePagerAdapter {
final List<MyFragment> fragments = new ArrayList<MyFragment>();
public FragmentAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
return fragments.get(i);
}
#Override
public int getCount() {
return fragments.size();
}
public void addFragment(MyFragment fragment) {
fragments.add(fragment);
}
public void removeFragment(int index) {
fragments.remove(index);
}
}
private class MyFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment, container, false);
}
}
}
main.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"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add"
android:id="#+id/add_button"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Remove first"
android:id="#+id/remove_button"/>
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Text"
android:id="#+id/textView"
android:layout_gravity="center_horizontal"/>
</LinearLayout>
OnCreateView() is only called when the actual Fragment is being drawn on the screen. Since you have removed a fragment at the beginning the pager no longer looks at the same fragment.
To better understand what is going on put a visible number on each of the fragments and then try adding and removing the fragments.
The issue is 'FragmentStatePagerAdapter' and the way it's implemented. The way to fix it is this:
Go to googlesource and grab the source for 'FragmentStatePagerAdapter'.
refactor (name, package,etc) to make it your own implementation.
Extend from your NEW version of 'FragmentStatePagerAdapter'
Modify your NEW version of 'FragmentStatePagerAdapter' using these steps below:
a) Change the mFragments.set(index, object) command to mFragments.add(index, object), in the the 'instantiateItem()' function.
b) Change the mFragments.set(index, object) command to mFragments.remove(index), in the 'destroyItem' function.
All of this assumes that you are using an ArrayList to hold fragments in your 'extends' class and that they are synced to match the ViewPager.
You will need to add a 'pageIndex' to each fragment, so that you can tell ViewPager when your item has 'moved' (due to deleting an item, that is NOT the LAST item).
This took several days to run down...hope it helps someone :-)
I am using Jake Wharton's TabPageIndicator with ViewPager. Each tab is a fragment that extends from SherlockFragment. In one fragment, on item click, I would like to replace a current fragment with another WITHIN THE SAME TAB. Visually, the current fragment disappears, no exception is thrown, but I get an empty space where the new fragment should be.
Code I am using to replace the fragment is:
SherlockFragment frag = new AboutFragment();
getSherlockActivity().getSupportFragmentManager()
.beginTransaction().setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(R.id.pager_content, frag, "dialogAbout")
.hide(this)
.addToBackStack(null)
.commit();
What is the correct way to replace this?
My main_activity xml is as follows:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.viewpagerindicator.TabPageIndicator
android:id="#+id/indicator"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:background="#000000" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1">
<FrameLayout android:id="#+id/pager_content"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</android.support.v4.view.ViewPager>
</LinearLayout>
and in my MainActivity I have set an adapter to viewPager. Adapter extends from FragmentPagerAdapter and looks like follows:
private class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
index = position % CONTENT.length;
switch (index) {
case FRAG_TAG_DEPOSIT:
return new DepositFragment();
case FRAG_TAG_HISTORY:
return new HistoryFragment();
case FRAG_TAG_LOCATIONS:
return new LocationsFragment();
case FRAG_TAG_SETTINGS:
return new SettingsFragment();
default:
return null;
}
}
#Override
public CharSequence getPageTitle(int position) {
return getString(CONTENT[position % CONTENT.length]).toUpperCase();
}
#Override
public int getCount() {
return CONTENT.length;
}
}
What I don't understand here is - in which containter is the fragment returned from the adapter placed? I don't specifally notify where to put it, I simply state which fragment should be put at which position. Could this be the reason replacement fails?
Since you are using ViewPager to load your fragments, you don't need to change the fragment manually. Just set the fragment that you want to switch to using the setCurrentItem like this where num is the index of the fragment that you want to switch to.
viewPager.setCurrentItem(num);