I would like to use interface to implement the communicate of passing data from fragments to activity's button which contains onClick event. I can see Map can write the data which is valid on editText field, but those values cannot be sent to activity. it shows error and stopped once I trigger the onClick event on activity.
The procedures are:
Users filled in the editText field
Once the editText field is out-focused, the TextWatcher with validation will check (If invalid, editText will not put into HashMap and hint user to edit, if user click submit button, it will reheat user change the correct answer at first)
When user filled all fields and click button, values on fragments will return as Hashmap, and checking it has null fields or not, and explode the values and putExtra() to next activity.
I was confused on the usage of the interface although I have read many sources and cases during troubleshooting this issue. Or May I use another solution to implement this function?
Thanks for any assistance.
Main activity:
......
Fragment_step_1 getHashMapStep1 = new Fragment_step_1();
Fragment_step_2 getHashMapStep2 = new Fragment_step_2();
public interface onPassValue{
public Map<Object, String> onPassValueStep1(Map<Object, String> insureApplicant);
}
public interface onPassValue2{
Map<Object, String> onPassValueStep2(Map<Object, String> insureApplicant2);
}
protected void onCreate(Bundle savedInstanceState) {
......
btn_sendInsureInfo.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
Fragment_step_1.onPassValueStep1();
Fragment_step_2.onPassValueStep2();
//NullPointerException on those two calling interface method
......
}
}
......
Fragment_step_1: (xxx is activity's name)
public class Fragment_step_1 extends Fragment implements xxx.onPassValue {
......
Map<Object, String> insureApplicant = new HashMap<>(4);
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onAttach(Context xxx){
super.onAttach(xxx);
try {
passValue = (onPassValue) xxx;
} catch (ClassCastException e) {
throw new ClassCastException(pingan_insure_info.toString()
+ " didn't implement onPassValue");
}
//THROW EXCEPTION ALWAYS
}
#Override
public Map<Object, String> onPassValueStep1(Map<Object, String> insureResult) {
for (Object key : insureResult.entrySet()) {
//System.out.println(key + " fragment_1 : " + insureResult.get(key));
System.out.println(" fragment_1 : " + key);
Log.e("map", String.valueOf(insureResult));
}
return insureResult;
}
......
Fragment_step_2: (xxx is activity's name)
public class Fragment_step_2 extends Fragment implements xxx.onPassValue2{
......
RelativeLayout correspondence;
HashMap insureApplicant2 = new HashMap<>(3);
#Override
public void onAttach(Context xxx){
super.onAttach(xxx);
try {
passValueStep2 = (onPassValueStep2) xxx;
} catch (ClassCastException e) {
throw new ClassCastException(xxx.toString()
+ " didn't implement onPassValue");
}
//THROW EXCEPTION ALWAYS
}
#Override
public Map<Object, String> onPassValueStep2(Map<Object, String> insureApplicantStep2){
for (Object key : insureApplicantStep2.entrySet()) {
System.out.println("fragment_2 : " + key);
Log.e("Hashmap2", String.valueOf(insureApplicantStep2));
}
return insureApplicant2;
}
All fragments' editText will be filled after the editText is valid and typing by user and send to the function and stored in HashMap.
For example: (AddTextChangedListener with TextWatcher)
residentAddress.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
#Override
public void afterTextChanged(Editable editable) {
residentAddress.setOnFocusChangeListener(new View.OnFocusChangeListener(){
#Override
public void onFocusChange(View view, boolean isFocus){
if(!isFocus){
if("".trim().equals(residentAddress.getText().toString())){
rAddress.setError("Resident Address is required.");
strAddress = "";
insureApplicant2.put(2, strAddress);
} else {
rAddress.setErrorEnabled(false);
rAddress.setError(null);
strAddress = residentAddress.getText().toString().trim();
insureApplicant2.put(2, strAddress);
onPassValueStep2(insureApplicant2);
//CAN PUT THE VALUE TO HASHMAP BUT CANNOT be RETURNED TO ACTIVITY :(
}
}
}
});
}
});
To pass values from fragment to activity create interface in fragment.not in activity.
Fragment A
public class FragmentA extends Fragment {
public interface InterfaceTest{
void passValue(String passval);
}
InterfaceTest interfaceTest;
#Override
public void onAttach(Context context) {
super.onAttach(context);
interfaceTest= (InterfaceTest) context;
}
public FragmentA() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView= inflater.inflate(R.layout.fragment_, container, false);
return rootView;
}
//
public void passToActivity(){
interfaceTest.passValue("yourvalues");
}
}
ACTIVITY
public class MainActivity extends AppCompatActivity implements FragmentA.InterfaceTest {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//create code for add fragment in activity
}
#Override
public void passValue(String passval) {
Log.e("print",passval);
}
}
Related
I have an app consisting of (among other things) one Activity and one Fragment. In the Activity som data is displayed using TextViews, and in the Fragment the user can input some data using EditText-views. I have a method which takes data from both the Activity and Fragment, performs some calculations and finally displays a result in both the Fragment and the Activity. This method currently works when I call if after editing the text in any of the EditText-views in the Fragment. However, I also want to call it each time I update the data in the Activity but when I attempt this, I can not fetch the EditText data because they return null.
So my question is: what is good practice, or the ”right way” to do when dealing with methods that are supposed to be reached from both an Activity and a Fragment? I would greatly appreciate if someone could lead me onto the right track.
I have read the official documentation on Fragments and there ViewModel was mentioned. But this doesn’t seem suitable in for my application since I want the Activity to be involved. Do I need to use this or can I go through my main Activity? I’ve also read about Interfaces, but I’m not sure which one would suit my project best. I’m currently using Interfaces, but I’m not sure if I’m doing it correctly.
I’ve also watched this video and read these following questions:
Shared ViewModel to help communication between fragments and parent activity
Call a fragmentMethod from another fragment, but can't refer to them in parentFragmentActivity's View pager
how to manage the fragments in android correctly?
"My God"’s reply to this question was helpful but I’m still not sure what is the best thing to do in my case, as I have a Fragment where user can input data, and the same fragment should also view data as a result of that input. (Maybe my first mistake is building the app like this?)
I provide some code in case it is helpful.
public class AccuracyFragment extends Fragment {
EditText editTextLevel, editTextAccuracy;
private OnFragmentInteractionListener mListener;
public AccuracyFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_accuracy, container, false);
editTextAccuracy = view.findViewById(R.id.text_accuracy_character);
editTextLevel = view.findViewById(R.id.text_level_character);
TextWatcher watcher = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
//Doing nothing
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
//Doing nothing
}
#Override
public void afterTextChanged(Editable editable) {
updateFragment(Float.valueOf(editTextLevel.getText().toString()), Float.valueOf(editTextAccuracy.getText().toString()));
}
};
editTextLevel.addTextChangedListener(watcher);
editTextAccuracy.addTextChangedListener(watcher);
return view;
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null; //I don’t know what this does
}
#Override
public void onResume() {
super.onResume();
// updateFragment(); //Should I have this?
}
public interface OnFragmentInteractionListener {
String[] onAccuracyFragmentInputChanged(float levelFromFragment, float accuracyFromFragment); }
public void updateFragment(float level, float accuracy) {
//Complicated method doing things with editTextLevel and editTextAccuracy. However, it doesn’t work when this method is called from outside AccuracyFragment – EditTexts are null
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public void onPause() {
super.onPause();
}
#Override
public void onDestroyView() {
super.onDestroyView();
}
#Override
public void onStop() {
super.onStop();
}
}
public class MainActivity extends AppCompatActivity implements AccuracyFragment.OnFragmentInteractionListener, AdapterView.OnItemSelectedListener {
AccuracyFragment accuracyFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_new);
selectedID = null;
textView1 = (TextView) findViewById(R.id.text_1);
textView2 = (TextView) findViewById(R.id.text_2);
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state, then we don't need to do anything and should return or else we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create a new Fragment to be placed in the activity layout
accuracyFragment = new AccuracyFragment();
}
}
//This is the core method which takes the value from two EditTexts in the Fragment, and two TextViews in the MainActivity
private String[] getRequiredAccuracy(float firstValueFromActivity, float secondValueFromActivity, float firstValueFromFragment, float secondValueFromFragment) {
//This methods uses parameters from the Activity, and two from the Fragment, and is intended to be called from both the Activity and from the Fragment itself
String returnValues[] = {s, q, r, c}; //This method is too complex to show, but it will end up outputting some values
return returnValues;
}
public void methodCalledUponClick(View view) {
//After showing a Dialog with some choices, I intend to call the method from fragment:
accuracyFragment.updateFragment();
}
#Override
public String[] onAccuracyFragmentInputChanged(float levelFromFragment, float accuracyFromFragment) {
String returnValues[] = {"0", "0", "0"};
if (selectedID != null) {
if (textView1.length() == 0 || textView2.length() == 0) {
//Do nothing if any of these are empty
} else {
returnValues = getRequiredAccuracy(Float.valueOf(textView1.getText().toString()), Float.valueOf(textView2.getText().toString()), levelFromFragment, accuracyFromFragment);
}
}
return returnValues;
}
}
}
You can go ahead with callback/Interface to communicate with fragment and activity simultaneously.
For Creating callback/Interface:
public interface CallBackListener {
void onCallBack(String value);// pass any parameter in your onCallBack which you want to return
}
In Fragment Class:
public class AccuracyFragment extends Fragment {
private CallBackListener callBackListener;
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//getActivity() is fully created in onActivityCreated and instanceOf differentiate it between different Activities
if (getActivity() instanceof CallBackListener)
callBackListener = (CallBackListener) getActivity();
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
EditText editText = (EditText) view.findViewById(R.id.edittext);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {}
#Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
if(callBackListener != null)
callBackListener.onCallBack(s.toString());
}
});
}
}
In your Activity:
public class MainActivity extends AppCompatActivity implements CallBackListener
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public void onCallBack(String value) {
Toast.makeText(mContext,"onCallback Called",Toast.LENGTH_LONG).show();
}
}
I have a parent activity NavigationActivity and it does something like this:
public class NavigationActivity extends Activity {
protected LinearLayout navigationBar;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
navigationBar = findViewById(R.id.navbar);
}
}
It has one method that sets the visibility of navigationBar:
#Override
protected void setNavbarVisibility(int view) {
if (this.navigationBar != null) {
this.navigationBar.setVisibility(view);
}
}
I have an activity AdventureActivity that extends NavigationActivity:
public class AdventureActivity extends NavigationActivity {
EditText title;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstancState);
renderAdventureLayout();
}
public void renderAdventureLayout() {
Handler h = new Handler();
title.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(final View v, boolean hasFocus) {
if (hasFocus) {
h.postDelayed(new Runnable() {
#Override
public void run() {
setNavbarVisibility(View.GONE);
}
}, 200);
} else {
h.postDelayed(new Runnable() {
#Override
public void run() {
setNavbarVisibility(View.VISIBLE);
}
}, 200);
}
}
}
}
}
Now, I didn't have to declare or initialize navigationBar in AdventureActivity, and it's already initially visible. However, the statement setNavbarVisibility(View.GONE); doesn't seem to work in AdventureActivity unless I also initialize navigationBar as it was initialized in NavigationActivity because according the debugger, navigationBar was null when it wasn't initialized in AdventureActivity. I find it redundant having to initialize navigationBar in both activities.
You haven't provided a content view in either of your activities, which results findViewById() method to return null.
If you want to create such an activity hierarchy, that means, that NavigationActivity should have an abstract method, that subclasses should implement:
public abstract class NavigationActivity extends Activity {
...
#LayoutRes
protected abstract int getLayoutId();
}
Then AdventureActivity would become:
public class AdventureActivity extends NavigationActivity {
...
#Override
#LayoutRes
protected int getLayoutId() {
return R.layout.some_activity;
}
}
Now in NavigationActivity#onCreate():
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
navigationBar = findViewById(R.id.navbar);
}
This mandates each of subclasses to provide a layout, where exists a LinearLayout with id navbar, otherwise your navigationBar would end up being null.
im newbie with fragment, and i want to insert some data into database from Fragment with methods from MainActivity
here is my code
LaporanFragment
public class LaporanFragment extends Fragment{
EditText judulL, isiL;
TextView nomor_ktp, ambilNama;
ImageView fotoL;
Button kirim;
private ProgressDialog progressDialog;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_laporan, container, false);
judulL = (EditText) v.findViewById(R.id.judulLaporan);
isiL = (EditText) v.findViewById(R.id.isiLaporan);
nomor_ktp = (TextView) getActivity().findViewById(R.id.nomor_ktp);
final String noktp = nomor_ktp.getText().toString();
//fotoL = (ImageView) v.findViewById(R.id.foto_laporan);
final String jdlLaporan = judulL.getText().toString();
final String isiLaporan = isiL.getText().toString();
kirim = (Button) v.findViewById(R.id.kirim_laporan);
kirim.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
((MainActivity)getActivity()).kirim_lapor(jdlLaporan, isiLaporan, noktp);
}
});
return v;
}
and MainActivity with kirim_lapor method
public void kirim_lapor(final String judul, final String isi, final String username){
StringRequest stringRequest = new StringRequest(Request.Method.POST,
Constants.URL_LAPOR,
new Response.Listener<String>(){
#Override
public void onResponse(String response){
//progressDialog.dismiss();
try{
JSONObject jsonObject = new JSONObject(response);
//Toast.makeText(LaporanFragment.this, jsonObject.getString("message"), Toast.LENGTH_LONG).show();
}catch(JSONException e){
e.printStackTrace();
}
}
},
new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error){
//progressDialog.hide();
//Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_LONG).show();
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("judul_laporan", judul);
params.put("isi_laporan", isi);
params.put("no_ktp", username);
return params;
}
};
RequestHandler.getInstance(this).addToRequestQueue(stringRequest);
}
the problem is when i press button kirim in fragment, the app will crash
please help me guys, sorry for my bad english.
To localize and hinder further error, instead of calling the MainActivity method via casting the getActivity(), you should make a Listener for telling the Activity about the data.
Create the interface for Listener in Fragment:
public class LaporanFragment extends Fragment {
private LaporanListener mListener;
// Define a Listener to 'speak up' to the main activity
public interface LaporanListener {
public void onSendReportClicked(String title, String content, String idNumber);
}
...
}
When clicking the button sendReport, use the listener:
public class LaporanFragment extends Fragment {
...
...
kirim.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
mListener.onSendReportClicked(jdlLaporan, isiLaporan, noktp);
}
});
...
...
}
Then you need to implement the interface Listener to the MainActivity:
public class MainActivity extends Activity implements LaporanFragment.LaporanListener {
...
#Override
public void onSendReportClicked(String title, String content, String idNumber) {
// MainActivity will receive the data here.
// You need to process here.
}
...
}
For further reading, read Creating Custom Listeners.
I am reading about how to interact between UI and background thread here.
This article has following note:
The AsyncTask does not handle configuration changes automatically,
i.e. if the activity is recreated. The programmer has to handle that
in his coding. A common solution to this is to declare the AsyncTask
in a retained headless fragment.
I dont understand what is retained headless fragment.
For example, in this way I can add fragment:
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(R.id.frame, new MyFragment());
transaction.commit();
And in fragment I can execute AsyncTask like this:
MyTask myTask = new MyTask();
String[] args = {"Hello"};
myTask.execute(args);
Is this called "to declare the AsyncTask in a retained headless fragment"?
Headless fragment is nothing but a fragment which does not have a view. In onCreate() of the fragment lifeCycle, use setRetainInstance(true);. This will not destroy the fragment even if the activity recreates. So if an AsyncTask is running in fragment, on recreation of the activity, you wont lose the AsyncTask.
In onCreate of the activity, you have to add the fragment with a tag. Before adding, check if the fragment exist using getFragmentManager().findFragmentByTag(TAG), if the fragment is null then create a new instance of the fragment and add it.
In Fragment there will not be any view inflated, so no need to override onCreateView().
An example of headlessFragment :
public class HeadlessProgressFragment extends Fragment {
private ProgressListener mProgressListener;
private AsyncTask<Void, Integer, Void> mProgressTask;
public interface ProgressListener {
void updateProgress(int progress);
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void setProgressListener(Context context) {
mProgressListener = (ProgressListener) context;
}
public void startProgress(final int size) {
if (mProgressTask == null || mProgressTask.getStatus() != AsyncTask.Status.RUNNING || mProgressTask.getStatus() == AsyncTask.Status.FINISHED) {
mProgressTask = new AsyncTask<Void, Integer, Void>() {
#Override
protected Void doInBackground(Void... params) {
for (int index = 0; index < size; index++) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
publishProgress(index + 1);
}
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (mProgressListener != null) {
mProgressListener.updateProgress(values[0]);
}
}
};
mProgressTask.execute();
}
}
}
In Activity Something like this :
public class MainActivity extends FragmentActivity implements HeadlessProgressFragment.ProgressListener {
private static final String TAG = "progress_fragment";
private ProgressBar mProgressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dummy_view);
mHeadlessProgressFragment = (HeadlessProgressFragment) getSupportFragmentManager().findFragmentByTag(TAG);
if (mHeadlessProgressFragment == null) {
mHeadlessProgressFragment = new HeadlessProgressFragment();
getSupportFragmentManager().beginTransaction().add(mHeadlessProgressFragment,TAG).commit();
}
mHeadlessProgressFragment.setProgressListener(this);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
final Button startFillBtn = (Button) findViewById(R.id.btn_start_filling);
startFillBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mHeadlessProgressFragment.startProgress(100);
}
});
}
#Override
public void updateProgress(int progress) {
mProgressBar.setProgress(progress);
}
}
As i simplified the complexity in my case by Just update your UI (if you have to) by checking the calling fragment or activity is present or not. Start the asynctask by assigning the weakreference of calling entity.
I need to build a DialogFragment which returns user input from the dialog to an activity.
The dialog needs to be called in an OnClickListener which gets called when an element in a listview gets clicked.
The return value of the DialogFragment (the input of the user) should be directly available in the OnClickListener in the activity.
I tried to implement this by sticking to the official docs: http://developer.android.com/guide/topics/ui/dialogs.html#PassingEvents
I need something like the following which doesn't work since I don't know how to make the anonymous OnClickListener implement the interface of the CustomNumberPicker class.
As far as I know implementing the interface is necessary in order to get data from the DialogFragment back to the Activity.
Main Activity:
public class MainAcitivity extends ActionBarActivity {
[...]
// ArrayAdapter of the Listview
private class ListViewArrayAdapter extends ArrayAdapter<Exercise> {
public ListViewArrayAdapter(Context context, ArrayList<Exercise> exercises) {
super(context, 0, exercises);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
[...]
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_workoutdetail, parent, false);
}
TextView tvSets = (TextView) convertView.findViewById(R.id.tvWorkoutExerciseSets);
tvSets.setText(sets.toString());
// OnClickListener for every element in the ListView
tvSets.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// This is where the Dialog should be called and
// the user input from the Dialog should be returned
DialogFragment numberpicker = new CustomNumberPicker();
numberpicker.show(MainActivity.this.getSupportFragmentManager(), "NoticeDialogFragment");
}
// Here I would like to implement the interface of CustomNumberPicker
// in order to get the user input entered in the Dialog
});
return convertView;
}
}
}
CustomNumberPicker (basically the same as in the docs):
public class CustomNumberPicker extends DialogFragment {
public interface NoticeDialogListener {
public void onDialogPositiveClick(DialogFragment dialog);
public void onDialogNegativeClick(DialogFragment dialog);
}
// Use this instance of the interface to deliver action events
NoticeDialogListener mListener;
// Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Verify that the host activity implements the callback interface
try {
// Instantiate the NoticeDialogListener so we can send events to the host
mListener = (NoticeDialogListener) activity;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString()
+ " must implement NoticeDialogListener");
}
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Sets")
.setPositiveButton("set", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Return stuff here to the activity?
}
})
.setNegativeButton("cancle", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
});
// Create the AlertDialog object and return it
return builder.create();
}
}
Something like this?
public class CustomNumberPicker extends DialogFragment {
private NoticeDialogListener ndl;
public interface NoticeDialogListener {
public void onDialogPositiveClick(DialogFragment dialog);
public void onDialogNegativeClick(DialogFragment dialog);
}
//add a custom constructor so that you have an initialised NoticeDialogListener
public CustomNumberPicker(NoticeDialogListener ndl){
super();
this.ndl=ndl;
}
//make sure you maintain an empty constructor
public CustomNumberPicker( ){
super();
}
// Use this instance of the interface to deliver action events
NoticeDialogListener mListener;
// Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
//remove the check that verfis if your activity has the DialogListener Attached because you want to attach it into your list view onClick()
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Sets")
.setPositiveButton("set", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
ndl.onDialogPositiveClick(dialog);
}
})
.setNegativeButton("cancle", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
ndl.onDialogNegativeClick(dialog);
}
});
// Create the AlertDialog object and return it
return builder.create();
}
}
and then your listView onClick becomes:
tvSets.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// This is where the Dialog should be called and
// the user input from the Dialog should be returned
//
//
DialogFragment numberpicker = new CustomNumberPicker(new NoticeDialogListener() {
#Override
public void onDialogPositiveClick(DialogFragment dialog) {
//What you want to do incase of positive click
}
#Override
public void onDialogNegativeClick(DialogFragment dialog) {
//What you want to do incase of negative click
}
};);
numberpicker.show(MainActivity.this.getSupportFragmentManager(), "NoticeDialogFragment");
}
// Here I would like to implement the interface of CustomNumberPicker
// in order to get the user input entered in the Dialog
});
Do read the comments I have added.And it can even be further optimized because you really dont need an entire dialog instance to get the values you need.
EDIT a possible optimization could be:
Changing the Listener interface to :
public interface NoticeDialogListener {
public void onDialogPositiveClick(String output);
public void onDialogNegativeClick(String output);
//or whatever form of output that you want
}
Then modify the implemented methods accordingly.
You should have your activity, implement your interface (NoticeDialogListener).
public class MainActivity extends ActionBarActivity implements
NoticeDialogListener{
#Override
public void onDialogPositiveClick(DialogFragment dialog){
//Do something
}
#Override
public void onDialogNegativeClick(DialogFragment dialog){
//Do some other things
}
[...]
}
Then in your button click listeners of the dialog, you use the mListener and call the methods, which is now implemented in the activity and the code will be executed there.
builder.setMessage("Sets")
.setPositiveButton("set", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if(mListener != null)
mListener.onDialogPositiveClick(CustomNumberPicker.this);
}
});
Also note that you should set the mListener to null in the onDetach() method of your DialogFragment.
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
Here's how it's done:
In the Activity where you show the DiaogFragment, set the arguments of the DialogFragment with the desired name value pair.
Also make sure that the activity implements the DialogInterface.OnClickListener
In the overridded onClick pick up the value from the aforementioned name value pair
public class MainActivity extends AppCompatActivity implements DialogInterface.OnClickListener {
private static SettingsFragment settingsFragment;
private Button btnSettings;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
btnSettings = (Button) findViewById(R.id.btnSettings);
btnSettings.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
settingsFragment = new SettingsFragment();
Bundle bundle = new Bundle();
bundle.putString("myKey", null);
settingsFragment.setArguments(bundle);
//Use the commented out line below if you want the click listener to return to a fragment instead of an activity
//assuming that this class in a fragment and not an activity
//rotateSettingsFragment.setTargetFragment(getActivity().getSupportFragmentManager().findFragmentByTag("TagForThisFragment"), 0);
settingsFragment.setTargetFragment(settingsFragment, 0);
settingsFragment.setCancelable(true);
settingsFragment.show(getSupportFragmentManager(), "SettingsFragment");
}
});
}
#Override
public void onClick(DialogInterface dialog, int which) {
if(getResources().getResourceEntryName(which).equals("btnSettingFragmentClose")) {
String myValue = settingsFragment.getArguments().getString("myKey");
dialog.dismiss();
}
}
}
In your DialogFragment declare a DialogInterface.OnClickListener and cast it to the activity in the onAttach.
In the event that needs to send back the data to the activity; set the buddle arguments and then call the onClickListener.onClick
public class SettingsFragment extends DialogFragment {
private View rootView;
private Button btnSettingFragmentClose;
private DialogInterface.OnClickListener onClickListener;
public SettingsFragment() {}
/* Uncomment this and comment out on onAttach when you want to return to a fragment instead of an activity.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onClickListener = (DialogInterface.OnClickListener) getTargetFragment();
}
*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_settings, container, false);
btnSettingFragmentClose = (Button) rootView.findViewById(R.id.btnSettingFragmentClose);
btnSettingFragmentClose.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getArguments().putString("myKey", "Hello World!");
onClickListener.onClick(getDialog(), v.getId());
}
});
return rootView;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
onClickListener = (DialogInterface.OnClickListener) activity;
}
catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement mainFragmentCallback");
}
}
}
This simple solution works for me:
public class MyActivity implements MyDialogFragment.Listener {
// ...
#Override
public void onMyEvent() {
// do something here
}
}
public class MyDialogFragment extends DialogFragment {
private Listener mCallback;
public interface Listener {
void onMyEvent();
}
#SuppressLint("RestrictedApi")
#Override
public void setupDialog(final Dialog dialog, int style) {
super.setupDialog(dialog, style);
View contentView = View.inflate(getContext(), R.layout.dialog_fragment_custom, null);
dialog.setContentView(contentView);
mCallback = (Listener) getActivity();
Button myBtn = (Button) dialog.findViewById(R.id.btn_custom);
myBtn.setOnClickListener(v -> {
mCallback.onMyEvent();
dismiss();
});
}
}
As an example you can use DatePickerDialog where DatePickerDialog.OnDateSetListener used to deliver result.
or this is one of my implementations that allow to keep dialog screen open until user not finished with some action or not entered valid data. With custom callback that provide exact interface to this dialog.
public class ConfirmPasswordDialog extends DialogFragment {
private OnPaswordCheckResult resultListener;
private TextView passwordView;
public ConfirmPasswordDialog(OnPaswordCheckResult resultListener){
this.resultListener = resultListener;
}
#Override
public android.app.Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View dialogView = inflater.inflate(R.layout.dialog_layout, null);
builder.setView(dialogView);
passwordView = (TextView) dialogView.findViewById(R.id.password);
passwordView.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {/*do nothing*/}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {/*do nothing*/}
#Override
public void afterTextChanged(Editable s) {
if(passwordView != null){
passwordView.setError(null);
}
}
});
builder.setView(dialogView);
builder.setMessage("Please enter password to finish with action");
builder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
/* do something when click happen, in this case mostly like dummy because data return later
* after validation or immediately if required*/
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.setTitle("Confirm password");
final AlertDialog dialog = builder.create();
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
#Override
public void onShow(final DialogInterface dialogInterface) {
Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
positiveButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
if(passwordView == null || !isAdded()){
return;
}
String password = passwordView.getText().toString();
if(PrefUtils.isPasswordValid(getActivity(), password)){
if(resultListener == null){
return;
}
/* Return result and dismiss dialog*/
resultListener.onValidPassword();
dialog.dismiss();
} else {
/* Show an error if entered password is invalid and keep dialog
* shown to the user*/
String error = getActivity().getString(R.string.message_password_not_valid);
passwordView.setError(error);
}
}
});
}
});
return dialog;
}
/**
* Custom callback to return result if entered password is valid
*/
public static interface OnPaswordCheckResult{
void onValidPassword();
}
}