Reputation: 163
I am new to android studio and I am trying implement a dialogfragment to pop up whenever a button is clicked or a toolbar action_add button is clicked. It sends data from the dialog box to a fragment or an activity (trying to understand how it works for both). My dialog class has an interface, which worked great when I implemented it in the fragment, but the activity is a bit different. I am using the interface to pass the data to the activity, then I am using Bundle to transfer the data from the activity to the fragment. I believe the error occurs in the onAttach since I have getTargetFragment();
Is it possible to have multiple activities/fragments implement a single interface? If so, how do I cater to both the activity and fragment in the interface, onAttach, and the sending of data?
Thank you guys in advance, below is my code for the custom_dialog class for the dialogbox, and a fragment which is attached to an activity. The goal is either press the button in the fragment or the toolbar on the activity to open a dialog and get an input from the user, that will be transferred to be displayed.
Error:
Process: com.example.andrewg.dialogfragment, PID: 13335
java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.andrewg.dialogfragment.MyCustomDialog$OnInputSelected.sendInput(java.lang.String)' on a null object reference
at com.example.andrewg.dialogfragment.MyCustomDialog$2.onClick(MyCustomDialog.java:58)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25881)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6649)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:826)
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);
//Redundant
MenuItem actionMenuItem = menu.findItem(R.id.action_add);
actionMenuItem.setOnMenuItemClickListener(new
MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
return false;
}
});
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 to pass dialog input into an intent to send to the
fragment
/*Intent intent = new Intent(getApplicationContext(),
MainFragment.class);
intent.putExtra("Dialog Input", dialogInput);
startActivity(intent);*/
//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();
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(input);
}
}
Custom Dialog: I added a second interface variable for activity for onAttach to use getActivity(), but it doesn't seem right.
public class MyCustomDialog extends DialogFragment {
private EditText Input;
private TextView ActionOK, ActionCANCEL;
public OnInputSelected onInputSelected_Fragment;
public OnInputSelected onInputSelected_Activity;
public interface OnInputSelected{
void sendInput(String input);
}
@Override
public void onAttach(Context context) {
try{
onInputSelected_Fragment = (OnInputSelected) getTargetFragment();
onInputSelected_Activity = (OnInputSelected) getActivity();
}catch(ClassCastException e){
Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
}
super.onAttach(context);
}
@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_Fragment.sendInput(Input.getText().toString());
onInputSelected_Activity.sendInput(Input.getText().toString());
getDialog().dismiss();
}
});
return view;
}
}
Upvotes: 1
Views: 1405
Reputation: 13019
Is it possible to have multiple activities/fragments implement a single interface?
Yes it is. You just have to be aware that getActivity()
will return null
if the DialogFragment
is not attached to an Activity
but to another Fragment
whereas getTargetFragment()
will return null
if there is no target Fragment
, e.g. when you show the dialog directly from your Activity
and so did not call setTargetFragment()
.
Since casting null
to anything does not result in an Exception
, all you have to do in your code is a null
check before actually calling an interface method.
But because you will not have a target Fragment
and an Activity
showing the same instance of your DialogFragment
, you can change your code and do with just one field
private OnInputSelected onInputSelected;
Then in onAttach()
you check which one is null - Activity
or target Fragment
- and set onInputSelected
correctly once and for all.
try{
Fragment onInputSelected_Fragment = getTargetFragment();
Activity onInputSelected_Activity = getActivity();
if (onInputSelected_Fragment != null){
onInputSelected = (OnInputSelected)onInputSelected_Fragment;
}
else {
onInputSelected = (OnInputSelected)onInputSelected_Activity;
}
// throw RuntimeException here if onInputSelected still is null
//
}catch(ClassCastException e){
Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
}
Note that onInputSelected
might be null if you forgot to set a target Fragment
when showing the dialog from a Fragment. This would be a programming error, so maybe you want to throw a RuntimeException
in this case. If your app makes it past this point, you have something implementing the interface, mission accomplished.
In your onClick()
you can simply write
onInputSelected.sendInput(Input.getText().toString());
Upvotes: 1
Reputation: 721
getActivity() is returning null. According to the answer here the getActivity() will return null until after onAttach is run. If you move super.onAttach() to the beginning of your onAttach method then it should return the activity correctly.
@Override
public void onAttach(Context context) {
super.onAttach(context);
try{
onInputSelected_Fragment = (OnInputSelected) getTargetFragment();
onInputSelected_Activity = (OnInputSelected) getActivity();
}catch(ClassCastException e){
Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
}
}
Upvotes: 0