Attempt to invoke virtual method on a null object reference - java

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

Related

Using Bundle to send data from activity to fragment

I am trying to use Bundle to send data from an activity to a fragment. The activity is receiving the input from a dialogbox when the user clicks on the actionbar add icon. The button also opens the dialogbox but it sends the data straight to the fragment (I'm trying to learn the difference between activity and fragment and to interact with the dialogfragment). None of the solutions on the internet have worked for me, and I was hoping someone can help
I have provided a visualization to aid in my explanation of the issue. So initially, I click the action add icon that opens the dialogbox (2nd pic), when I enter an input, it doesn't alter the data on the fragment. Only when I press the action add icon for a second time, does the first input get updated (3rd pic). Also you may notice that it says "Bundle{[Dialog Input = First Input]}" where First Input is the user input. How do I change this to just, First Input. I tried clearing the textview before setting the value, but that doesn't work. Now lastly when I press the button, it opens the dialogbox and when I enter in data, the data from the action add icon (handled in activity then data sent to fragment) overlaps with the data from the button (data sent straight to fragment). Any help would be appreciated. Thanks in advance.
MainActivity:
public class MainActivity extends AppCompatActivity implements
MyCustomDialog.OnInputSelected{
public String dialogInput;
FragmentManager fragmentManager;
#Override
public void sendInput(String input) {
dialogInput = input;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
//Inflate the menu, this adds items to the action bar if it is present
getMenuInflater().inflate(R.menu.menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
//Handle action bar clicks here. The action bar will automatically handle clicks on the home/up button
//so long as you specify a parent activity in AndroidManifest.xml
switch(item.getItemId()){
case R.id.action_add:
MyCustomDialog dialog = new MyCustomDialog();
dialog.show(getSupportFragmentManager(), "MyCustomDialog");
//Trying Bundle to pass data, dialog input between activity and fragment
Bundle bundle = new Bundle();
bundle.putString("Dialog Input", dialogInput);
//Set Fragment class arguments
MainFragment fragment = new MainFragment();
fragment.setArguments(bundle); //set argument bundle to fragment
fragmentManager.beginTransaction().replace(R.id.MainFragment,fragment).commit(); //now replace Mainfragment
Toast.makeText(this, "Action_Add Clicked Successfully", Toast.LENGTH_SHORT).show();
}
return super.onOptionsItemSelected(item);
}
}
MainFragment:
public class MainFragment extends Fragment implements MyCustomDialog.OnInputSelected{
TextView InputDisplay;
Button OpenDialog;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
InputDisplay = view.findViewById(R.id.InputDisplay);
OpenDialog = view.findViewById(R.id.Open_Dialog);
//Getting Main Activity dialog information with Bundle, that was received from toolbar add
Bundle bundle = getArguments();
if(bundle != null){
String dialogInput = bundle.toString();
//Clearing since Fragment call and activity call overlap each other.
InputDisplay.setText("");
InputDisplay.clearComposingText();
InputDisplay.setText(dialogInput);
}
//String dialogInput = this.getArguments().getString("Dialog Input");
OpenDialog.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.d("MainFragment", "onClick: opening dialog");
MyCustomDialog customDialog = new MyCustomDialog();
customDialog.setTargetFragment(MainFragment.this, 1);
customDialog.show(getFragmentManager(), "MyCustomDialog");
}
});
return view;
}
#Override
public void sendInput(String input) {
InputDisplay.setText("");
InputDisplay.setText(input);
}
}
My Custom Dialog:
public class MyCustomDialog extends DialogFragment {
private EditText Input;
private TextView ActionOK, ActionCANCEL;
private OnInputSelected onInputSelected;
public interface OnInputSelected{
void sendInput(String input);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try{
Fragment onInputSelected_fragment = getTargetFragment();
Activity onInputSelected_activity = getActivity();
if(onInputSelected_fragment != null){
onInputSelected = (OnInputSelected) onInputSelected_fragment;
}else{
onInputSelected = (OnInputSelected) onInputSelected_activity;
}
//throw new RuntimeException("Custom Dialog onAttach Listener was NULL");
}catch(ClassCastException e){
Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
}
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_my_custom, container, false);
Input = view.findViewById(R.id.Input);
ActionOK = view.findViewById(R.id.Action_OK);
ActionCANCEL = view.findViewById(R.id.Action_CANCEL);
ActionCANCEL.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getDialog().dismiss();
}
});
ActionOK.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onInputSelected.sendInput(Input.getText().toString());
getDialog().dismiss();
}
});
return view;
}
}
How do I change this to just, First Input.
your output is printed like this "Bundle{[Dialog Input = First Input]}" because you are directly doing bundle.toString(); instead of getting the value you have stored in the bundle.
change the above to this
String dialogInput = bundle.getString("Dialog Input")
InputDisplay.setText(dialogInput);
the data from the action add icon overlaps with the data from the button
Clear the existing text in the text view before setting the new value like this
String dialogInput = bundle.getString("Dialog Input")
InputDisplay.setText(");
InputDisplay.setText(dialogInput);
Also, I noticed that all the variable names that you have used are not following camel case I suggest you correct that as well.

'startActivity(android.content.Intent, android.os.Bundle)' in 'android.support.v4.app.Fragment' cannot be applied to '(android.content.Intent, int)'

I'm developing my final year project about trip planner, and my app has fragments that contains other features. In FragmentPhoto, which I created to upload photo into the app has error regarding the startActivity(). The error said 'startActivity(android.content.Intent, android.os.Bundle)' in 'android.support.v4.app.Fragment' cannot be applied to '(android.content.Intent, int)'. Here is my code for the fragment.
public class FragmentPhoto extends Fragment {
private Button mSelectImage;
private StorageReference mStorage;
private static final int GALLERY_INTENT = 2;
public FragmentPhoto() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mStorage = FirebaseStorage.getInstance().getReference();
mSelectImage = (Button) getView().findViewById(R.id.selectImage);
mSelectImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivity(intent,GALLERY_INTENT);
}
});
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.activity_fragment_photo, container, false);
}
}
The error kinda related to the private static final int GALLERY_INTENT = 2, since the extends is not AppCompatActivity. Is there any way to resolve this? Thank you in advance.
ACTION_PICK send the request to installed apps(registered with this action to supply images) to allow user to pick images so to identify the request uniquely, a unique int parameter is passed which will be returned in onActivityResult so you have to use startActivityForResult
so use startActivityForResult(intent,GALLERY_INTENT);

Click button in one tab and change textview in the anoter tab

I have tab1 and tab3 also these have their classes and I want to click button in tab1 and change textview in the tab3, but I couldn't find anyway.
This is my tab1 class
public class tab1Contacts extends Fragment{
TextView tv;
EditText et;
TextView tv3;
personInfo pı;
public personInfo returnpı(){
return pı;
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.tab1contents, container, false);
Button btn_jog = (Button) rootView.findViewById(R.id.jogging_button);
tv = (TextView) rootView.findViewById(R.id.newRecordText);
et = (EditText) rootView.findViewById(R.id.durationtext) ;
pı = new personInfo();
pı.eyesPower = 100;
pı.brainPower = 100;
pı.armsPower = 100;
pı.legsPower = 100;
pı.hearthPower = 100;
pı.energyLevel = 100;
pı.calorie = 2000;
pı.condition = 0;
btn_jog.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
int duration = Integer.parseInt(et.getText().toString());
pı.jogging(duration);
//I want to change here textview in the tab3.
}
});
return rootView;
}
}
This also my tab3 Class:
public class Tab3Contacts extends Fragment {
TextView tv3;
double newBrainpower;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.tab3contents, container, false);
tv3 = (TextView) rootView.findViewById(R.id.list_text) ;
return rootView;
}
}
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//pager.setCurrentItem(yourindex);// if you use pager
getTabHost().setCurrentTab(yourindex);
}
});
If I'm reading your question correctly, then what you need is for tab3 to listen to events from tab1. For that you will want to implement some kind of internal notification/eventing system. This is typically handled through a notification handling class that will register observers/listeners.
An example from a project I've been maintaining:
public class NotificationManager {
public interface Observer {
public void update(String notificationName, Bundle data);
}
private static NotificationManager singletonNotifier = null;
private HashMap<String, ArrayList<Observer>> mObservables = null;
private NotificationManager() {
mObservables = new HashMap<String, ArrayList<Observer>>();
}
//enforce singleton
public static NotificationManager getInstance() {
if (singletonNotifier == null) {
singletonNotifier = new NotificationManager();
}
return singletonNotifier;
}
public void addObserver(String notificationName, Observer observer) {
// add to map
// in multi-threaded apps make sure you use synchronized or a mutex
}
public void removeObserver(String notificationName, Observer observer) {
// remove from map; mind threading
// overload as necessary for your design
}
public void notifyObservers(String notificationName, Bundle data) {
// go through your map of observers, build an array of observers
// that need to update, then for each observer, call
// observer.update(notificationName, data);
}
}
Then your tab3 class would need to implement the Observer interface and on object construction register itself with the NotificationManager with a string value for the type of notification it wants (use best practices for constants instead of string literal arguments), using the call
NotificationManager.getInstance().addObserver("Tab1DataChange", this);
It will need to implement the update(String, Bundle) method, which will make all the changes that you need.
Then in the class for the tab1 object, add to the click listener this call:
NotificationManager.getInstance().notifyObservers("Tab1DataChange", data);
Where data is any information that observers would need to know to respond. In keeping with the idea of decoupling code, do not put together a data bundle that is explicitly for one listener, because at some point you might need something else to listen for the same event. Save yourself some grief now by designing the data bundle to contain what would need to update regardless of who is consuming the event.
Some lessons learned for me:
Pay attention to Android lifecycle. OnPause and OnDestroy for the active view(s) should unregister the listener so that you don't end up with a null pointer exception if something triggers that event while the observer object is not available. OnCreate and OnResume should reregister. In some cases I have been able to not worry about OnPause/OnResume, but depending on your app you may need them.

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 :)

Extended Fragment Stil Fragment

I am trying to use the method
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a DummySectionFragment (defined as a static inner class
// below) with the page number as its lone argument.
Fragment fragment = new MappingPage();
Bundle args = new Bundle();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
fragment.setArguments(args);
return fragment;
}
I have written a class called MappingPage(), if I try to use the above method, the fragment I create is of type MappingPage because has been extended and hence throw an error as the function returns a fragment (not a MappingPage Object)
EDIT:I am getting an error when I do
Fragment fragment = new MappingPage();
Eclipse tell me that I need to change fragment to type MappingPage, which means i have to change the return type of the function
two Questions
1) How are you supposed to put your custom fragments into this?
2) Why does the dummySectionFrament return a Fragment Object and not a DummySectionFragment object?
thanks in advance
public static class DummySectionFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
public static final String ARG_SECTION_NUMBER = "section_number";
public DummySectionFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main_dummy,
container, false);
TextView dummyTextView = (TextView) rootView
.findViewById(R.id.section_label);
dummyTextView.setText(Integer.toString(getArguments().getInt(
ARG_SECTION_NUMBER)));
return rootView;
}
}
here is my class
public class MappingPage extends Fragment
{
private MapView map;
private MapController myMapController;
public static final String ARG_SECTION_NUMBER = "1";
public MappingPage() {
}
public View onCreateView (LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Context context = new ContextThemeWrapper(getActivity(), R.style.fragment_theme);
//LayoutInflater Inflater = inflater.cloneInContext(context);
View view=inflater.inflate(R.layout.fragment_main_dummy, container, false);
return view;
}
#Override
public void onResume() {
map = (MapView)getActivity().findViewById(R.id.openmapview);
map.setBuiltInZoomControls(true);
myMapController = map.getController();
myMapController.setZoom(15);
super.onResume();
}
}
Regarding your first question, this should work. MappingPage is an indirect instance of the Fragment class, and you can therefore return it.
About question two, the method that you call always returns a specific type, like Fragment in the case of getItem(). However, this can be a subclass of that as well, and if you are sure it is, you can safely cast it to that subclass.

Categories