I am implementing nested fragments in a ViewPager. When I perform the transaction by replacing the child fragment in the parent fragment after executing addToBackStack(), I am able to see the view shown below. But when I press back button and the code reaches the MainActivity's onBackPressed() method, I notice the stack size is 1, and I successfully popout the Fragment. The problem starts now
My app closes and for a brief amount of time, I see my child fragment closing and then the app closing.
MainActivity
public class MainActivity extends FragmentActivity implements FragmentCommunicationListener {
ViewPager viewPager;
PagerAdapter pagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//createTabs();
viewPager = (ViewPager) findViewById(R.id.pager);
Fragment[] fragments = new Fragment[]{
new ViewStudentsFragment(),
new AddStudentFragment()
};
pagerAdapter = new PagerAdapter(getSupportFragmentManager(), this, fragments);
viewPager.setAdapter(pagerAdapter);
}
#Override
public void passMsg(Fragment fragment, Bundle msg) {
int currentItem = viewPager.getCurrentItem();
Fragment targetFragment;
if (currentItem == 0) {
targetFragment = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + 1);
((AddStudentFragment) targetFragment).renderData(msg);
viewPager.setCurrentItem(1);
} else {
targetFragment = getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + 0);
((ViewStudentsFragment) targetFragment).updateList(msg,viewPager);
viewPager.setCurrentItem(0);
}
}
#Override
public void onBackPressed() {
ViewStudentsFragment fragment = (ViewStudentsFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + 0);
super.onBackPressed();
if (fragment.getChildFragmentManager().getBackStackEntryCount() != 0) {
fragment.getChildFragmentManager().popBackStack();
}
}
}
Parent Fragment
public class ViewStudentsFragment extends Fragment {
ArrayList<Student> studentList = new ArrayList<Student>();
ListView list;
ListViewAdapter listAdapter;
GridView grid;
FragmentCommunicationListener fragComm = null;
int resIdView, resIdSort;
GridViewAdapter gridAdapter;
static ImageButton toggleView, toggleSort;
View view;
ViewPager viewPager=null;
public ViewStudentsFragment() {
}
private static ViewStudentsFragment viewStudentsFragment;
FragmentTransaction transaction;
Fragment details ;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_view_students, container, false);
toggleView = (ImageButton) view.findViewById(R.id.viewImageButton);
toggleSort = (ImageButton) view.findViewById(R.id.sortImageButton);
transaction= getChildFragmentManager().beginTransaction();
view.findViewById(R.id.container).setVisibility(View.GONE);
...
toggleView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
...
}
});
toggleSort.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
...
}
});
registerForContextMenu(list);
registerForContextMenu(grid);
return view;
}
public void updateList(Bundle bundle,ViewPager vp) {
viewPager=vp;
...
}
listAdapter.notifyDataSetChanged();
gridAdapter.notifyDataSetChanged();
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof FragmentCommunicationListener) {
fragComm = (FragmentCommunicationListener) activity;
} else {
throw new ClassCastException();
}
}
#Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
...
}
#Override
public boolean onContextItemSelected(MenuItem item) {
...
switch (menuItemIndex) {
case Constant.VIEW_STUDENT_DETAIL:
bundle.putSerializable("viewField", student); view.findViewById(R.id.container).setVisibility(View.VISIBLE);
transaction.addToBackStack(null);
details = new Details();
transaction.replace(R.id.container, details, "task");
transaction.commit();
details.setArguments(bundle);
break;
...
default:
}
listAdapter.setStudentList(studentList);
gridAdapter.setStudentList(studentList);
listAdapter.notifyDataSetChanged();
gridAdapter.notifyDataSetChanged();
return true;
}
}
ChildFragment
public class Details extends Fragment {
View view;
static Details details;
Context context;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
context=container.getContext();
Bundle bundle = getArguments();
view=inflater.inflate(R.layout.display_details, container, false);
...
return view;
}
#Override
public void onDetach() {
super.onDetach();
view.findViewById(R.id.displayDetails).setVisibility(View.GONE);
}
#Override
public void onDestroyView() {
super.onDestroyView();
view.findViewById(R.id.displayDetails).setVisibility(View.GONE);
}
#Override
public void setArguments(Bundle bundle) {
super.setArguments(bundle);
}
}
PagerAdapter
public class PagerAdapter extends FragmentPagerAdapter {
private String[] tabMenu;
private int pageCount;
private Context context;
private Fragment[] fragments;
public PagerAdapter(FragmentManager fm, Context context, Fragment[] fragments) {
super(fm);
this.context = context;
tabMenu = context.getResources().getStringArray(R.array.tab_menu);
pageCount = tabMenu.length;
this.fragments = fragments;
}
#Override
public Fragment getItem(int position) {
return fragments[position];
}
#Override
public int getCount() {
return fragments.length;
}
#Override
public CharSequence getPageTitle(int position) {
return tabMenu[position];
}
}
If you use FragmentTransaction.addToBackStack(), the back button is handled automatically for you.
When you do this:
#Override
public void onBackPressed() {
ViewStudentsFragment fragment = (ViewStudentsFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + 0);
super.onBackPressed();
if (fragment.getChildFragmentManager().getBackStackEntryCount() != 0) {
fragment.getChildFragmentManager().popBackStack();
}
}
you are basically popping the backstack twice, which is why your application quit.
Replace the onBackPressed() method in your Activity with this:
#Override
public void onBackPressed() {
ViewStudentsFragment fragment = (ViewStudentsFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.pager + ":" + 0);
if (fragment.getChildFragmentManager().getBackStackEntryCount() != 0) {
fragment.getChildFragmentManager().popBackStack();
}
else {
super.onBackPressed();
}
}
Related
My application structure is like this:
ActivityMedia is main activity that has 2 tabs: Photo & Video, each tab is FragmentCategory.
In each tab, the 1st screen is list of Category, and when click on a category, will show FragmentMediaList, which is list of Media for the category, with this code:
((ActivityMedia) getActivity()).addFragment(FragmentMediaList.newInstance(mediaType, aSite, null));
When clicking back from FragmentMediaList of Photo, it will show FragmentCategory of Photo, same for Video tab
In FragmentMediaList, when click on an item, will show ActivityMediaPlayer with this code:
Intent it = new Intent(getActivity(),ActivityMediaPlayer.class);
getActivity().startActivity(it);
When clicking back from ActivityMediaPlayer, app will show FragmentMediaList , back again, will show FragmentCategory
Everything was fine when I tested on my device, but on some of user's devices, when starting ActivityMediaPlayer, the ActivityMedia seems to be destroyed. When click back from ActivityMediaPlayer, the FragmentMediaList is showed, click back again, FragmentCategory is showed, but when click on a category in FragmentCategory, crash happens in this line of code of HostFragment class:
getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).addToBackStack(null).commit();
It said "Activity has been destroyed"
Here are my classes details:
ActivityMedia:
public class ActivityMedia extends BaseActivity
...
protected void onCreate(Bundle savedInstanceState) {
viewPager = (ViewPager) findViewById(R.id.viewpager);
tabLayout = (TabLayout) findViewById(R.id.tablayout);
customPagerAdapter = new CustomPagerAdapter(getSupportFragmentManager());
// increase this limit if you have more tabs!
viewPager.setOffscreenPageLimit(1);
viewPager.setAdapter(customPagerAdapter);
tabLayout.setupWithViewPager(viewPager);
...
#Override public void onBackPressed() {
if(!BackStackFragment.handleBackPressed(getSupportFragmentManager())){
super.onBackPressed();
}
}
CustomPagerAdapter:
public class CustomPagerAdapter extends FragmentStatePagerAdapter {
private final List<String> tabTitles = new ArrayList<String>() {{
add("VIDEO");
add("PHOTO");
}};
private List<Fragment> tabs = new ArrayList<>();
public CustomPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
initializeTabs();
}
public CustomPagerAdapter(FragmentManager fragmentManager, Fragment fragment1, Fragment fragment2) {
super(fragmentManager);
tabs.add(fragment1);
tabs.add(fragment2);
}
private void initializeTabs() {
tabs.add(HostFragment.newInstance(FragmentSite.newInstance(MediaType.Movie)));
tabs.add(HostFragment.newInstance(FragmentSite.newInstance(MediaType.Photo)));
}
#Override
public Fragment getItem(int position) {
return tabs.get(position);
}
#Override
public int getCount() {
return tabs.size();
}
#Override
public CharSequence getPageTitle(int position) {
return tabTitles.get(position);
}
}
HostFragment:
public class HostFragment extends BackStackFragment {
private Fragment fragment;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.host_fragment, container, false);
if (fragment != null) {
replaceFragment(fragment, false);
}
return view;
}
public void replaceFragment(Fragment fragment, boolean addToBackstack) {
try{
if (addToBackstack) {
getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).addToBackStack(null).commit();
} else {
getChildFragmentManager().beginTransaction().replace(R.id.hosted_fragment, fragment).commit();
}
}catch (Exception e){
getChildFragmentManager().beginTransaction().remove(this);
K.log("Error when commit fragment: " + e.getLocalizedMessage());
}
}
public static HostFragment newInstance(Fragment fragment) {
HostFragment hostFragment = new HostFragment();
hostFragment.fragment = fragment;
return hostFragment;
}
}
BackStackFragment:
public abstract class BackStackFragment extends BaseFragment {
public static boolean handleBackPressed(FragmentManager fm)
{
if(fm.getFragments() != null){
for(Fragment frag : fm.getFragments()){
if(frag != null && frag.isVisible() && frag instanceof BackStackFragment){
if(((BackStackFragment)frag).onBackPressed()){
return true;
}
}
}
}
return false;
}
protected boolean onBackPressed()
{
FragmentManager fm = getChildFragmentManager();
if(handleBackPressed(fm)){
return true;
} else if(getUserVisibleHint() && fm.getBackStackEntryCount() > 0){
fm.popBackStack();
return true;
}
return false;
}
}
I have a problem in my Application, I load a fragment in my activity and in the fragment i used the ViewPager(for TabLayout) so i want to handle the BackPressed of ViewPager.
Because there is no OnKeyDown override method is available in fragment so i want the refrence of my Fragment ViewPager in my Parent Activty so i can handle this situation in onBackPressed() method Activity.
Activity Code :
fragment= new TabFragment();
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.containerView, fragment);
ft.commitAllowingStateLoss();
// ft.commit();
}
#Override
public void onBackPressed() {
if (isMainShown) {
// want the refrence of view pager so i can handle ViewPager on backPress
}
else {
//display tab fragment
}
//super.onBackPressed();
}
TabFragment :
public class TabFragment extends Fragment {
public TabFragment() {
// Required empty public constructor
}
public static TabLayout tabLayout;
public static ViewPager viewPager;
public static int int_items = 2 ; View x;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
x=inflater.inflate(R.layout.content_main_menu,null);
tabLayout=(TabLayout) x.findViewById(R.id.tabs);
viewPager =(ViewPager) x.findViewById(R.id.viewpager);
setupViewPager(viewPager);
//viewPager.setAdapter(new MyAdapter(getChildFragmentManager()));
viewPager.setOffscreenPageLimit(1);
tabLayout.post(new Runnable() {
#Override
public void run() {
tabLayout.setupWithViewPager(viewPager);
}
});
return x;
}
#RequiresApi(api = Build.VERSION_CODES.KITKAT)
#Override
public void onDetach() {
super.onDetach();
try {
Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private void setupViewPager(ViewPager viewPager) {
MyAdapter adapter = new MyAdapter(getChildFragmentManager());
adapter.addFragment(new TabOne(), "TAb1");
adapter.addFragment(new TabTwo(), "Tab2");
viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
final InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(x.getWindowToken(), 0);
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}
private class MyAdapter extends FragmentPagerAdapter{
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
return super.instantiateItem(container, position);
}
}
}......
add this code in your onBackPressed()
fragobjTab = (TabFragment) getSupportFragmentManager().findFragmentById(R.id.containerView);
try {
fragobjTab.onBackPressed();
}catch (Exception e){
e.printStackTrace();
}
And create a onBackPressed method in your tabFragment and setCurrentItem to the ViewPager:
public void onBackPressed()
{
// set page to your viewPager
// 0 is your first viewpager fragment
viewPager.setCurrentItem(0,true);
}
I have an activity and pager adapter in it which displays 3 fragments. The fragments are shuffled. I need to know the number of each page when I am swiping through them and that number needs to be displayed in a textView. For example, when I am in the first page number 1 is displayed, second page - number 2 and so on.
PagerAdapter.java
public class PagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
#Override
public Fragment getItem(int arg0) {
return this.fragments.get(arg0);
}
#Override
public int getCount() {
return this.fragments.size();
}
public void setFragments(List<Fragment> fragments) {
this.fragments = fragments;
}
}
mainActivity.java
public class testActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pager_adapter);
initialisePaging();
}
private void initialisePaging() {
List<Fragment> fragments = new Vector<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()));
PagerAdapter mPagerAdapter = new PagerAdapter(this.getSupportFragmentManager(), fragments);
Collections.shuffle(fragments, new Random(System.nanoTime()));
ViewPager pager = (ViewPager) findViewById(R.id.viewpager);
pager.setAdapter(mPagerAdapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_test, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
If you want to be displaying the number in your fragment, use this
In your Activity
#Override
public Fragment getItem(int position) {
// Here you can add an argument that will be passed to the fragment
// In your case you need to pass the page number
Fragment fragment = this.fragments.get(arg0);
Bundle b = new Bundle();
b.putInt("page_number", position);
fragment.setArguments(b);
return fragment;
}
In you fragment do this
int number;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Read the data that Activity passed to us
if (savedInstanceState == null) {
Bundle args = getArguments();
if (args != null) {
number = args.getInt("page_number") + 1;
}
}
else {
number = savedInstanceState.getInt("page_number");
}
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
// Here you need to find your TextView and show the number
View v = inflater.inflate(R.layout.some_layout, container, false);
((TextView) v.findViewById(R.id.someTextView)).setText(String.format("%d", number));
return v;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("page_number", number);
}
If you want to be displaying the number in your Activity, use this
private void initialisePaging() {
List<Fragment> fragments = new Vector<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()));
PagerAdapter mPagerAdapter = new PagerAdapter(this.getSupportFragmentManager(), fragments);
Collections.shuffle(fragments, new Random(System.nanoTime()));
// !!! Change ME!!!!!
textView = (TextView) findViewById(R.id.someTextView);
ViewPager pager = (ViewPager) findViewById(R.id.viewpager);
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int i, float v, int i2) {
}
#Override
public void onPageSelected(int i) {
textView.setText("Position: " + i);
}
#Override
public void onPageScrollStateChanged(int i) {
}
});
pager.setAdapter(mPagerAdapter);
}
Use PageChangeListener as:
yourPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position)
{
yourTextView.setText("Page: " + position);
}
... ...
});
I'm having an Activity that hosts two Fragments. One which contains an EditText and one that shows the input in a GridView. Fragment1 implements an interface to notify when the user wants to save the input. In the Activity I want to pass the data to the ArrayAdapter of Fragment2 but here I get the NullPointerException.
Activity:
public class SwipeTest extends FragmentActivity implements TestFragment1
.OnFragmentInteractionListener {
private final String[] tabs = {"Test 1", "Test 2"};
private ViewPager pager;
private ActionBar actionBar;
private TabsPagerAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.pager = new ViewPager(this);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup
.LayoutParams.MATCH_PARENT,
ViewGroup
.LayoutParams.MATCH_PARENT);
this.pager.setLayoutParams(params);
setContentView(this.pager);
this.actionBar = getActionBar();
this.adapter = new TabsPagerAdapter(
getSupportFragmentManager());
this.pager.setAdapter(this.adapter);
this.actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
setUpViewPager();
setUpActionBar();
}
/**
* Sets up the ViewPager and adds an OnPageChangeListener
*/
private void setUpViewPager() {
this.pager.setAdapter(this.adapter);
// pager needs an id; crashes if it has none
this.pager.setId(123456789);
// Set up the listener
ViewPager.OnPageChangeListener onPageChangeListener = new ViewPager
.OnPageChangeListener() {
#Override
public void onPageScrolled(int i, float v, int i2) {
}
#Override
public void onPageSelected(int i) {
SwipeTest.this.actionBar.setSelectedNavigationItem(i);
}
#Override
public void onPageScrollStateChanged(int i) {
}
};
this.pager.setOnPageChangeListener(onPageChangeListener);
}
/**
* Sets up the ActionBar with it's tabs and adds an ActionBar.TabListener to
* them
*/
private void setUpActionBar() {
this.actionBar = getActionBar();
this.actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Set up listener
ActionBar.TabListener tabListener = new ActionBar.TabListener() {
#Override
public void onTabSelected(ActionBar.Tab tab,
FragmentTransaction
fragmentTransaction) {
SwipeTest.this.pager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab,
FragmentTransaction
fragmentTransaction) {
}
#Override
public void onTabReselected(ActionBar.Tab tab,
FragmentTransaction
fragmentTransaction) {
}
};
for (String tab_name : this.tabs) {
this.actionBar.addTab(
this.actionBar.newTab()
.setText(tab_name)
.setTabListener(tabListener));
}
}
/**
* Interface method of Fragment1
*/
#Override
public void onFragmentInteraction(String s) {
// This gets the fragment correct
TestFragment2 fragment = (TestFragment2) ((TabsPagerAdapter) pager
.getAdapter()).getItem(1);
// This assigns null to adapter
ArrayAdapter<String> adapter = (ArrayAdapter<String>) fragment
.getAdapter();
adapter.add(s);
adapter.notifyDataSetChanged();
}
public class TabsPagerAdapter extends FragmentPagerAdapter {
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
return new TestFragment1();
case 1:
return new TestFragment2();
}
return null;
}
#Override
public int getCount() {
return 2;
}
}
}
Fragment1:
public class TestFragment1 extends Fragment {
private OnFragmentInteractionListener mListener;
private EditText edit;
public TestFragment1() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
LinearLayout layout = new LinearLayout(getActivity());
layout.setLayoutParams(params);
this.edit = new EditText(getActivity());
Button btn = new Button(getActivity());
btn.setText("Save");
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onButtonPressed();
}
});
layout.addView(this.edit);
layout.addView(btn);
return layout;
}
public void onButtonPressed() {
if (this.mListener != null) {
String input = this.edit.getText().toString();
this.mListener.onFragmentInteraction(input);
}
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
this.mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement " +
"OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
this.mListener = null;
}
public interface OnFragmentInteractionListener {
public void onFragmentInteraction(String s);
}
}
Fragment2:
public class TestFragment2 extends Fragment {
private final List<String> data = new ArrayList<String>();
private ArrayAdapter<String> adapter;
public TestFragment2() {
// Required empty public constructor
}
public ArrayAdapter getAdapter() {
return this.adapter;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
GridView view = new GridView(getActivity());
GridView.LayoutParams params = new AbsListView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
view.setLayoutParams(params);
this.data.add("Hello");
this.data.add("World");
this.adapter = new ArrayAdapter(getActivity(),
android.R.layout
.simple_list_item_1,
this.data);
view.setAdapter(this.adapter);
return view;
}
}
I don't know why ArrayAdapter<String> adapter = (ArrayAdapter<String>) fragment.getAdapter(); is null. So, how do I get access to the fields of Fragment2?
It seems problem about lifecycle. As you may know, onCreateView() is async method, so fragment instance could be accessed before setAdapter().
My suggestion is to create TestFragment2 instance in SwipeTest#onCreate(). Also, your code creates new instance every time button pressed. It seems not good idea.
The problem was within the PagerAdapter. I used getItem() to recieve the Fragment which is bound to the tab's position. The mistake was that I returned a new instance of that Fragment instead of returning the existing Fragment. Since that new Fragment has never been shown the Fragment's onCreateView() and hence setUpAdapter() has never been called. That's why I recieved NPE in
ArrayAdapter<String> adapter = (ArrayAdapter<String>) fragment
.getAdapter();
adapter.add(s);
Approach[*]:
public class TabsPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragments = new ArrayList<Fragment>();
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
mFragments.add(0, new TestFragment1());
mFragments.add(1, new TestFragment2());
}
#Override
public Fragment getItem(int index) {
if (index > (mFragments.size() - 1) )
return null;
return mFragments.get(index);
}
#Override
public int getCount() {
return mFragments.size();
}
}
[*] Untested since I changed my code and don't use the PagerAdapter anymore but I think the solution is comprehensible.
If the pager has never shown the second fragment, when your fragment1 interface callback is called the pager returns null:
// This gets the fragment correct
TestFragment2 fragment = (TestFragment2) ((TabsPagerAdapter) pager
.getAdapter()).getItem(1);
So your subsequent call to getAdapter() will result in the NPE.
I have a parent SherlockFragmentActivity class that contains ViewPager with 4 Fragments inside.
One of them extends from SherlockListFragment and I want to scroll it to top by click on it's tab.
MainActivity class:
public class MainActivity extends SherlockFragmentActivity {
ViewPager viewPager;
TabAdapter tabAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
setTheme(Constants.THEME);
viewPager = new ViewPager(this);
viewPager.setId(R.id.pager);
viewPager.setBackgroundResource(R.color.background_color);
tabAdapter = new TabAdapter(this,viewPager);
super.onCreate(savedInstanceState);
setContentView(viewPager);
// Action bar setup
setupTabs(savedInstanceState);
}
private void setupTabs(Bundle savedInstanceState) {
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for (int i = 1; i <= 4; i++) {
ActionBar.Tab tab = getSupportActionBar().newTab();
switch (i) {
case 1:
tab.setText(R.string.feed).setTabListener(tabAdapter);
tabAdapter.addTab(EventListFragment.class);
break;
case 2:
tab.setText(R.string.downloads).setTabListener(tabAdapter);
tabAdapter.addTab(LocalEventListFragment.class);
break;
case 3:
tab.setText(R.string.tags).setTabListener(tabAdapter);
tabAdapter.addTab(TagsFragment.class);
break;
case 4:
tab.setText(R.string.settings).setTabListener(tabAdapter);
tabAdapter.addTab(SettingsFragment.class);
break;
}
getSupportActionBar().addTab(tab);
}
}
TabAdapter class:
public static class TabAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener, ActionBar.TabListener {
private final Context mContext;
private final ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
private final ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
private final ActionBar mActionBar;
private final ViewPager mViewPager;
public TabAdapter(SherlockFragmentActivity activity, ViewPager pager){
super(activity.getSupportFragmentManager());
mContext = activity;
mActionBar = activity.getSupportActionBar();
mViewPager = pager;
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public void addTab(Class<?> clss){
classes.add(clss);
mFragments.add(Fragment.instantiate(mContext, clss.getName(),
null));
}
#Override
public Fragment getItem(int i) {
return mFragments.get(i);
}
public int getId(int index){
return mFragments.get(index).getId();
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
// fixed double call onTabReselected
if (mActionBar.getSelectedNavigationIndex() != position)
mActionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrollStateChanged(int i) {
}
#Override
public int getCount() {
return 4;
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
final int position = tab.getPosition();
final Fragment fragment = mFragments.get(position);
if (fragment != null) {
switch (position) {
case 0:
// call tab's ListFragment scroll to top item
((EventListFragment) fragment).scrollToTop();
break;
case 1:
// do something
break;
}
}
}
onTabReselected called, then user press current tab, so I try to do EventListFragment scrolling:
EventListFragment class:
public class EventListFragment extends SherlockListFragment {
#Override
public void onCreate(Bundle state) {
super.onCreate(state);
mAdapter = new EventListAdapter(getSherlockActivity());
setListAdapter(mAdapter);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
}
#Override
public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View eventListLayout = inflater.inflate(R.layout.eventlist_fragment,null);
ListView lv = (ListView) eventListLayout.findViewById(android.R.id.list);
ViewGroup parent = (ViewGroup) lv.getParent();
//Remove ListView and add PullToRefreshListView in its place
int lvIndex = parent.indexOfChild(lv);
parent.removeViewAt(lvIndex);
mPullToRefreshListView = onCreatePullToRefreshListView(inflater, savedInstanceState);
parent.addView(mPullToRefreshListView, lvIndex, lv.getLayoutParams());
return eventListLayout;
}
public void scrollToTop() {
if (getSherlockActivity() != null) { // null after screen rotation
final ListView listView = getListView();
listView.post(new Runnable(){
public void run() {
listView.smoothScrollToPosition(0);
}});
}
}
It works on application start, but after screen rotation scrollToTop - getSherlockActivity() returns null.
If remove this condition check, have an exception:
java.lang.IllegalStateException: Content view not yet created
at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
at com.project.fragment.EventListFragment.scrollToTop(EventListFragment.java:337)
at com.project.MainActivity$TabAdapter.onTabReselected(MainActivity.java:411)
at com.actionbarsherlock.internal.app.ActionBarWrapper$TabWrapper.onTabReselected(ActionBarWrapper.java:327)
I haven't totally understatinding how to ViewPager and its FragmentPagerAdapter works. So can't find a problem, that occurs.
Use this FragmentPageAdapter:
public class TestFragmentAdapter extends FragmentPagerAdapter implements IconPagerAdapter {
protected static final String[] CONTENT = new String[] { "CATEGORIAS", "PRINCIPAL", "AS MELHORES", };
protected static final int[] ICONS = new int[] {
R.drawable.perm_group_calendar,
R.drawable.perm_group_camera,
R.drawable.perm_group_device_alarms,
};
private int mCount = CONTENT.length;
public TestFragmentAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {Fragment f = null;
switch(position){
case 0:
{
f = new ArrayListFragment();//YourFragment
// set arguments here, if required
Bundle args = new Bundle();
f.setArguments(args);
break;
}
case 1:
{
f = new HomeFragment();//YourFragment
// set arguments here, if required
Bundle args = new Bundle();
f.setArguments(args);
break;
}
case 2:
{
f = new EndlessCustomView();//YourFragment
// set arguments here, if required
Bundle args = new Bundle();
f.setArguments(args);
break;
}
default:
throw new IllegalArgumentException("not this many fragments: " + position);
}
return f;
}
#Override
public int getCount() {
return mCount;
}
#Override
public CharSequence getPageTitle(int position) {
return TestFragmentAdapter.CONTENT[position % CONTENT.length];
}
#Override
public int getIconResId(int index) {
return ICONS[index % ICONS.length];
}
public void setCount(int count) {
if (count > 0 && count <= 10) {
mCount = count;
notifyDataSetChanged();
}
}
}
This Activity:
public class BaseSampleActivity extends SlidingFragmentActivity {
private static final Random RANDOM = new Random();
TestFragmentAdapter mAdapter;
ViewPager mPager;
PageIndicator mIndicator;
protected ListFragment mFrag;
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.themed_titles);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mAdapter = new TestFragmentAdapter(getSupportFragmentManager());
mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
mPager.setCurrentItem(1);
mIndicator = (TitlePageIndicator)findViewById(R.id.indicator);
mIndicator.setViewPager(mPager);
}
Applied solution according to this post:
MainActivity class:
//...
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
FragmentManager manager = getSupportFragmentManager();
for (int i = 0; i < tabAdapter.getClasses().size(); i++) {
String className = tabAdapter.getClasses().get(i).getName();
if (manager.findFragmentByTag(className) != null) {
manager.putFragment(outState, className, manager.findFragmentByTag(className));
}
}
}
//...
TabAdapter class:
//...
public void addTab(Class<?> clss, FragmentManager manager, Bundle savedInstanceState) {
classes.add(clss);
String className = clss.getName();
tags.add(className);
Fragment fragment;
if (savedInstanceState != null) {
fragment = manager.getFragment(savedInstanceState, className);
if (fragment == null) {
fragment = createFragment(className);
manager.beginTransaction().add(fragment, className);
}
} else {
fragment = createFragment(className);
manager.beginTransaction().add(fragment, className);
}
mFragments.add(fragment);
}
//...
where createFragment is a simple method, that creates needed fragment by passed class name.