I've been wrestling with this for quite a while now.
I switched from having a regular Activity to now using fragments (by users recommendations), but I've been getting the same issue.
I have 3 pages that are now 3 fragments inside of a ViewPager. I want the button with the id addSiteButton in the addSite fragment; when clicked, to scroll over to setCurrentItem(2).
Any help is appreciated! Here's my code.
The FragmentActivity
public class fieldsActivity extends FragmentActivity {
private static final int NUMBER_OF_PAGES = 3;
ViewPager mPager;
MyFragmentPagerAdapter mMyFragmentPagerAdapter;
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// to create a custom title bar for activity window
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.fields);
// use custom layout title bar
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.topbar);
mPager = (ViewPager) findViewById(R.id.fieldspager);
mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mMyFragmentPagerAdapter);
mPager.setCurrentItem(1);
}
private static class MyFragmentPagerAdapter extends FragmentPagerAdapter {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int page) {
switch (page) {
case 0: return new settingFields();
case 1: return new addSite();
case 2: return new createSite();
}
return null;
}
#Override
public int getCount() {
return NUMBER_OF_PAGES;
}
}
The class with the button
public class addSite extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (container == null) {
return null;
}
RelativeLayout mRelativeLayout = (RelativeLayout) inflater.inflate(R.layout.add_site, container, false);
final ViewPager mPager = (ViewPager) mRelativeLayout.findViewById(R.id.fieldspager);
Button addSiteButton = (Button) mRelativeLayout.findViewById(R.id.addSiteButton);
addSiteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mPager.setCurrentItem(2, true);
}
});
return mRelativeLayout;
}
}
I feel like I'm going in circles and not getting the desired motion.
Thanks for the help!
Simply make the Fragment as a class inside the FragmentActivity and take out the variable shadowing in onCreateView() since your pager is a global variable.
public class fieldsActivity extends FragmentActivity {
private static final int NUMBER_OF_PAGES = 3;
ViewPager mPager;
MyFragmentPagerAdapter mMyFragmentPagerAdapter;
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// to create a custom title bar for activity window
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.fields);
// use custom layout title bar
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.topbar);
mPager = (ViewPager) findViewById(R.id.fieldspager);
mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mMyFragmentPagerAdapter);
mPager.setCurrentItem(1);
}
public static class addSite extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
RelativeLayout mRelativeLayout = (RelativeLayout) inflater.inflate(R.layout.add_site, container, false);
//final ViewPager mPager = (ViewPager) mRelativeLayout.findViewById(R.id.fieldspager);
Button addSiteButton = (Button) mRelativeLayout.findViewById(R.id.addSiteButton);
addSiteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mPager.setCurrentItem(2, true);
}
});
return mRelativeLayout;
}
}
}
Also consider using the #Override annotation whenever you override a method so you don't make mistakes and use proper naming conventions. Java classes start with a capital.
Related
I have an activity that has a ViewPager, as following:
public class timetables extends FragmentActivity {
private ViewPager viewPager;
private RelativeLayout page1;
private RelativeLayout page2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timetables);
Intent intentTimetables = getIntent();
setTitle(Global.lineName);
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(new MainPageAdapter());
}
class MainPageAdapter extends PagerAdapter {
#Override
public int getCount() {
return 2;
}
#Override
public Object instantiateItem(ViewGroup collection, int position) {
View page;
switch (position) {
case 0:
if (page1 == null) {
page1 = (RelativeLayout) LayoutInflater.from(timetables.this).inflate(R.layout.activity_timetable_1, collection, false);
}
page = page1;
break;
((ViewPager) collection).addView(page, 0);
return page;
}
}
And when I run it, it loads good activity_timetable_1, but it doesn't run the code I have in that class, for example:
public class timetable_1 extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timetable_1);
GridView gridWorking = (GridView) findViewById(R.id.gridWorking);
gridWorking.setAdapter(new textAdapter(this));
}
}
Could anyone tell me what I am doing wrong? Thank you so much
if (page1 == null) {
page1 = (RelativeLayout) LayoutInflater.from(ctx).inflate(R.layout.activity_timetable_1, collection, false);
}
Do you expect when this inflates the custom view you have will be called?
Replace it with:
if (page1 == null) {
page1 = new Activity1Form(this);
}
Activity1Form = timetable_1
Also, extends ViewGroup instead of FragmentActivity at timetable_1, with this you should modify the class, remove the onCreate and place the code inside it on the constructor.
public class Activity1Form extends ViewGroup {
public Activity1Form(Context ctx) {
LayoutInflater.from(timetables.this).inflate(R.layout.activity_timetable_1, collection, true);
GridView gridWorking = (GridView) findViewById(R.id.gridWorking);
gridWorking.setAdapter(new textAdapter(this));
}
}
You should instantiate your class instead of the layout.
EDIT:
Also, your activity_1 (awful name by the way), is a Activity and cant be placed inside another Activity or used as a Fragment, you must extende Fragment or View (or any View child such RelativeLayout/ViewGroup)
In my application the fragment activity holds two fragments, Fragment A and Fragment B. Fragment B is a view pager that contains 3 fragments.
In my activity, to prevent that the fragment is recreated on config changes:
if(getSupportFragmentManager().findFragmentByTag(MAIN_TAB_FRAGMENT) == null) {
getSupportFragmentManager().beginTransaction().replace(R.id.container, new MainTabFragment(), MAIN_TAB_FRAGMENT).commit();
}
Code for Fragment B:
public class MainTabFragment extends Fragment {
private PagerSlidingTabStrip mSlidingTabLayout;
private LfPagerAdapter adapter;
private ViewPager mViewPager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tab, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
this.adapter = new LfPagerAdapter(getChildFragmentManager());
this.mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
this.mViewPager.setAdapter(adapter);
this.mSlidingTabLayout = (PagerSlidingTabStrip) view.findViewById(R.id.sliding_tabs);
this.mSlidingTabLayout.setViewPager(this.mViewPager);
}
}
Code for the adapter:
public class LfPagerAdapter extends FragmentPagerAdapter {
private static final int NUM_ITEMS = 3;
private FragmentManager fragmentManager;
public LfPagerAdapter(FragmentManager fm) {
super(fm);
this.fragmentManager = fm;
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
Log.d("TEST","TEST");
switch (position) {
case 1:
return FragmentC.newInstance();
case 2:
return FragmentD.newInstance();
default:
return FragmentE.newInstance();
}
}
}
My problem is that I am not able to retain the state of the view pager an its child fragments on orientation changes.
Obviously this is called on every rotation:
this.adapter = new LfPagerAdapter(getChildFragmentManager());
which will cause the whole pager to be recreated, right? As a result
getItem(int position)
will be called on every rotation and the fragment will be created from scratch and losing his state:
return FragmentC.newInstance();
I tried solving this with:
if(this.adapter == null)
this.adapter = new LfPagerAdapter(getChildFragmentManager());
in onViewCreated but the result was that on rotation the fragments inside the pager where removed.
Any ideas how to correctly retain the state inside the pager?
You will need to do 2 things to resolve the issue:
1) You should use onCreate method instead of onViewCreated to instantiate LfPagerAdapter;
i.e.:
public class MainTabFragment extends Fragment {
private PagerSlidingTabStrip mSlidingTabLayout;
private LfPagerAdapter adapter;
private ViewPager mViewPager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
this.adapter = new LfPagerAdapter(getChildFragmentManager());
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_tab, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
this.mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
this.mViewPager.setAdapter(adapter);
this.mSlidingTabLayout = (PagerSlidingTabStrip) view.findViewById(R.id.sliding_tabs);
this.mSlidingTabLayout.setViewPager(this.mViewPager);
}
}
2) You will need to extend FragmentStatePagerAdapter instead of FragmentPagerAdapter
Android will automatically recreate your activity on configuration without this line android:configChanges="keyboardHidden|orientation|screenSize" in you manifest so that you can handle onConfiguration change yourself.
The only way then is to use onSaveInstanceState() in both your activityFragement to save viewPager state(current position for example) and in fragments where you need to save stuff
Example on how you can save current position of viewpager and restore it onConfiguration change
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
int position = mViewPager.getCurrentItem();
outState.Int("Key", position );
}
#Override //then restore in on onCreate();
public void onCreated(Bundle savedInstanceState) {
super.onCreated(savedInstanceState);
// do stuff
if (savedInstanceState != null) {
int position= savedInstanceState.getInt("Key");
mViewPager.setCurrentItem(position)
}
}
Of course, this is a very basic example.
Ps: to restore in fragment use onActivityCreated() instead of onCreate() method.
Here is another example on how to retain state : Click me!
i wanted to know if someone can help me solve this issue. I have a button setup and when its clicked, its supposed to navigate to another viewpager page. But everytime its clicked, the app crashes returning the error at onClick at fragmenttab2 class. Here is my Main Activity.
public class MainActivity extends SherlockFragmentActivity {
ViewPager viewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
viewPager.setCurrentItem(1);
}
}
Here is the fragment containing the onClick:
public class FragmentTab1 extends SherlockFragment {
ViewPager viewPager;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Activity activity = getActivity();
if (activity != null) {
addListenerOnButton();
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragmenttab1, container, false);
return view;
}
public void addListenerOnButton() {
Button nextfrag = (Button) getView().findViewById(R.id.btn_nextfrag);
nextfrag.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
((ViewPager) viewPager.findViewById(R.id.pager)).setCurrentItem(1);
}
});
}
}
The log:
E/AndroidRuntime java.lang.NullPointerException
E/AndroidRuntime com.test.seriessample.FragmentTab1$1.onClick(FragmentTab1.java:58)
your viewPager is probably null. I also see that viewPager itself is a ViewPager and in the onClick you search for another ViewPager within that ViewPager which doesn't seem right.
EDIT:
you should add something like this in your activity
public ViewPager getPager() {
return viewPager;
}
And in the onClick of your Fragment
((MainActivity) getActivity()).getPager().setCurrentItem(1);
I have an Activity with a ViewPager containing multiple fragments. how can i now access a TextView in one of that fragments to change its text from the main activity? I tried multiple ways and they all ended in a NullPointerException.
Activity:
public class SummonerOverview extends SherlockFragmentActivity implements TabListener, OnPageChangeListener {
private ViewPager mPager;
private PagerAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.summoner_overview);
initialize();
}
private void initialize() {
// initialize Pager
mPager = (ViewPager) findViewById(R.id.viewpager);
mAdapter = new PagerAdapter(getSupportFragmentManager());
mPager.setAdapter(mAdapter);
mPager.setCurrentItem(1);
mPager.setOnPageChangeListener(this);
}
}
PagerAdapter:
public class PagerAdapter extends FragmentPagerAdapter {
public PagerAdapter(FragmentManager fm) {
super(fm);
frags = new Fragment[3];
frags[0] = new StatisticsFragment(0);
frags[1] = new RatingsFragment(1);
frags[2] = new HistoryFragment(2);
}
private final int NUM_PAGES = 3;
Fragment[] frags;
#Override
public Fragment getItem(int arg0) {
if (arg0 == 0)
return frags[0];
else if (arg0 == 1)
return frags[1];
else
return frags[2];
}
#Override
public int getCount() {
return NUM_PAGES;
}
}
The Fragment:
public class StatisticsFragment extends SherlockFragment {
public StatisticsFragment(int fragNr) {
this.fragNr = fragNr;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_overview_statistics, container, false);
return v;
}
}
The Textview in the StatisticsFragment is labeled with an id in the fragment_overview_statistics.xml, but when i try
TextView tv = (TextView) findViewById(R.id.id_of_the_textview)
tv.setText("text");
from within the onCreate() Method of the Activity after the initialize() Method, i get an Exception.
Okay, after another hour of googling, i think i solves my own question (although i'm sure there is a better method to solve that)
I now hold all the Data that is displayed by the fragments in the MainActivity and make the Fragmets consume this Data in their onCreateView() Method by using public Methods of the Activity.
In the PagerAdapter, i overwrote getItemPosition() to:
public int getItemPosition(Object object) {
return POSITION_NONE;
}
Now, everytime i update some of the Data, i also call mAdapter.notifyDataSetChanged() which leads into a recreation of all fragments.
It works, although it looks like a poor hack for me. Im sure there must be a better solution because now i have to recreate all fragments for changing one TextView of one Fragment, what is surely not the way it is intended to be done.
I am trying to show a FragmentDialog ( created and shown as a dialog NOT added as content in a view hierarchy) where there is a ViewPager whose content is given by a FragmentPagerAdapter (provides Fragments consisting of an image).
The code works perfect when showing ViewPager + FragmentPagerAdapter from a FragmentActivity, but get the following exception when doing it from a FragmentDialog:
"IllegalArgumentException: No view found for id 0x7f040077 for fragment SimpleFragment..."
Here is my code:
A SherlockFragmentActivity with a button to create and show the dialog.
public class BorrameActivity extends SherlockFragmentActivity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.one_act);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
showTheDialog();
}});
}
private void showTheDialog(){
AchGalleryDialog newFragment = AchGalleryDialog.newInstance(achs);
newFragment.show(getSupportFragmentManager(), "dialog");
}
The FragmentDialog:
public class AchGalleryDialog extends DialogFragment{
public AchGalleryDialog(){
}
public static AchGalleryDialog newInstance(){
AchGalleryDialog f = new AchGalleryDialog();
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_user_result, container);
getDialog().setTitle("Hola tronco");
//content to show in the fragments
int[] images = new int[]{R.drawable.d1, R.drawable.d2, R.drawable.d3};
ViewPager pager = (ViewPager) view.findViewById(R.id.pager);
MyFragmentAdapter adapter = new MyFragmentAdapter(getFragmentManager(),images);
pager.setAdapter(adapter);
return view;
}
}
This is the very simple MyFragmentPagerAdapter,
I put only the getItem() method, and nullPointer checks:
#Override
public Fragment getItem(int position) {
return MySimpleFragment.newInstance(images[position]);
}
And finally SimpleFragment:
public class SimpleFragment extends Fragment{
int id;
public static SimpleAchFragment newInstance(int imgId){
SimpleFragment f = new SimpleFragment();
Bundle args = new Bundle();
args.putLong(ID_BUNDLE, imgId);
f.setArguments(args);
return f;
}
public SimpleAchFragment(){
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.id = getArguments() != null ? getArguments().getInt(ID_BUNDLE) : 0;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.simple_fragment, container, false);
FragmentActivity mAct = getActivity();
ImageView img = (ImageView) v.findViewById(R.id.image);
img.setImageDrawable(mAct.getResources().getDrawable(id));
return v;
}
}
More info, if the content passed to the adapter ( an int array with 3 ints) has length zero, then the adapter doesn't try to create any Fragment so dialogs appears correctly but empty (as expected).
The Exception is thrown at SimpleFragment.onCreateView() at the time of inflating.
The id referenced in the exception (as not found) correspond to ViewPager 's id, with is properly defined in R.layout.simple_fragment.
I have try also to build the Dialog with an AlertDialog.builder and also directly with Dialog() contructor, but get the same behaviour.
Try this:
In class AchGalleryDialog
MyFragmentAdapter adapter = new MyFragmentAdapter(getChildFragmentManager(),images);
instead of
MyFragmentAdapter adapter = new MyFragmentAdapter(getFragmentManager(),images);
Because of this:
http://developer.android.com/about/versions/android-4.2.html#NestedFragments
Hope this will help!