Android - Accessing views in a fragment - java

I have a navigation drawer activity where i am attaching different fragments
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, rp_fragment.OnFragmentInteractionListener, sensors_fragment.OnFragmentInteractionListener, stats_fragment.OnFragmentInteractionListener {
I'm trying to modify the text property of a textview contained within a fragment but i keep getting a null pointer exception on the view (java.lang.NullPointerException: Attempt to invoke virtual method 'void com.teicrete.alucard.sempi.sensors_fragment.ctv_tvst(int)' on a null object reference).
Mainactivity:
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (id == R.id.nav_camera) {
ft.replace(R.id.fragment_placeholder, new rp_fragment());
ft.commit();
} else if (id == R.id.nav_gallery) {
ft.replace(R.id.fragment_placeholder, new sensors_fragment());
ft.commit();
gentempdata();
sensors_fragment sf = (sensors_fragment)fm.findFragmentById(R.id.fragment_sensors);
sf.ctv_tvst(1);
Fragment:
public class sensors_fragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
TextView ctv_tv;
TextView otv_tv;
View view;
...
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_sensors, container, false);
TextView ctv_tv = (TextView)view.findViewById(R.id.ctv_tv);
TextView otv_tv = (TextView)view.findViewById(R.id.otv_tv);
//ctv_tv.setText("Test"); Only this works
return view;
}
...
public void ctv_tvst(int mtext) {
ctv_tv.setText(mtext);
}
I've looked at similar posts here but i was not able to resolve my issue. The only place where i am able to modify the textview is within the onCreateView of the fragment (see code above). If i try to do it from the mainactivity or anywhere else i get the null pointer error.
Any insights?
Edit: Please see my response in android_griezmann's post.
For now, i am doing everything that i need to do, inside the fragment classes themselves and then i load them. I would still like to figure out why i can't access its methods or views externally.

Change those lines from onCreateView:
TextView ctv_tv = (TextView)view.findViewById(R.id.ctv_tv);
TextView otv_tv = (TextView)view.findViewById(R.id.otv_tv);
To:
ctv_tv = (TextView)view.findViewById(R.id.ctv_tv);
otv_tv = (TextView)view.findViewById(R.id.otv_tv);
Currently you are assigning reference to new object by TextView ctv_tv but actually you want this reference to be assigned to class field so you don't have to add TextView.

Your problem is Fragment itself not the TextView. You are getting fragment object as null not the textview.
So try to find the fragment like the below.
if (id == R.id.nav_gallery) {
ft.replace(R.id.fragment_placeholder, new sensors_fragment(),"YOUR_TAG_NAME");
ft.commit();
gentempdata();
sensors_fragment sf = (sensors_fragment) fm.findFragmentByTag("YOUR_TAG_NAME");
sf.ctv_tvst(1);
}
Also change this method too!
public void ctv_tvst(int mtext) {
ctv_tv.setText("" + mtext);
}

#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
TextView ctv_tv = (TextView)getView().findViewById(R.id.ctv_tv);
TextView otv_tv = (TextView)getView().findViewById(R.id.otv_tv);
}
override onactivity created, and then refer your views.. Infact it is the best place to get reference to your views withot getting NullPointer Exception

If you want to pass data while replacing or adding then pass the data in your fragment constructor like below
else if (id == R.id.nav_gallery) {
ft.replace(R.id.fragment_placeholder, new sensors_fragment("your data"));
ft.commit();
Then in your fragmet change the textview value based on the constructor's data

Here is the problem. Just remove the TextView
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_sensors, container, false);
**TextView** ctv_tv = (TextView)view.findViewById(R.id.ctv_tv);
**TextView** otv_tv = (TextView)view.findViewById(R.id.otv_tv);
//ctv_tv.setText("Test"); Only this works
return view;
}

Replace the following code
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_sensors,container, false);
TextView ctv_tv = (TextView)view.findViewById(R.id.ctv_tv);
TextView otv_tv = (TextView)view.findViewById(R.id.otv_tv);
//ctv_tv.setText("Test"); Only this works
return view;
}
with
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_sensors,container, false);
ctv_tv = (TextView)view.findViewById(R.id.ctv_tv);
otv_tv = (TextView)view.findViewById(R.id.otv_tv);
//ctv_tv.setText("Test"); Only this works
return view;
}
Create a variable String ctv_tv_string and assign the value in ctv_tvst() method do not assign to the TextView. it would not throw an error as you are not referring any ui element after this in oncreateview ctv_tv.setText(ctv_tv_string)

Related

NPE in Fragment when setting textView

I Have host MainActivity and 2 Fragments: MainFragment and StringsFragment.
I want to set text in TextView (which is in MainFragment) from StringsFragment.
So in StringsFragment I have:
public interface OnFragmentInteractionListener {
void messengeFromBroadcastFragment(String value);
}
public void onClick(View view) {
String myValue = "helloWorld";
mListener.messengeFromBroadcastFragment(myValue);
}
In MainFragment:
TextView tv;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_main, container, false);
tv = view.findViewById(R.id.tv);
return view;
}
public void setTextView(String value){
tv.setText(value);
}
and MainActivity:
public void messengeFromBroadcastFragment(String value) {
mainFragment = new MainFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragmentContainer, mainFragment);
transaction.addToBackStack(null);
transaction.commit();
mainFragment.setTextView(value);
}
And when I call onClick from StringsFragment, I got nullpointer:
setText(java.lang.CharSequence)' on a null object reference
from here
public void setTextView(String value){
tv.setText(value);
}
Looks like when you call
mainFragment.setTextView(value);
Your tv TextView is not exists yet.
For fixing it you have to use Bundle for passing String from Activity to fragment.
Also there are another variants like:
Storing values in Singleton
Storing values in Shared Preferences
Creating queue of values you should display with Observer pattern
Using ReplayRelay from RxRelay library as RxBus . Actually
almost the same as 3rd option
So there will be a bit less boilerplate than using Bundle

ListView of a Fragment (of a ViewPager inside a Fragment) that implements LoaderCallbacks<Cursor> is empty when I return back to it

The ListView inside of a Fragment that contains a ViewPager is empty when I return back to it. This ViewPager is inside of a Fragment because I'm using a Navigation Drawer Layout. I'll try to explain it in more details now:
I am using a Navigation Drawer layout and, consequently, using Fragments. In the main activity, in onCreate, I set the current Fragment with these lines of code:
#Override
protected void onCreate(Bundle savedInstanceState) {
/* More code */
currentFragment = new MainFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.layout_for_fragments, currentFragment).commit();
}
The MainFragment class contains a ViewPager that is initialized with these lines of code in the onCreateView():
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
/* More code */
ViewPager viewPager = (ViewPager) rootView.findViewById(R.id.main_view_pager);
viewPager.setAdapter(new SimpleFragmentPagerAdapter(getActivity(), getActivity().getSupportFragmentManager()));
/* Code for Tablayout */
return rootView;
}
One of the Fragments of the SimpleFragmentPagerAdapter class is this one that implements LoaderManager.LoaderCallbacks
public class ExpensesFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private ExpenseCursorAdapter mExpenseCursorAdapter;
private static final int EXPENSE_LOADER = 1;
public ExpensesFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_expenses, container, false);
ListView expensesListView = (ListView) rootView.findViewById(R.id.expenses_list_view);
mExpenseCursorAdapter = new ExpenseCursorAdapter(getContext(), null);
expensesListView.setAdapter(mExpenseCursorAdapter);
getLoaderManager().initLoader(EXPENSE_LOADER, null, this);
return rootView;
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(/* arguments */);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mExpenseCursorAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
mExpenseCursorAdapter.swapCursor(null);
}
}
On the first run, everything works fine. However, if I click on the navigation drawer menu, which I'm implementing this way, the ListView becomes empty:
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
int id = item.getItemId();
if (id == R.id.nav_main) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().remove(currentFragment).commit();
currentFragment = new MainFragment();
fragmentManager.beginTransaction().replace(R.id.layout_for_fragments, currentFragment).commit();
} else if (id == R.id.nav_blank) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().remove(currentFragment).commit();
currentFragment = new BlankFragment();
fragmentManager.beginTransaction().replace(R.id.layout_for_fragments, currentFragment).commit();
}
drawer.closeDrawer(GravityCompat.START);
return true;
}
The problem also exists when I rotate the device. There is a difference though. When I use the Navigation Drawer menu, none of the methods of the LoaderManager.LoaderCallbacks are called, but when I rotate the device, onLoadFinished is called.
Anyway, the problem persists. Any ideias how to solve it?
Thanks in advance.
I have found the solution!
Basically, I made two mistakes.
The first one is about the rotation of the device.
When the device is rotated, the Activity is destroyed and onCreate is called again. In this case, I should't call new MainFragment(). So, I check if the savedInstanceState is null before I call it, like this:
if (savedInstanceState == null) {
currentFragment = new MainFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().
replace(R.id.layout_for_fragments, currentFragment).commit();
}
The second one is about a fragment within a fragment. In this case, I can't simply call getSupportFragmentManager(). I should call getChildFragmentManager() instead and that's it!

Android Studio - Fragment button action

I tried to make an application that shows a fragment at start then you can change that fragment to an other one with a button in the first fragment. But when I put the button action in the fragments java, it won't start and I get the nullpointerexception error. Could you tell me why?
public class Fragment_main extends Fragment {
FragmentManager fm = getFragmentManager();
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Button button_inditas = (Button) getView().findViewById(R.id.button_inditas);
button_inditas.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fm.beginTransaction().replace(R.id.content_frame, new Fragment_1()).commit();
}
});
return inflater.inflate(R.layout.fragment_main,container,false);
}
}
But when I put the button action in the fragments java, it won't start and I get the nullpointerexception error. Could you tell me why?
Well I see some errors on your code...
First of all you can't call getView() if you haven't inflated one, it means that you MUST inflate a view as i'll put on my answer and then you can avoid the getView() and use that view itself.
And instead of returning the inflater you have to return your View
This is how your Fragment should look like:
public class Fragment_main extends Fragment {
public Fragment_main() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main,container,false);
Button button_inditas = (Button)rootView.findViewById(R.id.button_inditas);
button_inditas.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fragmentManager = getFragmentManager ();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction ();
fragmentTransaction.add (R.id.content_frame, new Fragment_1());
fragmentTransaction.commit ();
});
return rootView;
}
}
You have to inflate the view first and use the view returned by that instead of getView ().
View view = inflater.inflate (...);
and then
... = (Button) view.findView(...);
This happens because the view that is returned by getView () hasn't been created yet, so getView() returns null.

Attempt to invoke virtual method on a null object reference

This is the error i get:
03-11 08:27:48.513: E/AndroidRuntime(23647): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.plan.yeahimin/com.plan.yeahimin.PlanDetailsActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.io.Serializable android.os.Bundle.getSerializable(java.lang.String)' on a null object reference
I understand its due to a variable having a null value but I can't workout why. It looks like it's 'EXTRA_NEW_PLAN' in the getSerializable() method in the DetailsFragment but other than that I don't know. I'm new to Android so forgive me if it's obvious but any help would be greatly appreciated.
Here is my code for the ListFragment;
public class PlanListFragment extends ListFragment {
public final static String TAG = "com.plan.yeahimin.PlanListFragment";
public final static String EXTRA_NEW_PLAN = "com.plan.yeahimin.plan_id";
private Button mAddPlan;
private ArrayList<Plan> mPlansList;
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){
View v = inflater.inflate(R.layout.empty_view_or_list_view, parent, false);
ListView view = (ListView) v.findViewById(android.R.id.list);
view.setEmptyView(v.findViewById(android.R.id.empty));
mAddPlan = (Button) v.findViewById(R.id.add_a_plan);
mAddPlan.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
Log.d(TAG, "add plan clicked");
Plan plan = new Plan();
Log.d(TAG, "new plan created");
PlanArrayList.get(getActivity()).addPlans(plan);
Log.d(TAG, "plan added to mPlansList");
Intent i = new Intent(getActivity(), PlanDetailsActivity.class);
i.putExtra(PlanDetailsFragment.EXTRA_NEW_PLAN, plan.getId());
startActivity(i);
return;
}
});
return v;
}
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mPlansList = PlanArrayList.get(getActivity()).getPlans();
//ArrayList<Plan> mPlansList = new ArrayList<Plan>();
PlanArrayAdapter paa = new PlanArrayAdapter(mPlansList);
setListAdapter(paa);
}
public class PlanArrayAdapter extends ArrayAdapter<Plan>{
public PlanArrayAdapter(ArrayList<Plan> planList){
super(getActivity(), 0, planList);
}
public View getView(int position, View convertView, ViewGroup parent){
// Get the plan item for this position
Plan plan = getItem(position);
//If layout doesnt exist, inflate one
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.plan_list_fragment, parent, false);
}
TextView planTitle = (TextView) convertView.findViewById(R.id.plan_title);
planTitle.setText(plan.getTitle());
TextView planDate = (TextView) convertView.findViewById(R.id.plan_date);
planDate.setText(plan.getDate().toString());
return convertView;
}
}
}
and here is my code for the DetailsFragment which opens from add button;
public class PlanDetailsFragment extends Fragment {
private static final String TAG = "com.plan.yeahimin.PlanDetailsFragment";
public static final String EXTRA_NEW_PLAN = "com.plan.yeahimin.plan_id";
private EditText mTitleField;
private Button mDateButton;
private Button mTimeButton;
private EditText mLocationField;
private EditText mAttendeesField;
private EditText mDescriptionField;
private Plan mPlan;
private ArrayList<Plan> mPlansList;
public static PlanDetailsFragment newInstance(UUID planId){
Bundle args = new Bundle();
args.putSerializable(EXTRA_NEW_PLAN, planId);
PlanDetailsFragment f = new PlanDetailsFragment();
f.setArguments(args);
Log.d(TAG, "newInstance created");
return f;
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
UUID planId = (UUID)getArguments().getSerializable(EXTRA_NEW_PLAN);
mPlan = PlanArrayList.get(getActivity()).getPlan(planId);
}
#Override
public View onCreateView (LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){
View v = inflater.inflate(R.layout.plan_details_fragment, parent, false);
mTitleField = (EditText)v.findViewById(R.id.plan_title);
mLocationField = (EditText)v.findViewById(R.id.plan_location);
mAttendeesField = (EditText)v.findViewById(R.id.plan_attendees);
mDescriptionField = (EditText)v.findViewById(R.id.plan_description);
mDateButton = (Button)v.findViewById(R.id.plan_date);
mTimeButton = (Button)v.findViewById(R.id.plan_time);
return v;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
inflater.inflate(R.menu.main_to_do, menu);
}
public boolean onOptionsItemSelected(MenuItem item){
switch(item.getItemId()){
case R.id.save_button:
Log.d(TAG, "save button pressed");
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
I think you cannot send any arguments to your Fragment with .newInstance(), because this method does not accept any parameters according to the documentation. So even if you have overloaded .newInstance(UUID), the system calls .newInstance() (if calls at all, I have some doubts). Also please be aware that you put the parameter to Intent with .putExtra(), but do not recall it from the Intent.
In fact the right way to send arguments to a Fragment is as follows:
In the caller (usually it is an Activity, but maybe with another Fragment, like in your example, it would also work, I cannot say for sure):
PlanDetailsFragment fragment = new PlanDetailsFragment();
Bundle args = new Bundle();
args.putSerializable(PlanDetailsFragment.TAG_NEW_PLAN, plan.getID());
fragment.setArguments(args);
getFragmentManager().beginTransaction().add(fragment, TAG_DETAILS_FRAGMENT).commit();
In the fragment:
Bundle args = getArguments();
UUID planID = (UUID) args.getSerializable(TAG_NEW_PLAN);
It is not a ready-to-use code, you should adapt it to your classes and variables names, to where your tags are places, etc. The calls of Activity methods may also require some change if you prefer to work from another fragment. It is just an overall description.
My answer applies to the situation when both fragments are inside one activity. Your using of Intents make me have doubts in this, but I do not fully understand it.
You sent the arguments to an Activity(PlanDetailsActivity).
You should send the arguments to a Fragment through newInstance() method.
In your PlainDetailsActivity, you should create the fragment instance like:
UUID uuid = getIntent().getSerializableExtra(PlanDetailsFragment.EXTRA_NEW_PLAN);
PlanDetailsFragment f = PlanDetailsFragment.newInstance(uuid);

Android fragment onCreateView() never called

I've been searching for a while now, but I can't seem to find an answer to the following question:
Why does onCreateView never get triggered? This code is the code generated by Android Studio when creating a new MainActivity plus some of the code I wrote.
I want to use setTextConfig(...,...,...) to set the text in the text fields. But I can't seem to get them using findviewbyid(R.id....); because v is always null, even getView(); returns null.
public static class PlaceholderFragment extends Fragment {
private View v;
private EditText etCustomerName, etDeviceID;
private Spinner select;
public PlaceholderFragment() {
super();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v = inflater.inflate(R.layout.fragment_main, container, false);
return v;
}
public void setTextConfig(String customerName, String selectedOption, int deviceID){
View vv = getView();
//init form controls
etCustomerName = (EditText)vv.findViewById(R.id.etCustomerName);
etDeviceID = (EditText)v.findViewById(R.id.etDeviceId);
select = (Spinner)v.findViewById(R.id.select);
etCustomerName.setText(customerName);
ArrayAdapter adapter = (ArrayAdapter)select.getAdapter();
int index = adapter.getPosition(selectedOption);
select.setSelection(index);
etDeviceID = (EditText)getView().findViewById(R.id.etDeviceId);
etDeviceID.setText(Integer.toString(deviceID), TextView.BufferType.EDITABLE);
}
}
If you need more detailed info, just ask :)

Categories