Why can't I access my view after calling setContentView()? - android

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.

Related

Can I limit setText to within the scope of an individual fragment?

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");

How to initialize a button from a fragment

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();

Android - Text not displayed in TextView in spite of (apparently) working setView

I am working on an application which needs to retrieve some informations from the user and I am using Fragments to do that. The idea is to have a ListFragment that displays the name of different Fragments that allow the user to give informations, when user click on one name in the ListFragment, the corresponding fragment is displayed in full screen if we are in portrait mode or next the ListFragment in landscape mode.
As basis I used the code explained and given in the following links:
google fragment and Dynamic Layouts using the Fragment Manager.
I "slightly" modified the code of the links above to have two TextView in the Fragment used to retrieved informations. I am trying to retain states for this Fragment. I tried to set setRetainInstance to true but it does not work so I am trying with onSaveInstanceState.
In onSaveInstanceState I put the content of the TextView in a bundle and in onCreateView if savedInstanceState is not null then I retrieve the strings to be displayed in the TextView. I can see in debug mode that everything (seems to) work, after onSaveInstanceState the Bundle has the strings and in onCreateView the strings are correctly retrieved and pass to the TextView with setView but at the end the TextView remain empty.
Why does that happens? How can that happen?
Below the code of my fragment:
public class DataFragment extends AbstractFragment {
private final String TAG = getClass().getSimpleName();
private View mView;
private TextView mFirstNameTextView = null;
private TextView mMailTextView = null;
Bundle mArgs;
public DataFragment(){
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
mView = inflater.inflate(R.layout.data_fragment, container, false);
mFirstNameTextView = (TextView)mView.findViewById(R.id.setting_Firstname);
mMailTextView = (TextView)mView.findViewById(R.id.setting_Mail);
if (savedInstanceState!=null){
String test = savedInstanceState.getString("name");
String retest = savedInstanceState.getString("mail");
mFirstNameTextView.setText(test);
mMailTextView.setText(retest);
}
return mView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public void onDetach() {
super.onDetach();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
String test = mFirstNameTextView.getText().toString();
String testmail = mMailTextView.getText().toString();
outState.putString("name", mFirstNameTextView.getText().toString());
outState.putString("mail", mMailTextView.getText().toString());
}
#Override
public void onPause() {
super.onPause();
}
#Override
public int getShownIndex() {
int ret = getArguments().getInt("index", 0);
return ret;
}
}
To be complete this Fragment inherit from AbstractFragment. AbstractFragment as mentioned by its name is an abstract class that inherit from Fragment. I use this class because I plan to have several fragments and this class helps me to write less code. For completeness below is the code for the AbstractFragment:
public abstract class AbstractFragment extends Fragment {
public AbstractFragment(){}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setRetainInstance(true);
}
public int getShownIndex(){
int ret = getArguments().getInt("index", 0);
return ret;
}
}
Edit:
Just to be clear, the problem is not to retrieve data, the problem is to display data. I am retrieving the values I stored using onSaveInstanceState but these values are not displayed in TextView. Below a screenshot of my application with a breakpoint on onSaveInstanceState, this screenshot has been taken after I entre the Strings "foo" and "bar" in my TextView and then I rotate the AVD, we can see on the screenshot that strings "foo" and "bar" are inside the savedInstanceState Bundle and the variables test and retest are correctly initialized to "foo" and "bar", but these strings are not displayed in the TextView
You are confusing arguments and savedInstanceState. Go through the developer documents here to know what is savedInstanceState.
Refer arguments bundle rather than savedInstanceState bundle to make it work proper. Refer the following code
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
mView = inflater.inflate(R.layout.data_fragment, container, false);
mFirstNameTextView = (TextView)mView.findViewById(R.id.setting_Firstname);
mMailTextView = (TextView)mView.findViewById(R.id.setting_Mail);
Bundle arguments = getArguments();
if (arguments !=null){
String test = arguments.getString("name");
String retest = arguments.getString("mail");
mFirstNameTextView.setText(test);
mMailTextView.setText(retest);
}
else {
Log.e(DataFragment.class, "Arguments was null, did not load the name and email TextView");
}
return mView;
}

NullPointerException when calling findViewById in onCreate

Whenever I use findViewById in my Activity's onCreate I get a NullPointerException. For example:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
TextView mTextView = (TextView) findViewById(R.id.textview);
}
I have read that the problem might by that the views may not be fully loaded when I try to find them and that is why I get a null pointer. If I call for findViewById in onCreateView of my Activity's fragment, everything works fine:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
TextView mTextView = (TextView) rootView.findViewById(R.id.textview);
}
However, I need to use mTextView outside this fragment class. I have seen many examples in Android's official documentation where findViewById is used in the Activity's onCreate. What am I doing wrong?
From the layout xmls, place your textview in the layout xml- activity_main so the activity's setContentView can find it there.
Currently the textview is in the layout xml fragment_main
fyi: fragment_main and activity_main are two different layouts. If your app structure doesn't demand the fragment then you don't need it at all.
you can try something like this -
class MainActivity extends Activity{
public TextView tv;
protected void onCreate(Bundle savedInstance){
......
}
public void doActionOnTextView()
{
//at this point you have the textview in tv
// do what you want to do with it
}
}
class MyFragment extends Fragment{
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
TextView mTextView = (TextView) rootView.findViewById(R.id.textview);
mTextView.postRunnable(new Runnable(){
((MainActivity)getActivity()).tv=mTextView;
((MainActivity)getActivity()).doActionOnTextView();
}
}
Its not the best solution, but would work for you now.

Android - Add code to a fragment

I am a beginner in coding for Android and i am trying to do an application with two fragments. Unfortunately, when i add code to set action to my layout, it makes my application crash, so i'm wondering where i should put my code on the fragment file. If i take out the function onCreate, the application doesn't crash and my layout is good.
Here is my code. Thank you so much for your answer.
public class FragmentOne extends Fragment{
public static final String TAG = "FragmentOne";
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = View.inflate(getActivity(), R.layout.fragmentone, null);
return v;
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final EditText etData = (EditText) getView().findViewById(R.id.etData);
}
}
First of all I can see a few mistakes in your code.First of all as #user1873880 mentioned, onCreate() is always called before onCreateView(), so you should consider dealing with your views in onCreateView(). Second mistake which I can see is that you are not creating your View as it's designed to be used on Fragment. In my opinion the way your Fragment should look is like this :
public class FragmentOne extends Fragment {
private static final String TAG = "FragmentOne";
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
// create your view using LayoutInflater
return inflater.inflate(R.layout.fragmentone, container, false);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// do your variables initialisations here except Views!!!
}
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
// initialise your views
EditText etData = (EditText) view.findViewById(R.id.etData);
}
}
Hope this helps you! : )
The reason is that onCreate is invoked before onCreateView, so you can manipulate with your views only after onCreateView callback. For more information check Fragment lifecycle here.

Categories

Resources