View Pager and Fragments inside Fragment - Blank screen - android

I have an Activity "A" which contains a view pager that contains 5 fragments.
One of the fragment has over 7-8 fragments added programmatically.
Each fragment is instantiated using the static method instance() defined in the respective fragment.
for(int i = 0 ; i < 10; i++) {
CustomFragment fragment = (CustomFragment) Class.forName(classNameList.get(i)).instance();
getFragmentManager().beginTransaction().add(R.id.parentLinearLayout, fragment, fragmentTag).commitAllowingStateLoss();
}
Everything works fine when this activity is launched and traversed across different fragments.
But if I launch an activity "B" and return to activity "A". All the other fragments in the view pager load fine except the fragment that contains the set of fragments. It shows up as a blank screen. What could be the problem?

You are using a constructor to instantiate your Fragment and pass your parameters, and when Android re-instantiates the Fragment it doesn't use your constructor, and hence your parameters are disregarded. You should use Arguments to pass your parameters. This is an example of how you can do so:
Inside MyFragment.java:
private static final String KEY_ITEM = "item";
public static MyFragment getFragment(String myItem) {
MyFragment fragment= new MyFragment ();
Bundle args = new Bundle();
args.putString(KEY_ITEM, myItem);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String myItem = getArguments().getString(KEY_ITEM);
}
Inside your ViewPager adapter:
#Override
public Fragment getItem(int position) {
return MyFragment.getFragment("Your parameter here.");
}
Of course, I used a single String as a parameter, you can add as much parameters as you'd like.

Related

how to reload calling fragment with the previous state and set the result got as a callback from the dialog fragment it initiated

This is how my application runs.
The application has several fragments as view pager connecting through main activity. From Second Fragment, I launched custom fragment dialog and upon ok button click inside that fragment dialog, I am fetching the result into main activity. I am using android.support.v4 library.
public class MainActivity extends FragmentActivity implements MyDialog.Communicator {
private static String callBackMessage;
#Override
public Fragment getItem(int pos) {
switch (pos) {
case 0:
return FirstFragment.newInstance();
case 1:
return SecondFragment.newInstance(MainActivity.this);
default:
return ThirdFragment.newInstance();
}
}
#Override
public void onDialogMessage(String callBackMessage) {
MainActivity.callBackMessage = callBackMessage;
}
public static String getCallBackMessage() {
return callBackMessage;
}
This is my MyDialog Fragment class, here I am creating some rows and giving checkboxes to be selected.
private void performOKButtonFunctionality() {
String msg = checkedBoxesCount + "";
communicator.onDialogMessage(msg);
SecondFragment secondFragment = new SecondFragment();
secondFragment.testFunction(row);
dismiss();
}
Now because of my very early days into android and its fragment life cycle, I am calling a method on Second fragment after instantiating it. the row parameter just passes the row number.
Now inside the Second Fragment Class I have some thing like,
public void testFunction(String callBackRow) {
callBackRowNo = Integer.parseInt(callBackRow);
String callBackMessage = MainActivity.getCallBackMessage();
getTableData(callBackMessage + "");
}
In order to save the SecondFragment previous state, I am saving its context by making it static like this,
private static Context activityContext;
public static SecondFragment newInstance(Context context) {
activityContext = context;
SecondFragment f = new SecondFragment();
Bundle b = new Bundle();
f.setArguments(b);
return f;
}
Now doing so, I am able to use my Second Fragment only one time after clicking the ok button from Fragment Dialog. but upon clicking again, the app crashes at this point.
FragmentManager myDialogManager = getActivity().getSupportFragmentManager();
The raised exception is:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.support.v4.app.FragmentManager android.support.v4.app.FragmentActivity.getSupportFragmentManager()' on a null object reference
Now, my questions are:
How do we reenter into the oncreate method of the fragment where it just restores its previous state rather than saving it explicitly before launching
fragment dialog.
How does the fragment dialog pass parameter to the fragment which can be used to update the fragment field.
Is there some easy way to achieve this?
Before asking I searched the SO but could not find anything where this scenario was asked or explained.
I am badly stuck here and any solution/direction is much appreciated.
try getChildFragmentManager() instead of getSupportFragmentManager()
when do you make fragment with newInstace, you can put argsments like this.
public static MyFragment newInstance(int value) {
MyFragment f = new MyFragment();
Bundle args = new Bundle();
args.putInt("value", value);
f.setArguments(args);
return f;
}
it also needs constructor MyFragment()
public MyFragment() {}
get values from args like this
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (getArguments() != null) {
value = getArguments().getInt("value");
}
return inflater.inflate(R.layout.your_layout, container, false);
}

Fragment how to enter onCreateView lifecycle method

I want to create a new instance of a Fragment in a PagerAdapter with some method like Fragment1.newInstance(0);
but this does not enter the Fragment onCreateView method, where I want to take its layout... How can I make it enter the lifecycle method?
EDIT: For the swipe tabs I am using an external library, that extends PagerAdapter, maybe thats the problem?
Everything you need is described here. See section "Adding a fragment to an activity" especially to solve your problem.
This is because ViewPager doesn't recreate fragment on each swipe .. it creates fragment only once and keeps in memory and recreate once after it goes offScreenpageLimit() ...For recreating fragment each time you swipe ,you have to set
mViewPager.setOffscreenPageLimit(0);
But this method is not recommended if you have so many pages in viewpager (atleast more than 10)..
in your pager adapter class return the instance like this
#Override
public Fragment getItem(int position){
return Fragment1.newInstance(position);
}
And in your Fragment create a static instance like this
public static NearbyOffersFragment newInstance(int position) {
Fragment1 f = new Fragment1();
Bundle b = new Bundle();
b.putInt("position", position);
f.setArguments(b);
return f;
}
and onCreate of your fragment retrieve the postion
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
position = getArguments().getInt("position");
}

Update Fragment View within FragmentPagerAdapter in SherlockFragmentActivity

I am following the example FragmentTabsPager under the Samples from ActionBarSherlock.
In my case, I have created a simple SherlockActivity with the TabsPager and the same code from the sample for the FragmentPagerAdapter.
I created two fragments (different classes). The first one contains just four buttons. Depending of which one is clicked, the main Activity gets a code (e.g. 1,2,3,4). I get correctly the code for the pressed button.
Now, in the second Fragment I want to draw something depending on that code, or what is the same, update dynamically its view.
I have been searched for a solution but I haven´t got anything. In this case I have this:
[MainActivity.java]
public static class TabsAdapter extends FragmentPagerAdapter
implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
private final Context mContext;
private final TabHost mTabHost;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
[...]
#Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
I managed to update the Bundle info.args with the new value, but I don´t know how to pass it to the fragment correctly and order it to update its dynamic View.
Is this possible?
Thanks in advance.
UPDATE
Finally, I used what Sunny kindly explained. I kept an SparseArray with my Fragments so I could definitely access them as I know its position inside MainActivity:
[MainActivity.java]
//Once the user presses the button on the first fragment, the callback calls
// this function
public void onImageSelected(int iNumber){
MySecondFragment msf = (MySecondFragment)mTabsAdapter.getFragment(POS_1);
if(msf != null){
msf.setNumImage(iNumber);
msf.updateView();
}
}
And finally in MySecondFragment.java I can access my Layout components (e.g. a GridView), as they were initialized during onCreateView, so I put all the code I needed inside my updateView() method.
For instance, I update the content of a GridView:
[MySecondFragment.java]
myGridView.setBackground(getResources().getDrawable(iCode));
HTH
UPDATE 2
I have included the following code in order to save the fragments into the state bundle of the main activity. When the application changes from portrait to landscape or viceversa (it is recreated) the function getItem is not being called, so the SparseArray map is not refilled again with the necessary Fragments:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(CURRENT_TAB, mTabHost.getCurrentTabTag());
getSupportFragmentManager().putFragment(outState, MyFirstFragment.class.getName(), mTabsAdapter.getFragment(POS_1));
getSupportFragmentManager().putFragment(outState, MySecondFragment.class.getName(),
mTabsAdapter.getFragment(POS_2));
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
MyFirstFragment mff = (MyFirstFragment) getSupportFragmentManager().getFragment(savedInstanceState, MyFirstFragment.class.getName());
if(mff != null)
mTabsAdapter.setFragment(POS_1, mff);
MySecondFragment msf = (MySecondFragment) getSupportFragmentManager().getFragment(
savedInstanceState, MySecondFragment.class.getName());
if(msf != null)
mTabsAdapter.setFragment(POS_2, msf);
mTabHost.setCurrentTabByTag(savedInstanceState
.getString(CURRENT_TAB));
}
}
Also, inside the TabsAdapter class:
public Fragment getFragment(int pos) {
return map.get(pos);
}
public void setFragment (int position, Fragment fragment) {
map.put(position, fragment);
}
HTH
The source for FragmentPagerAdapter shows that Fragments are given the following tag:
"android:switcher:" + viewId + ":" + id;
Where viewId is the R.id of your ViewPager and id is the page number.
Therefore you can retrieve the second page's Fragment by using:
activity.getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + pager.getId() + ":2")
Where pager is your ViewPager. You'd then want to cast that returned Fragment to whatever specific class you have, then call a method to pass in the information you need.
anhanniballake answer might work. But This might break in future.
To actually get the fragment you can define a sparesArray in your TabAdapter class
private SparseArray<Fragment> map;
and add your fragments in this array
#Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
Fragment fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
map.put(position, fragment);
return fragment;
}
You also need to remove the fragment from map when fragment get destroy
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
map.remove(position);
super.destroyItem(container, position, object);
}
Now define a public method in TabAdapter class which can get Fragments
public Fragment getFragment(int pos) {
return map.get(pos);
}
Now in Your Main SherlockFragementActivity you can get the instance of any fragment
MyFragment f = (MyFragment) mTabsAdapter.getFragment(1); // 1 means second fragment from left
Now call any method in your fragments using the fragment instance you got above
f.doAnyThingInMySecondFragment();
doAnyThingInMySecondFragment must be declared public :)

One Activity, multiple Fragments and setRetainInstance

in my app I'm using one activity and two fragments. The app uses a layout with a container so the fragments are added via transactions. The first fragment contains a listview and the other fragment a detail view for the listview items.
Both fragments use setRetainInstance(true). The fragments are added via a replace transaction and addToBackStack(null) is set. The listfragment contains an instance variable which holds some infos for the list. Now I'm changing to detail and press back and the instance variable is null. I read about setRetainInstance and addToBackStack and removed addToBackStack, but even then the instance variable is null.
Does anyone know what I might be doing wrong?
regards,
Thomas
setRetainInstance(true) will tell the FragmentManager to keep the fragment around when the containing Activity is killed and rebuilt for some reason. It doesn't guarantee that the Fragment instance will stick around after a transaction to add or replace. It sounds like your adapter is being garbage collected and you're not creating a new one.
A more generally easy solution would be to make a viewless Fragment to retain your ListAdapter. The way you do this is to create the Fragment, set the retain instance to true, and return null in the method onCreateView(). To add it, just called addFragment(Fragment, String) via the FragmentTransaction. You never remove or replace it, so it will always stay in memory for the length of the app. Screen rotations won't kill it.
Whenever your ListFragment is created, in onCreateView() get the FragmentManager and use either the method findFragmentById() or FindFragmentByTag() to retrieve your retained fragment from memory. Then get the adapter from that fragment and set it as your adapter for the list.
public class ViewlessFragment extends Fragment {
public final static string TAG = "ViewlessFragment";
private ListAdapter mAdapter;
#Override
public ViewlessFragment() {
mAdapter = createAdater();
setRetainInstance(true);
}
#Override
public void onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return null;
}
public ListAdapter getAdapter() {
return mAdapter;
}
}
public class MyListFragment extends ListFragment {
final public static String TAG = "MyListFragment";
#Override
public void onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View returnView = getMyView();
final ViewlessFragment adapterFragment = (ViewlessFragment) getFragmentManager().findFragmentByTag(ViewlessFragment.TAG);
setListAdapter(ViewlessFragment.getAdapter());
return returnView;
}
}
public class MainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle icicle) {
// ... setup code...
final FragmentManager fm = getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
ViewlessFragment adapterFragment = fm.findFragmentByTag(ViewlessFragment.TAG);
if(adapterFragment == null) {
ft.add(new ViewlessFragment(), ViewlessFragment.TAG);
}
ft.add(R.id.fragmentContainer, new MyListFragment(), MyListFragment.TAG);
ft.commit();
}
}

Passing an Object from an Activity to a Fragment

I have an Activity which uses a Fragment. I simply want to pass an object from this Activity to the Fragment.
How could I do it?
All the tutorials I've seen so far where retrieving data from resources.
EDIT :
Let's be a bit more precise:
My Activity has a ListView on the left part. When you click on it, the idea is to load a Fragment on the right part.
When I enter this Activity, an Object Category is given through the Intent. This Object contains a List of other Objects Questions (which contains a List of String). These Questions objects are displayed on the ListView.
When I click on one item from the ListView, I want to display the List of String into the Fragment (into a ListView).
To do that, I call the setContentView() from my Activity with a layout. In this layout is defined the Fragment with the correct class to call.
When I call this setContentView(), the onCreateView() of my Fragment is called but at this time, the getArguments() returns null.
How could I manage to have it filled before the call of onCreateView() ?
(tell me if I'm not clear enough)
Thanks
Create a static method in the Fragment and then get it using getArguments().
Example:
public class CommentsFragment extends Fragment {
private static final String DESCRIBABLE_KEY = "describable_key";
private Describable mDescribable;
public static CommentsFragment newInstance(Describable describable) {
CommentsFragment fragment = new CommentsFragment();
Bundle bundle = new Bundle();
bundle.putSerializable(DESCRIBABLE_KEY, describable);
fragment.setArguments(bundle);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
mDescribable = (Describable) getArguments().getSerializable(
DESCRIBABLE_KEY);
// The rest of your code
}
You can afterwards call it from the Activity doing something like:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
Fragment fragment = CommentsFragment.newInstance(mDescribable);
ft.replace(R.id.comments_fragment, fragment);
ft.commit();
In your activity class:
public class BasicActivity extends Activity {
private ComplexObject co;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_page);
co=new ComplexObject();
getIntent().putExtra("complexObject", co);
FragmentManager fragmentManager = getFragmentManager();
Fragment1 f1 = new Fragment1();
fragmentManager.beginTransaction()
.replace(R.id.frameLayout, f1).commit();
}
Note: Your object should implement Serializable interface
Then in your fragment :
public class Fragment1 extends Fragment {
ComplexObject co;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Intent i = getActivity().getIntent();
co = (ComplexObject) i.getSerializableExtra("complexObject");
View view = inflater.inflate(R.layout.test_page, container, false);
TextView textView = (TextView) view.findViewById(R.id.DENEME);
textView.setText(co.getName());
return view;
}
}
You should create a method within your fragment that accepts the type of object you wish to pass into it. In this case i named it "setObject" (creative huh? :) ) That method can then perform whatever action you need with that object.
MyFragment fragment;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (getSupportFragmentManager().findFragmentById(android.R.id.content) == null) {
fragment = new MyFragment();
getSupportFragmentManager().beginTransaction().add(android.R.id.content, detailsFragment)
.commit();
} else {
fragment = (MyFragment) getSupportFragmentManager().findFragmentById(
android.R.id.content);
}
fragment.setObject(yourObject); //create a method like this in your class "MyFragment"
}
Note that i'm using the support library and calls to getSupportFragmentManager() might be just getFragmentManager() for you depending on what you're working with
Get reference from the following example.
1. In fragment:
Create a reference variable for the class whose object you want in the fragment. Simply create a setter method for the reference variable and call the setter before replacing fragment from the activity.
MyEmployee myEmp;
public void setEmployee(MyEmployee myEmp)
{
this.myEmp = myEmp;
}
2. In activity:
//we need to pass object myEmp to fragment myFragment
MyEmployee myEmp = new MyEmployee();
MyFragment myFragment = new MyFragment();
myFragment.setEmployee(myEmp);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.main_layout, myFragment);
ft.commit();
Passing arguments by bundle is restricted to some data types. But you can transfer any data to your fragment this way:
In your fragment create a public method like this
public void passData(Context context, List<LexItem> list, int pos) {
mContext = context;
mLexItemList = list;
mIndex = pos;
}
and in your activity call passData() with all your needed data types after instantiating the fragment
WebViewFragment myFragment = new WebViewFragment();
myFragment.passData(getApplicationContext(), mLexItemList, index);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.my_fragment_container, myFragment);
ft.addToBackStack(null);
ft.commit();
Remark: My fragment extends "android.support.v4.app.Fragment", therefore I have to use "getSupportFragmentManager()". Of course, this principle will work also with a fragment class extending "Fragment", but then you have to use "getFragmentManager()".
To pass an object to a fragment, do the following:
First store the objects in Bundle, don't forget to put implements serializable in class.
CategoryRowFragment fragment = new CategoryRowFragment();
// pass arguments to fragment
Bundle bundle = new Bundle();
// event list we want to populate
bundle.putSerializable("eventsList", eventsList);
// the description of the row
bundle.putSerializable("categoryRow", categoryRow);
fragment.setArguments(bundle);
Then retrieve bundles in Fragment
// events that will be populated in this row
mEventsList = (ArrayList<Event>)getArguments().getSerializable("eventsList");
// description of events to be populated in this row
mCategoryRow = (CategoryRow)getArguments().getSerializable("categoryRow");
If the data should survive throughout the application lifecycle and shared among multiple fragments or activities, a Model class might come into consideration, which has got less serialization overhead.
Check this design example
This one worked for me:
In Activity:
User user;
public User getUser(){ return this.user;}
In Fragment's onCreateView method:
User user = ((MainActivity)getActivity()).getUser();
Replace the MainActivity with your Activity Name.

Categories

Resources