So basically, I have a dialog where the user inputs data. This dialog is launched from a recyclerView with a custom adapter. I need to call notifyDataSetChanged on the fragment from the dialog after the user clicks "finish".
How do I do this?
Code:
public class FoodListAdapter extends RecyclerView.Adapter<FoodListAdapter.ViewHolder>
implements View.OnClickListener {
//blabla
#Override
public void onClick(View v) {
int position = (int) v.getTag();
String foodItemTitle = mFoodItemList.get(position).getTitle();
FoodEditDietDialog dialog = new FoodEditDietDialog();
dialog.show(fm, mFoodItemList.get(position).getTimeStamp());
}
public class Fragment extends Fragment {
//blabla
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View fragment = inflater.inflate(R.layout.fragment, container, false);
foodList = databaseHelper.getList("somethingsomething");
recyclerView = Fragment.findViewById(R.id.list);
recyclerView.setHasFixedSize(false);
layoutManager = new LinearLayoutManager(this.getContext());
recyclerView.setLayoutManager(layoutManager);
adapter = new FoodListAdapter(fm, foodList);
recyclerView.setAdapter(adapter);
}
public class FoodEditDietDialog extends DialogFragment {
//blabla
button = dialog.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
databaseHelper.doSomething();
dismissDialog();
//TODO: call method in dietFragment to notify adapters
}
});
}
You need to do this through your hosting Activity.
public interface DataChangeListener {
void onDataChange();
}
public class YourActivity extends Activity implements DataChangeListener {
private List<DataChangeListener> dataChangeListeners = new ArrayList<>();
#Override
public void onDataChange() {
for(DataChangeListener listener in dataChangeListeners) {
listener.onDataChange();
}
}
public void addDataChangeListener(DataChangeListener listener) {
dataChangeListeners.add(listener);
}
public void removeDataChangeListener(DataChangeListener listener) {
dataChangeListeners.remove(listener);
}
}
public class Fragment extends Fragment implements DataChangeListener {
#Override
public void onAttach(Activity activity) {
if (activity instanceOf YourActivity) {
activity.addDataChangeListener(this)
}
}
#Override
public void onDetach() {
Activity activity = getActivity();
if (activity instanceOf YourActivity) {
activity.removeDataChangeListener(this)
}
}
#Override
public void onDataChange() {
adapter.notifyDataSetChanged();
}
}
public class FoodEditDietDialog extends DialogFragment {
//blabla
button = dialog.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
databaseHelper.doSomething();
dismissDialog();
//TODO: call method in dietFragment to notify adapters
Activity activity = getActivity();
if (activity instanceOf DataChangeListener) {
activity.onDataChange();
}
}
});
}
So basically, dialog will call activity, activity will notify fragment, fragment will notify adapters. Alternatively, you can pass your fragment (as DataChangeListener) to your adapter, and to your dialog. And call it from there.
Related
I was able to create a single event callback from a fragment to the activity. However, I have problem with creating two event callbacks from the same fragment.
Basically, I have a button on the fragment1 layout, when click on it, it will execute something in mainactivity, and then, it changes hide the Button at fragment1 layout, and then I need to send a Boolean from the fragment to activity, then initiate fragment2.
How can I implement the 2nd callback interface in the mainacativity.
Here is my code:
at Fragment1:
public class Fragment1 extends Fragment{
private static final String TAG = "Fragment1";
Boolean in1, in2;
Button btn1;
ListenerA mListenerA;
public interface ListenerA{
public void methodA(Boolean in1);
};
ListenerB mListenerB;
public interface ListenerB{
public void methodB(Boolean in2);
};
#Override
public void onAttach(Context context) {
super.onAttach(context);
if(context instanceof ListenerA)
mListenerA = (ListenerA)context;
if(context instanceof ListenerB)
mListenerB = (ListenerB)context;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View myView = inflater.inflate(R.layout.fragment1, container, false);
btn1 = (Button) myView.findViewById(R.id.btn1_ID);
btn1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mListenerA.methodA(true);
}
});
return myView;
}
public void showBtnMethod (Boolean showBtn){
if (showBtn==false) {
btn1.setVisibility(View.INVISIBLE);
mListenerB.methodB(true); //***** problematic line
}
}
}
and the code in the main activity is below. please note that on the first line I was able only to implement one method from the fragment.
public class MainActivity extends AppCompatActivity implements Fragment1.ListenerA {
private static final String TAG = "MainActivity";
public Fragment1 mFragment1;
public Fragment2 mFragment2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
FragmentTransaction transaction1 = getSupportFragmentManager().beginTransaction();
mFragment1 = new Fragment1();
transaction1.replace(R.id.content_fragment_ID, mFragment1);
transaction1.commit();
}
}
// this gets called by ListenerA when click on the button
public void methodA(Boolean in){
if (in==true){
Toast.makeText(MainActivity.this, "mMethodA Executed", Toast.LENGTH_SHORT).show();
triggerShowMethod();
}
};
private void triggerShowMethod(){
mFragment1.showBtnMethod(false);
};
// Problematic section is below
public void methodB(Boolean in){
if (in==true){
FragmentTransaction transaction2 = getSupportFragmentManager().beginTransaction();
mFragment2 = new Fragment2();
transaction2.replace(R.id.content_fragment_ID, mFragment2);
transaction2.commit();
Toast.makeText(MainActivity.this, "mMethodB Executed", Toast.LENGTH_LONG).show();
}
};
}
You can simply consolidate both interfaces into one:
public interface Listener{
public void methodA(Boolean in1);
public void methodB(Boolean in2);
};
and in your MainActivity:
public class MainActivity extends AppCompatActivity implements Fragment1.Listener
Your activity doesn't implement ListenerB.
public class MainActivity extends AppCompatActivity implements Fragment1.ListenerA, Fragment1.ListenerB {
}
I am calling a Fragment Dialogue from a Fragment, and when I try to impliment a callback it isn't calling back to the Fragment, I think it is calling back to the Activity. I believe this is by design from Google, is there anyway around that? How can couple the Dialogue Fragment to the Fragment?
My code is as follows;
Dialogue Fragment
public class CustomDatePicker extends DialogFragment {
private DateListener listener;
...
public interface DateListener {
void onDateComplete();
}
}
Fragment
public class User extends Fragment implements CustomDatePicker.DateListener {
...
#Override
public void onDateComplete() {
Log.i("TEST", "onDateComplete: " + date);
}
}
For those purposes you can use setTargetFragment()and onActivityResult().
Check this: Receive result from DialogFragment or Callback to a Fragment from a DialogFragment. In this case you dont need any interfaces.
Interfaces:
public class CustomDatePicker extends DialogFragment {
private DateListener listener;
public interface DateListener {
void onDateComplete(long date);
}
public void setListener(DateListener listener) {
this.listener = listener;
}
public void sendToFragment(long value) { //value you want to pass
if (listener != null) {
listener.onDateComplete(value);
}
}
}
and
public class User extends Fragment implements CustomDatePicker.DateListener {
public void showDialog(){
CustomDatePicker datePicker = new CustomDatePicker();
datePicker.setListener(this); // since User fragment implements it
...
datePicker.show(getFragmentManager(), "TAG");
}
#Override
public void onDateComplete(long date) {
Log.d("Interface", "onDateComplete: " + date);
}
}
Call sendToFragment() with your value where you need, hope this help.
instead of implementing your listener to your fragment get a new instance of your dialog fragment and new your listener when you want to call it
add newInstance to your dialog fragment like this:
public static CustomDatePicker newInstance(DateListener listener) {
return new CustomDatePicker();
}
and show your dialog fragment in this way:
CustomDatePicker.newInstance(new CustomDatePicker.DateListener() {
#Override
public void onDialogPositiveListener(DialogFragment dialog) {
}).show(getFragmentManager(), "tag");
so you can get result in your code
What I ended up doing, based off of comments and answers was the following.
Dialogue Fragment;
public class CustomDatePicker extends DialogFragment {
private DateListener listener;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
...
Button btnSave = (Button) v.findViewById(R.id.save);
btnSave.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getDate();
dismiss();
listener.onDateComplete();
}
});
...
}
...
public interface DateListener {
void onDateComplete();
}
}
Fragment;
public class User extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
CustomDatePicker customDatePicker = new CustomDatePicker();
customDatePicker.setListener(new CustomDatePicker.DateListener() {
#Override
public void onDateComplete() {
adapter.notifyDataSetChanged();
}
});
...
}
...
}
I'm creating an app, and I have a doubt on how to communicate between fragments, I know I must communicate to the parent activity and etcetera my question is more best practice oriented. My app consists of a MainActivity with a navigation drawer which depending on the selection calls a fragment and puts it on the main screen.
I have 2 fragments which via a button need to call another fragment (I can convert it to an activity with no problem) that opens the camera to scan a barcode (BarScanFragment) (https://github.com/dm77/barcodescanner).
My question is it possible to know which fragment called the BarScanFragment so I can send the argument to the correct fragment, and how do I achieve it.
BarScanFragment.java
public class BarScanFragment extends Fragment implements ZXingScannerView.ResultHandler{
private ZXingScannerView mScannerView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mScannerView = new ZXingScannerView(getActivity());
return mScannerView;
}
#Override
public void onResume() {
super.onResume();
mScannerView.setResultHandler(this);
mScannerView.startCamera();
}
#Override
public void onPause() {
super.onPause();
mScannerView.stopCamera();
}
#Override
public void handleResult(Result result) {
Log.i("TAG", result.getText());
Bundle args = new Bundle();
args.putString("barcodeScan", result.getText());
}
FragmentA.java
......
......
barcodeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Fragment content = new BarScanFragment();
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.flFragmentContainer, content).addToBackStack("TRADEIN")
.commit();
/*Intent intent = new Intent(rootView.getContext(), BarcodeScannerActivity.class);
startActivity(intent);*/
}
});
Fragment b.java
.....
.....
barcodeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Fragment content = new BarScanFragment();
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.flFragmentContainer, content).addToBackStack("INFOPRODUCT")
.commit();
/*Intent intent = new Intent(rootView.getContext(), BarcodeScannerActivity.class);
startActivity(intent);*/
}
});
Take a look on setTargetFragment and getTargetFragment. It's the easiest way to communicate back and forth between fragment.
Here there is a little example https://github.com/alexfu/TargetFragmentExample
You can add a parameter to the BarScanFragment's constructor:
public BarScanFragment(int creatorFragment) {
// Do something with creatorFragment
}
Save "creatorFragment" values in your activity:
public static final int FRAGMENT_1 = 0;
public static final int FRAGMENT_2 = 1;
Then in your button listeners create the BarScanFragment with a specific value:
Fragment content = new BarScanFragment(MainActivity.FRAGMENT_1);
or
Fragment content = new BarScanFragment(MainActivity.FRAGMENT_2);
Declare callbacks in your activity and call methods from another fragment using these callbacks
public interface Callback1 {
void callbackMethod1();
}
public interface Callback2 {
void callbackMethod2();
}
public class Activity extends ActionBarActivity implements Callback1, Callback2 {
Fragment1 mFragment1;
Fragment2 mFragment2;
#Override
public void callbackMethod1() {
mFragment2.method2();
}
#Override
public void callbackMethod2() {
mFragment1.method1();
}
}
public class Fragment1 extends Fragment {
Callback1 callback;
#Override
public void onAttach(android.app.Activity activity) {
super.onAttach(activity);
callback = (Callback1) getActivity();
}
void callMethodOfFragment1() {
callback.callbackMethod1();
}
#Override
public void onDetach() {
super.onDetach();
callback = null;
}
public void method1() {
do_sth_in_fr2();
}
}
public class Fragment2 extends Fragment {
Callback2 callback;
#Override
public void onAttach(android.app.Activity activity) {
super.onAttach(activity);
callback = (Callback2) getActivity();
}
void callMethodOfFragment2() {
callback.callbackMethod2();
}
#Override
public void onDetach() {
super.onDetach();
callback = null;
}
public void method2() {
do_sth_in_fr1();
}
}
I'm trying return a String from the DialogFragment, following this instructions https://stackoverflow.com/a/14808425/2933117 , but I only get logcat error
java.lang.ClassCastException: com.example.testinterface.MainActivity cannot be cast to com.example.testinterface.InterDialog$EditNameDialogListener
I appreciate any help or explanation of why I get an error, thanks in advance.
Here is my code:
MainActivity
public class MainActivity extends FragmentActivity {
private Button Btn;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Btn=(Button)findViewById(R.id.button1);
Btn.setOnClickListener(new View.OnClickListener(){
public void onClick (View view) {
FragmentManager fm = getSupportFragmentManager();
InterDialog pruebafragment = new InterDialog();
pruebafragment.show(fm,"MyFragment");
}
});
}
public void onFinishEditDialog (String inputText) {
Toast.makeText(this, "Hi, " + inputText, Toast.LENGTH_SHORT).show();
}
}
and DialogFragment
public class InterDialog extends DialogFragment {
public interface EditNameDialogListener {
void onFinishEditDialog(String inputText);
}
public InterDialog() {
// Empty constructor required for DialogFragment
}
EditNameDialogListener activity;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.new_layout, container);
activity = (EditNameDialogListener) getActivity();
Button button = (Button)view.findViewById(R.id.Btnreturn);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
activity.onFinishEditDialog("errorrrr");
getDialog().dismiss();
}
});
return view;
}
}
The line:
activity = (EditNameDialogListener) getActivity();
is what is breaking it. Make sure your activity implements the EditNameDialogListener interface by doing
public class MainActivity extends FragmentActivity implements EditNameDialogListener {
Hope that helps
I am going to explain my problem as short as possible.
I have a Fragment called FragmentA which displays a DialogFragment after clicking on a specific button.
public class FragmentA extends Fragment implements OnClickListener {
...
#Override
public void OnClick(View v) {
if (v == dialogButton) {
showDialog();
}
}
public void showDialog() {
String diagName = getResources().getString(R.string.dialog_title);
MyDialog myDialog = MyDialog.newInstance(getFragmentAValue());
myDialog.show(getFragmentManager(), diagName);
}
}
public class MyDialog extends DialogFragment implements OnClickListener {
...
#Override
public void onClick(View view) {
if (view == acceptButton) {
...
}
else if (view == cancelButton) {
...
}
}
}
The dialog is dispalyed without any problem. But my problem consists in after myDialog is dismissed onResume() method in FragmentA is never called and FragmentA is shown and you can interact with it without any problem.
public class FragmentA extends Fragment implements OnClickListener {
...
#Override
public void onResume() {
super.onResume();
resumeFragmentA();
}
}
So, what I have done in order to fix this issue is copying an instance of FragmentA in the end of onActivityCreated() method and call the method resumeFragmentA() when user has closed the dialog.
public class FragmentA extends Fragment implements OnClickListener {
FragmentA fragmentA = null;
...
#Override
public void onResume() {
super.onResume();
resumeFragmentA();
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
...
fragmentA = this;
}
...
}
public class MyDialog extends DialogFragment implements OnClickListener {
...
#Override
public void onClick(View view) {
if (view == acceptButton) {
storeData();
dismissDialog();
}
else if (view == cancelButton) {
dismissDialog();
}
}
public void dismissDialog() {
FragmentA.fragmentA.resumeFragmentA();
dismiss();
}
}
I know this solution is tricky but I do not know to solve in a more brilliant way. Do you know it? Any Idea?
Thanks in advance!
For a better reading of my code, here you have my full code:
public class FragmentA extends Fragment implements OnClickListener {
...
FragmentA fragmentA = null;
...
#Override
public void OnClick(View v) {
if (v == dialogButton) {
showDialog();
}
}
public void showDialog() {
String diagName = getResources().getString(R.string.dialog_title);
MyDialog myDialog = MyDialog.newInstance(getFragmentAValue());
myDialog.show(getFragmentManager(), diagName);
}
#Override
public void onResume() {
super.onResume();
resumeFragmentA();
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
...
fragmentA = this;
}
...
}
public class MyDialog extends DialogFragment implements OnClickListener {
...
static MyDialog newInstance(int value) {
MyDialog fragment = new MyDialog ();
Bundle args = new Bundle();
args.putInt("value", value);
fragment.setArguments(args);
return fragment;
}
#Override
public void onClick(View view) {
if (view == acceptButton) {
storeData();
dismissDialog();
}
else if (view == cancelButton) {
dismissDialog();
}
}
public void dismissDialog() {
FragmentA.fragmentA.resumeFragmentA();
dismiss();
}
...
}
If you just want to call onResume of FragmentA, make a call to startActivityForResult inside your DialogFragment, and start your FragmentActivity. This will call onResume.
public void dismissDialog() {
getActivity().startActivityForResult(getActivity().getIntent(), 10);
dismiss()
}
Now Google has released Android Architecture Components library, the best solution is to use a shared ViewModel between both Fragments with your Activity as the one that implements LifecycleRegistryOwner.
Doing this, we will avoid refreshing UI calling onResume and we don't need to code any tricky solution.
A solution could be the following one:
public MyActivity extends AppCompatActivity {
...
}
public class FragmentA extends Fragment implements OnClickListener {
...
private SharedViewModel mSharedViewModel;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSharedViewModel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
mSharedViewModel.getData().observe(this, value -> {
updateUIValue(value);
});
}
#Override
public void OnClick(View v) {
if (v == dialogButton) {
showDialog();
}
}
#Override
public void OnClick(View v) {
if (v == dialogButton) {
showDialog();
}
}
public void showDialog() {
String diagName = getResources().getString(R.string.dialog_title);
MyDialog myDialog = MyDialog.newInstance(getFragmentAValue());
myDialog.show(getFragmentManager(), diagName);
}
}
public class MyDialog extends DialogFragment implements OnClickListener {
...
private SharedViewModel mSharedViewModel;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSharedViewModel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
mSharedViewModel.getData().observe(this, value -> {
updateUIValue(value);
});
}
#Override
public void onClick(View view) {
if (view == acceptButton) {
...
mSharedViewModel.setValue(newValue);
}
else if (view == cancelButton) {
dismiss();
}
}
}
public class SharedViewModel extends ViewModel {
final MutableLiveData<Resource<Value>> data = new MutableLiveData<>();
public MutableLiveData<Value> getData() {
return data;
}
public void setValue(Value newValue) {
this.data.setValue(newValue)
}
}
Another way is by implementing an interface. Create an interface and let the activity/fragment (which one you need) implements it, then pass itself as an interface object to your DialogFragment.
CustomDialogFragment df = new CustomDialogFragment();
Bundle b = new Bundle();
b.putSerializable(KEY, this); // current activity/fragment that implements the interface
df.setArguments(b);
df.show(getFragmentManager(), TAG);
Then in your DialogFragment get the listener, and call the listener's method when you need to (in this case after/before you called dismiss()).