In my app, I have multiple instances of the same fragment, ActivityFragment. In each fragment, there is an activity_text textview. When the fragment is added to the layout, I want to set the activity_text textview within that fragment during onCreate. However, when I try to do this, every ActivityFragment onscreen will have their activity_text textview changed.
Is there any way that I can limit setText to within the scope of an individual fragment without using unique Tags or IDs for each fragment?
Here is my ActivityFragment class:
public static class ActivityFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable final ViewGroup container, Bundle savedInstanceState) {
final View fragment1 = inflater.inflate(R.layout.activity_fragment, container, false);
final TextView activityText = (TextView) fragment1.findViewById(R.id.activity_text);
//Calling setText changes the activityText Textview in every fragment onscreen
activityText.setText(text);
return fragment1;
}
}
Here is my MainActivity class:
public class MainActivity extends FragmentActivity {
Static String text;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i != 5; i++) {
//This ensures each fragment receives a unique String
text = "success" + i;
ActivityFragment myFragment = new ActivityFragment();
getFragmentManager().beginTransaction()
.add(R.id.fragment_container, myFragment).commit();
}
}
}
It looks like there's two problems with your code.
The first is that you're using a static String text to transmit information from your Activity to your Fragments. In general, this is not a good idea; if you need to "pass" something from your Activity to your Fragment at the time it's constructed, you should use Fragment.setArguments(Bundle) to do that.
The second is that you seem to have a misconception about the timing of FragmentTransactions. Your for loop changes the value of text and then .commit()s a new fragment five times. But these transactions are asynchronous, and the fragment lifecycle has its own timeline. So your onCreateView() will actually wind up being called after your activity onCreate() has finished, and so each of your five fragments gets the same value from text.
Here's how I'd solve it:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i != 5; i++) {
Bundle args = new Bundle();
args.putString("text", "success" + i);
ActivityFragment myFragment = new ActivityFragment();
myFragment.setArguments(args);
getFragmentManager().beginTransaction()
.add(R.id.fragment_container, myFragment).commit();
}
}
public static class ActivityFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable final ViewGroup container, Bundle savedInstanceState) {
final View fragment1 = inflater.inflate(R.layout.activity_fragment, container, false);
final TextView activityText = (TextView) fragment1.findViewById(R.id.activity_text);
String text = getArguments().getString("text");
activityText.setText(text);
return fragment1;
}
}
}
Your issue have nothing to do with the scope of the textview.
Each fragment is individually setting the textView to text, as you instructed them to do so. That happens because onCreateView gets called much latter, after all the fragment transactions happened.
On your code you put the comment: //This ensures each fragment receives a unique String, actually, there's nothing there ensuring that fragments are receiving anything. I recommend you reading what a static means in Java. On that for-loop you change the value of text 5 times in a row and the last value is "success4".
The correct way to pass parameters to a fragment is via a Bundle added calling the methong setArguments(Bundle)
I searched and this answer here https://stackoverflow.com/a/35737232/906362 shows very well explained how to do that. The gist is something like this:
for (int i = 0; i != 5; i++) {
ActivityFragment myFragment = new ActivityFragment();
Bundle b = new Bundle();
// This really ensures each fragment receives a unique String
b.putString("text", "success" + i)
myFragment.setArguments(b);
getFragmentManager().beginTransaction()
.add(R.id.fragment_container, myFragment).commit();
}
and then to get this value on the fragment:
TextView activityText = (TextView) fragment1.findViewById(R.id.activity_text);
activityText.setText(getArguments().getString("text");
Related
I used below code to passing data from Activity to Fragment.
Fragment Code
public class StoryDetailFragmentInfo extends Fragment {
View view;
TextView txtDescrible;
final static String getData = "abc";
public static StoryDetailFragmentInfo newInstance(String paramater){
Bundle bundle = new Bundle();
bundle.putString(getData,"Message");
StoryDetailFragmentInfo fragmentInfo = new StoryDetailFragmentInfo();
fragmentInfo.setArguments(bundle);
return fragmentInfo;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getArguments()!=null){
String test = getArguments().getString(getData);
}
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.story_detail_fragment_info_layout, container, false);
txtDescrible = view.findViewById(R.id.txt_describleDetail);
return view;
}
}
MainActivity Code
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.story_detail_layout);
addControls();
loadData();
intentData();
fillDataToView();
}
private void intentData() {
StoryDetailFragmentInfo.newInstance("Hello babe");
}
Problem is getArgument() in my fragment is null and i can't get data What should i do?
Can you help me explain it? Thanks for reading.
If you want to pass data from activity to fragment as initial data, you can send data from activity to ViewPagerAdapter, then pass data from ViewPagerAdapter to fragment in getItem() function.
In Activity:
adapter = new ViewPagerAdapter(getSupportFragmentManager(), this,false));
adapter.setData(data);
In ViewPagerAdapter:
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
MyFragment fragment = new MyFragment();
fragment.setData(data);
return fragment
}
}
If you want to pass data from activity to fragment in real time (like updating data), I recommend to use EventBus. It is a modern and convenient tool to communicate between activity and fragment. Check it at https://github.com/greenrobot/EventBus
Firstly, in the method newInstance of class StoryDetailFragmentInfo, you write these:
bundle.putString(getData,"Message"); while leave paramater alone. So you need change
bundle.putString(getData,"Message");
to
bundle.putString(getData,paramater);
Secondly, you try to get values from arguments from fragment by the key 'AdapterStoryDetailViewPager.idData', but this key is different from the key which you used to save value. So you need to change
String test = getArguments().getString(AdapterStoryDetailViewPager.idData);
to
String test = getArguments().getString(getData);
After these two steps, you would get the right value.
I have a button inside of a fragment that I need to be able to access in the activity to change it's text. I am using this code in my main activity:
CategoryFragment frag = new CategoryFragment();
getSupportFragmentManager().beginTransaction().add(R.id.activity_main, frag).commit();
frag.setButtonText(i);
The problem is the button is never initialized using the onCreateView() method (that method never even gets called) which causes a null pointer exception. I tried adding an onCreate() method in the fragment, which gets called, but I have to get the view in order to initialize my button. Since the view hasn't yet been initialized, I get another null pointer exception from the view. Here is my best attempt at the onCreate():
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
button = (Button) getView().findViewById(R.id.buttonFrag);
}
In OnCreateView() do like this :
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.yout_layout, container, false);
button = (Button) rootView.findViewById(R.id.buttonFrag);
return rootView;
}
You have completely misunderstood the way Fragment and Activity work with each other. An Activity mainly has the duty to "show" the Fragment, and you need to initialize the Button using your CategoryFragment class.
Override Category Fragment's onActivityCreated() and then add the following:
Button button = (Button) getView.findViewById(R.id.your_views_id);
button.setButton("Voila");
Study about Activity and Fragment Interaction. This might help u. http://simpledeveloper.com/how-to-communicate-between-fragments-and-activities/
You can use “static factory method” Refer following code
public class CategoryFragment extends Fragment {
/**
* Static factory method that takes an int parameter,
* initializes the fragment's arguments, and returns the
* new fragment to the client.
*/
public static CategoryFragment newInstance(String i) {
CategoryFragment f = new CategoryFragment();
Bundle args = new Bundle();
args.putInt("buttonText", i);
f.setArguments(args);
return f;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam = getArguments().getString("buttonText");
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view=inflater.inflate(R.layout.fragment_category, container, false);
Button b=(Button) view.findViewById(R.id.button);
b.setText(mParam);
return view;
}
}
and from your activity just call
getSupportFragmentManager().beginTransaction().add(R.id.activity_main, CategoryFragment.newInstance(i)).commit();
Here's the code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
String message = "Email: \n" +intent.getStringExtra(MainActivity.ACCOUNT_EMAIL);
setContentView(R.layout.activity_display_message);
TextView textView = (TextView) findViewById(R.id.display_info);
//textView.setText(message);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_display_message,
container, false);
return rootView;
}
}
The ID name in the XML is definitely right, but for whatever reason this commented line always returns null. When I have the line commented the fragment opens with the text view there (i have some placeholder text). I can't access this thing from the code.
You say that the fragment displays with the TextView, but you're attempting to reference the TextView from your Activity (and the Activity's layout).
Without seeing the layouts, I'm guessing the TextView is in fragment_display_message.xml (and not activity_display_message.xml).
It seems that you want to move the logic of accessing the TextView into your Fragment's onCreateView(). Bear in mind that you may want to use Fragment arguments to set the message as obtaining it directly from the Activity's intent will no longer work.
I was experimenting on Android fragments, hence I created two fragments ListFragment and DetailFragment. The problem is that when I click on the ListFragment and call a DetailFragment method to show the selected item from the ListFragment no result is shown on the DetailFragment. Here is the DetailFragment Code :
private static final String DETAIL_FRAG_TAG = "detail_fragment";
private Context appContext = null;
private TextView lblItemDetail = null;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// inflate the fragment layout
View rootView = inflater.inflate(R.layout.fragments_detail_fragment, container, false);
lblItemDetail = (TextView) rootView.findViewById(R.id.lbl_itemDetail);
//at this point the TextView is not null===>see L0g.i
Log.i(DETAIL_FRAG_TAG, " ---MyDetailFragment---oncreateView()--lblItemDetail =[" + lblItemDetail + "]");
// get the fragment activity context
appContext = this.getActivity();
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
}
/**
* show the details of the item selected on the listFragment.
* #param itemDetail - the details of the item selected on ListFragment.
*/
public void showLstItemDetail(String itemDetail) {
if (lblItemDetail != null) {
// the View to show Text should not be Null.
lblItemDetail.setText(itemDetail);
}
//at this point calling this method shows
that the `TextView` is Null yet it's
initialized in the
oncreate() as a class member variable ---why am i
getting Null after the `oncreate` is finished.
Log.i(DETAIL_FRAG_TAG, "------showItemDetail---------msg=[" + itemDetail + "] txt=[" + lblItemDetail + "]");
}
//when I create an instance of `MYDetailFragment` and call the method to show the details of item Selected on the `DetailFragment` the `TextView` will be null. Why?
MYDetailFragment detailFrag = new MyDetailFragment();
detailFrag.showLstItemDetail("Selected List Item");
Please verify the following tutorial about fragments if any useful information Click here
during those two lines:
MYDetailFragment detailFrag = new MyDetailFragment();
detailFrag.showLstItemDetail("Selected List Item");
the onCreateView() was not called yet. That means the fragment rootView was never created and the TextView was never created YET!
the view will only be created after you use a fragment transaction, to put that fragment in the layout, the fragment will then be attached to the activity (onAttach()) and after a few more callbacks onCreateView() will be called. Only then anything can be set to it.
The standard good practice to pass parameters to a fragment is using a Bundle. Look an example code:
on the activity:
MYDetailFragment detailFrag = new MYDetailFragment();
Bundle b = new Bundle();
detailFrag.setArguments(b);
b.putString("detail", value);
// then proceed to the fragment transaction
then on your fragment:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// inflate the fragment layout
View rootView = inflater.inflate(R.layout.fragments_detail_fragment, container, false);
lblItemDetail = (TextView) rootView.findViewById(R.id.lbl_itemDetail);
Bundle b = getArguments();
lblItemDetail.setText(b.getString("details"));
public class MainActivity extends Activity implements MainMenuFragment.OnMainMenuItemSelectedListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
// add menu fragment
MainMenuFragment myFragment = new MainMenuFragment();
fragmentTransaction.add(R.id.menu_fragment, myFragment);
//add content
DetailPart1 content1= new DetailPart1 ();
fragmentTransaction.add(R.id.content_fragment, content1);
fragmentTransaction.commit();
}
public void onMainMenuSelected(String tag) {
//next menu is selected replace existing fragment
}
I have a need to display two list views side by side, menu on left and its content on right side. By default, the first menu is selected and its content is displayed on right side. The Fragment that displays content is as below:
public class DetailPart1 extends Fragment {
ArrayList<HashMap<String, String>> myList = new ArrayList<HashMap<String, String>>();
ListAdapter adap;
ListView listview;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(savedInstanceState!=null){
myList = (ArrayList)savedInstanceState.getSerializable("MYLIST_obj");
adap = new LoadImageFromArrayListAdapter(getActivity(),myList );
listview.setAdapter(adap);
}else{
//get list and load in list view
getlistTask = new GetALLListTasks().execute();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.skyview_fragment, container,false);
return v;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("MYLIST_obj", myList );
}
}
The onActivityCreated and onCreateView are called twice. There are many examples out there using fragments. Since I am beginner in this subject, I am unable relate the example with my problem. I need a fool proof way to handle orientation change. I have NOT declared android:configChanges in manifest file. I need the activity destroy and recreate so that I can use different layout in landscape mode.
You are creating a new fragment every time you turn the screen in your activity onCreate(); But you are also maintaining the old ones with super.onCreate(savedInstanceState);. So maybe set tag and find the fragment if it exists, or pass null bundle to super.
This took me a while to learn and it can really be a pain when you are working with stuff like viewpager.
I'd recommend you to read about fragments an extra time as this exact topic is covered.
Here is an example of how to handle fragments on a regular orientation change:
Activity:
public class MainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
TestFragment test = new TestFragment();
test.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, test, "your_fragment_tag").commit();
} else {
TestFragment test = (TestFragment) getSupportFragmentManager().findFragmentByTag("your_fragment_tag");
}
}
}
Fragment:
public class TestFragment extends Fragment {
public static final String KEY_ITEM = "unique_key";
public static final String KEY_INDEX = "index_key";
private String mTime;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_layout, container, false);
if (savedInstanceState != null) {
// Restore last state
mTime = savedInstanceState.getString("time_key");
} else {
mTime = "" + Calendar.getInstance().getTimeInMillis();
}
TextView title = (TextView) view.findViewById(R.id.fragment_test);
title.setText(mTime);
return view;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("time_key", mTime);
}
}
A good guideline about how to retain data between orientation changes and activity recreation can be found in android guidelines.
Summary:
make your fragment retainable:
setRetainInstance(true);
Create a new fragment only if necessary (or at least take data from it)
dataFragment = (DataFragment) fm.findFragmentByTag("data");
// create the fragment and data the first time
if (dataFragment == null) {