I want to create a DialogFragment subclass that can be reused accross my app. So when an Activity wants to create a DialogFragment, it can set its own texts and attach its own listeners for the positive and negative buttons. For the texts inside the DialogFragment, I pass them to the fragment using the arguments bundle to make sure they are persisted when the configuration changes. However, the listeners for the buttons cannot be passed to the fragment with these arguments.
What would be best practice to attach these listeners to the DialogFragment, without losing them when the configuration changes?
BaseDialogFragment.java
public abstract class BaseDialogFragment extends AppCompatDialogFragment {
public AppCompatDialog dialog;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(getLayoutResource(), null);
ButterKnife.bind(this, view);
return view;
}
#Override
public void onStart() {
super.onStart();
dialog = (AppCompatDialog) getDialog();
if (dialog != null) {
WindowManager windowManager =
(WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
dialog.getWindow().setLayout(width - 75, ViewGroup.LayoutParams.WRAP_CONTENT);
WindowManager.LayoutParams params = dialog.getWindow().getAttributes();
dialog.getWindow().setAttributes(params);
dialog.getWindow().setBackgroundDrawable(ContextCompat.getDrawable(getContext(), R.drawable.dialog_rounded_back));
}
}
protected abstract int getLayoutResource();
#Override
public void show(FragmentManager manager, String tag) {
try {
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commitAllowingStateLoss();
} catch (IllegalStateException e) {
}
}
}
Child fragment dialog:
public class InvitationAcceptRejectDialog extends BaseDialogFragment {
public InvitationAcceptRejectDialog() {
}
#Override
protected int getLayoutResource() {
return R.layout.invite_accept_reject_dialog;
}
protected OnDialogClickListener alertListener;
#BindView(R.id.tvDialogTitle)
AppCompatTextView tvDialogTitle;
#BindView(R.id.tvDialogMessage)
AppCompatTextView tvDialogMessage;
int requestCode;
public String dialogTitle;
public String dialogMessage;
public Bundle bundle;
#OnClick({R.id.imgCloseDialog, R.id.btnYes, R.id.btnNo})
public void dialgClick(View view) {
switch (view.getId()) {
case R.id.imgCloseDialog:
break;
case R.id.btnYes:
alertListener.onPositiveClick(dialog, requestCode, bundle);
break;
case R.id.btnNo:
alertListener.onNegativeClick(dialog, requestCode, bundle);
break;
}
dialog.dismiss();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
tvDialogTitle.setText(dialogTitle);
tvDialogMessage.setText(dialogMessage);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
public static class Builder {
InvitationAcceptRejectDialog alertDialogFragment;
public Builder() {
alertDialogFragment = new InvitationAcceptRejectDialog();
}
public Builder setTitle(String title) {
alertDialogFragment.dialogTitle = title;
return this;
}
public Builder setMessage(String message) {
alertDialogFragment.dialogMessage = message;
return this;
}
public Builder setBundel(Bundle bundel) {
alertDialogFragment.bundle = bundel;
return this;
}
public Builder setCallback(OnDialogClickListener mListener, int code) {
alertDialogFragment.alertListener = mListener;
alertDialogFragment.requestCode = code;
return this;
}
public InvitationAcceptRejectDialog build() {
return alertDialogFragment;
}
}
}
Implementation in activity or fragmnet:
InvitationAcceptRejectDialog build = new InvitationAcceptRejectDialog.Builder()
.setCallback(this, Constant.DialogConstant.ACCEPET_INVITE)
.setTitle(getString(R.string.logout))
.setMessage(getString(R.string.logout_message))
.build();
build.show(getSupportFragmentManager(), "TAG");
Interface for handle positive and negative button click:
public interface OnDialogClickListener {
void onPositiveClick(DialogInterface dialog, int id, Bundle bundle);
void onNegativeClick(DialogInterface dialog, int id, Bundle bundle);
}
I would do something like this, Use buttons with as interfaces and you can call this class any where you want in your project. and you can save it's instance too on configuration change :
public class MyDialogFragment extends DialogFragment {
// the fragment initialization parameters,
private static final String DIALOG_TITLE = "DIALOG_TITLE";
private static final String DIALOG_MESSAGE = "DIALOG_MESSAGE";
private static final String DIALOG_BUTTON_POSITIVE = "DIALOG_BUTTON_POSITIVE";
private static final String DIALOG_BUTTON_NEGATIVE = "DIALOG_BUTTON_NEGATIVE";
private String Title;
private String Message;
private String btnPositive;
private String btnNegative;
public interface DialogFragmentButtonPressedListener {
void onPositiveButtonClick();
void onNegativeButtonClick();
}
public static MyDialogFragment newInstance(String title, String message, String btnPositiveText, String btnNegativeText) {
MyDialogFragment fragment = new MyDialogFragment();
Bundle args = new Bundle();
args.putString(DIALOG_TITLE, title);
args.putString(DIALOG_MESSAGE, message);
args.putString(DIALOG_BUTTON_POSITIVE, btnPositiveText);
args.putString(DIALOG_BUTTON_NEGATIVE, btnNegativeText);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
Title = getArguments().getString(DIALOG_TITLE);
Message = getArguments().getString(DIALOG_MESSAGE);
btnPositive = getArguments().getString(DIALOG_BUTTON_POSITIVE);
btnNegative = getArguments().getString(DIALOG_BUTTON_NEGATIVE);
}
}
// updated this method. before update it was onAttach(Activity activity)
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (!(context instanceof DialogFragmentButtonPressedListener)) {
throw new ClassCastException(context.toString() + " must implement DialogFragmentButtonPressedListener");
}
}
static Handler handler = new Handler(Looper.getMainLooper());
final Runnable runnable = new Runnable( ) {
#Override
public void run() {
if (mAlertDialog.isShowing()) {
mAlertDialog.dismiss();
}
}
};
AlertDialog mAlertDialog = null;
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// return new AlertDialog.Builder(getActivity())
// .setTitle(Title)
// .setMessage(Message)
// .setPositiveButton(btnPositive, new DialogInterface.OnClickListener() {
//
// #Override
// public void onClick(DialogInterface dialog, int which) {
// ((DialogFragmentButtonPressedListener) getActivity()).onPositiveButtonClick();
// }
// })
// .setNegativeButton(btnNegative, new DialogInterface.OnClickListener() {
//
// #Override
// public void onClick(DialogInterface dialog, int which) {
// ((DialogFragmentButtonPressedListener) getActivity()).onNegativeButtonClick();
// }
// })
// .create();
return mAlertDialog;
}
}
And in my calling activity I would do like this:
new MyDialogFragment();
myDialogFragment = MyDialogFragment.newInstance("successfull", "Please follow the instructions", " OK ", "negativeButtonText");
myDialogFragment.show(getSupportFragmentManager(), "MyDialogFragment");
Regarding passing the listeners, you can create an interface with two functions one each for the positive and the negative button in your DialogFragment. Inside click listeners of your positive and negative buttons, you can call these interface methods accordingly. Create a method inside your DialogFragment to set this Interface.
Related
my onAttach() method assigns the context to the listener, however, my listener is null somehow. How can I fix this problem properly? I hope you can provide me the code with some instructions?
ChooseScreen class which initializes the dialog (In this case nameDialog):
public class ChooseScreen extends AppCompatActivity {
private Button vsFriend;
private Button vsAndroid;
private NameDialog.NameDialogListener listener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_choose_screen);
vsFriend = findViewById(R.id.vsF);
vsAndroid = findViewById(R.id.vsA);
vsFriend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
openDialog();
}
});
}
public void openDialog() {
NameDialog nameDialog = new NameDialog();
nameDialog.show(getSupportFragmentManager(), "example");
}
}
NameDialog class with getTexts interface:
public class NameDialog extends AppCompatDialogFragment {
private EditText firstPlayer;
private EditText secondPlayer;
private NameDialogListener listener;
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
final View view = inflater.inflate(R.layout.layout_dialog, null);
firstPlayer = view.findViewById(R.id.edit_player1);
secondPlayer = view.findViewById(R.id.edit_player2);
builder.setView(view)
.setTitle("Names")
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
String player1 = firstPlayer.getText().toString();
String player2 = secondPlayer.getText().toString();
listener.getTexts(player1, player2);
// Intent intent = new Intent(NameDialog.this.getActivity(), Game.class);
// startActivity(intent);
}
});
return builder.create();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try{
listener = (NameDialogListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "must implement");
}
}
public interface NameDialogListener {
void getTexts(String player1, String player2);
}
}
Game class which implements NameDialogListener and overrides the interface method(getTexts):
public class Game extends AppCompatActivity implements
NameDialog.NameDialogListener {
private TextView player1Name;
private TextView player2Name;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
player1Name = findViewById(R.id.player1TextView);
player2Name = findViewById(R.id.player2TextView);
}
#Override
public void getTexts(String player1, String player2) {
player1Name.setText(player1);
player2Name.setText(player2);
}
}
Error: If I don't use try-catch block, the error will be NullPointerException because listener is null!
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.user.tictactoe, PID: 30462
java.lang.ClassCastException: com.example.user.tictactoe.ChooseScreen#1a0a489must implement
at com.example.user.tictactoe.NameDialog.onAttach(NameDialog.java:62)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1372)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1759)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1827)
at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:797)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2596)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2383)
at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2338)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2245)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:703)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1518)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
When you attach your Fragment you attempt to get a Listener from your ChoooseScreen Activity. That Activity does not implement NameDialogListener, so you get a ClassCastException. In your examples you show another Activity, Game that does implement the listener, however any activity you add your Fragment in will need to implement the listener to work with your onAttach() code.
Short answer: if you want to show the Fragment in ChooseScreen, your code requires ChooseScreen to implement NameDialogListener.
onAttach will get the context of your parent activity. when you open your Dialog from ChooseScreen activity, the parent is ChooseScreen. The interface callback will be given to ChooseScreen itself. Then what you need to do is to call Intent with player1Name and player2Name.
Anyways I will share the code for you.
Your ChooseScreen
public class ChooseScreen extends AppCompatActivity implements NameDialog.NameDialogListener {
private Button vsFriend;
private Button vsAndroid;
private NameDialog.NameDialogListener listener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_choose_screen);
vsFriend = findViewById(R.id.vsF);
vsAndroid = findViewById(R.id.vsA);
vsFriend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
openDialog();
}
});
}
public void openDialog() {
NameDialog nameDialog = new NameDialog();
nameDialog.show(getSupportFragmentManager(), "example");
}
#Override
public void getTexts(String player1, String player2) {
Intent intent = new Intent(this, Game.class);
intent.putExtra("PLAYER_ONE", player1);
intent.putExtra("PLAYER_TWO", player2);
startActivity(intent);
}
}
Your NameDialog
public class NameDialog extends AppCompatDialogFragment {
private EditText firstPlayer;
private EditText secondPlayer;
private NameDialogListener listener;
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
final View view = inflater.inflate(R.layout.layout_dialog, null);
firstPlayer = view.findViewById(R.id.edit_player1);
secondPlayer = view.findViewById(R.id.edit_player2);
builder.setView(view)
.setTitle("Names")
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
String player1 = firstPlayer.getText().toString();
String player2 = secondPlayer.getText().toString();
listener.getTexts(player1, player2);
//TODO you can simply use below code and comment listener.getTexts();
// Intent intent = new Intent(NameDialog.this.getActivity(), Game.class);
// intent.putExtra("PLAYER_ONE", player1);
// intent.putExtra("PLAYER_TWO", player2);
// startActivity(intent);
}
});
return builder.create();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
listener = (NameDialogListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "must implement");
}
}
public interface NameDialogListener {
void getTexts(String player1, String player2);
}
}
Your Game
public class Game extends AppCompatActivity
/* implements NameDialog.NameDialogListener*/ {
private TextView player1Name;
private TextView player2Name;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
player1Name = findViewById(R.id.player1TextView);
player2Name = findViewById(R.id.player2TextView);
player1Name.setText(
getIntent().getStringExtra("PLAYER_ONE"));
player2Name.setText(
getIntent().getStringExtra("PLAYER_TWO"));
}
// #Override
// public void getTexts(String player1, String player2) {
// }
}
Try this and let me know...
I am trying to implement callback between AsyncTask and Fragment but cannot find correct info how to do it. The issue is that all callback implementations are between activity and asynctask but I need between fragment and asynctask. Could someone give me small working example how to implement it without activity.
My action structure: Fragment call DialogFragment -> choose something and send server request to async task -> async task process everything and update view and some variables. My main problem is that I call prepareData() only once in onCreate and when I walk between other fragment and returns come back I see old data. That is to say there is not enough to update only view in onPost of asynctask. It will be good to have callback which will update the whole variables.
public class TermsAndConditionsFragment extends SherlockFragment implements OnClickListener, OnTouchListener, OnItemClickListener, onTaskListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fm = getSherlockActivity().getSupportFragmentManager();
prepareData();
}
public void prepareData() {
termsAndConditionsM = new TermsAndConditionsManager(getSherlockActivity());
termsAndConditions = termsAndConditionsM.getTermsAndConditions();
if (termsAndConditions != null) {
int totalPayments = Integer.valueOf(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
if (totalPayments > 0) {
paymentsData = termsAndConditionsM.getpayments();
if (paymentsData != null) {
payments = new ArrayList<Payment>();
for (int i = 1; i <= totalPayments; i++) {
paymentValues = new Payment();
paymentValues.setPaymentID(Integer.valueOf(paymentsData.get(ServerAPI.PAYMENT_NO + "_" + i)));
paymentValues.setPaymentDate(paymentsData.get(ServerAPI.PAYMENT_DATE + "_" + i));
paymentValues.setPaymentTotalAmount(paymentsData.get(ServerAPI.PAYMENT_TOTAL + "_" + i));
payments.add(paymentValues);
}
}
}
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = init(inflater, container);
if (payments != null || termsAndConditions != null)
updateTermsAndConditionsView();
return rootView;
}
private View init(LayoutInflater inflater, ViewGroup container) {
rootView = inflater.inflate(R.layout.fragment_terms_and_conditions, container, false);
...
return rootView;
}
public void updateTermsAndConditionsView() {
etHowMuch.setText("£" + termsAndConditions.get(ServerAPI.AMOUNT_OF_CREDIT));
etForHowLong.setText(Helpers.ConvertDays2Date(Integer.valueOf(termsAndConditions.get(ServerAPI.TERM_OF_AGREEMENT_IN_DAYS))));
PaymentAdapter adapter = new PaymentAdapter(getSherlockActivity(), R.layout.custom_loan_item, payments);
lvPayments.setAdapter(adapter);
tvNoOfPayments.setText(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
tvFirstPayment.setText(termsAndConditions.get(ServerAPI.FIRST_PAYMENT_DATE));
tvTotalRepayable.setText("£" + termsAndConditions.get(ServerAPI.TOTAL_REPAYABLE));
}
#Override
public void onClick(View v) {
ft = fm.beginTransaction();
howMuch = etHowMuch.getText().toString();
forHowLong = etForHowLong.getText().toString();
switch (v.getId()) {
case R.id.etHowMuch:
f = new NumberPaymentsPickerFragment();
args = new Bundle();
args.putInt(Const.HOW_MUCH, Integer.valueOf(howMuch.replace("£", "")));
args.putDouble(ServerAPI.PAYMENT_STEP, Const.PAYMENT_STEP);
args.putString(Const.STATE, ServerAPI.TERMS_AND_CONDITIONS);
f.setArguments(args);
f.setTargetFragment(this, DIALOG_FRAGMENT);
f.show(getActivity().getSupportFragmentManager(), Const.HOW_MUCH);
break;
case R.id.etForHowLong:
f = new NumberPaymentsPickerFragment();
args = new Bundle();
args.putInt(Const.FOR_HOW_LONG, Integer.valueOf(Helpers.ConvertDate2Days(forHowLong)));
args.putDouble(ServerAPI.PAYMENT_STEP, Const.PAYMENT_STEP);
args.putString(Const.STATE, ServerAPI.TERMS_AND_CONDITIONS);
f.setArguments(args);
f.setTargetFragment(this, DIALOG_FRAGMENT);
f.show(getActivity().getSupportFragmentManager(), Const.FOR_HOW_LONG);
break;
case R.id.tvPersonalDetails:
sfm.saveCurFragment(ServerAPI.PERSONAL_DETAILS, 0);
ft.replace(android.R.id.content, new PersonalDetailsFragment(), ServerAPI.PERSONAL_DETAILS).addToBackStack(null).commit();
break;
case R.id.tvAgreementDetails:
sfm.saveCurFragment(ServerAPI.AGREEMENT_DETAILS, 0);
ft.replace(android.R.id.content, new AgreementDetailsFragment(), ServerAPI.AGREEMENT_DETAILS).addToBackStack(null).commit();
break;
case R.id.bApply:
break;
}
#Override
public void onUpdateData() {
Log.d(TAG, "Update data");
}
}
DialogFragment:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
...
}
public Dialog onCreateDialog(Bundle savedInstanceState) {
...
return createDialog(v, R.string.for_how_long, etHowMuch, etForHowLong, etPromotionCode);
}
return null;
}
private Dialog createDialog(View view, int titleResID, final EditText howMuchField, final EditText forHowLongField, final EditText promotionCodeField) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(titleResID);
builder.setView(view);
builder.setPositiveButton(R.string.set, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
doShowProgress();
}
private void doShowProgress() {
ExecuteServerTaskBackground task = new
ExecuteServerTaskBackground(getActivity());
task.action = ServerAPI.GET_TERMS_AND_CONDITIONS;
onTaskListener listener = new onTaskListener() {
#Override
public void onUpdateData() {
Log.d(TAG, "Updaaate");
}
};
task.setListener(listener);
task.args = args;
task.execute();
}
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
return builder.create();
}
AsyncTask:
onTaskListener mListener;
public interface onTaskListener {
void onUpdateData();
}
public void setListener(onTaskListener listener){
mListener = listener;
}
public ExecuteServerTaskBackground(Activity activity) {
this.mActivity = activity;
this.mContext = activity.getApplicationContext();
}
#Override
protected void onPreExecute() {
pb = (ProgressBar) mActivity.findViewById(R.id.progressBar1);
pb.setVisibility(View.VISIBLE);
}
#Override
protected Void doInBackground(Void... params) {
ServerAPI server = new ServerAPI(mContext);
if (!args.isEmpty())
server.serverRequest(action, args);
else
server.serverRequest(action, null);
return null;
}
#Override
protected void onPostExecute(Void result) {
mListener.onUpdateData();
//There is I just update view but how to update whole variables throughtout callback?
// tvNoOfPayments = (TextView) mActivity.findViewById(R.id.tvNoOfPaymentsValue);
// tvFirstPayment = (TextView) mActivity.findViewById(R.id.tvFirstPaymentValue);
// tvTotalRepayable = (TextView) mActivity.findViewById(R.id.tvTotalRepayableValue);
//
// lvPayments = (ListView) mActivity.findViewById(R.id.lvData);
//
// termsConditionsM = new TermsAndConditionsManager(mContext);
//
// termsAndConditions = termsConditionsM.getTermsAndConditions();
//
// int totalPayments = Integer.valueOf(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
//
// if (totalPayments > 0) {
// if (termsAndConditions != null) {
// tvNoOfPayments.setText(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
// tvFirstPayment.setText(termsAndConditions.get(ServerAPI.FIRST_PAYMENT_DATE));
// tvTotalRepayable.setText("£" + termsAndConditions.get(ServerAPI.TOTAL_REPAYABLE));
// }
//
// paymentsData = termsConditionsM.getpayments();
//
// if (paymentsData != null) {
// Log.d(TAG, paymentsData.toString());
//
// payments = new ArrayList<Payment>();
//
// for (int i = 1; i <= totalPayments; i++) {
// paymentValues = new Payment();
// paymentValues.setPaymentID(Integer.valueOf(paymentsData.get(ServerAPI.PAYMENT_NO + "_" + i)));
// paymentValues.setPaymentDate(paymentsData.get(ServerAPI.PAYMENT_DATE + "_" + i));
// paymentValues.setPaymentTotalAmount(paymentsData.get(ServerAPI.PAYMENT_TOTAL + "_" + i));
// payments.add(paymentValues);
// }
//
// PaymentAdapter adapter = new PaymentAdapter(mContext, R.layout.custom_loan_item, payments);
// lvPayments.setAdapter(adapter);
// }
//
}
pb.setVisibility(View.GONE);
super.onPostExecute(result);
}
Without taking your code in consideration I will post the most essential to make a functional callback.
TestFragment:
public class TestFragment extends Fragment {
/* Skipping most code and I will only show you the most essential. */
private void methodThatStartsTheAsyncTask() {
TestAsyncTask testAsyncTask = new TestAsyncTask(new FragmentCallback() {
#Override
public void onTaskDone() {
methodThatDoesSomethingWhenTaskIsDone();
}
});
testAsyncTask.execute();
}
private void methodThatDoesSomethingWhenTaskIsDone() {
/* Magic! */
}
public interface FragmentCallback {
public void onTaskDone();
}
}
TestAsyncTask:
public class TestAsyncTask extends AsyncTask<Void, Void, Void> {
private FragmentCallback mFragmentCallback;
public TestAsyncTask(FragmentCallback fragmentCallback) {
mFragmentCallback = fragmentCallback;
}
#Override
protected Void doInBackground(Void... params) {
/* Do your thing. */
return null;
}
#Override
protected void onPostExecute(Void result) {
mFragmentCallback.onTaskDone();
}
}
I am trying to create a custom dialog using dialogFragment, here I am not be able to display the dialog. The main problem is overriden code is not getting called. Can anyone fix this issue. Here is my code:
BaseDialogFragment.java
public class BaseDialogFragment extends DialogFragment {
private int layoutId;
protected Activity mActivity;
public void setLayoutId(int layoutId){
this.layoutId = layoutId;
}
public BaseDialogFragment(){
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setStyle(BaseDialogFragment.STYLE_NO_TITLE, R.style.share_dialog);
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState){
View v = inflater.inflate(layoutId, container, false);
return v;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
public void initViews(View v){
getDialog().setCanceledOnTouchOutside(true);
}
}
CustomDialog.java:
#SuppressLint("ValidFragment")
public class CustomDialog extends BaseDialogFragment {
private String message;
private btnOkClick okClickListerner;
private TextView simpleMsg;
private WebView termsConditionWeb;
private Button okBtn;
Boolean isNormalDialog = false;
private Typeface fontClanProBold;
private View v;
private Context context;
public interface btnOkClick{
void clicked();
}
public CustomDialog(String message, btnOkClick okClickListerner, Boolean isNormalDialog){
this.message = message;
this.okClickListerner = okClickListerner;
this.isNormalDialog = isNormalDialog;
this.mActivity = null;
setLayoutId(R.layout.activity_custom_dialog);
initViews(v);
}
#Override
public void initViews(View v) {
super.initViews(v);
this.simpleMsg = (TextView) v.findViewById(R.id.simpleMsg);
this.termsConditionWeb= (WebView) v.findViewById(R.id.termsConditionWeb);
this.okBtn = (Button) v.findViewById(R.id.okBtn);
fontClanProBold = Typeface.createFromAsset(context.getAssets(), "fonts/ufonts.com_clanpro-bold.ttf");
Log.e("isNormal", isNormalDialog.toString());
if(isNormalDialog){
this.simpleMsg.setVisibility(View.VISIBLE);
this.simpleMsg.setText(message);
this.simpleMsg.setTypeface(fontClanProBold);
} else {
this.termsConditionWeb.setVisibility(View.VISIBLE);
this.termsConditionWeb.loadData(message, "text/html", "UTF-8");
}
setCancelable(false);
initEvent(v);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.mActivity = activity;
}
private void initEvent(View v){
okBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(okClickListerner != null){
okClickListerner.clicked();
}
dismiss();
}
});
}
public static void ShowDialog(FragmentManager fm, String message, btnOkClick okClickListerner, Boolean isNormalDialog){
CustomDialog dialog = new CustomDialog(message, okClickListerner, isNormalDialog);
dialog.show(fm, "");
}
}
MainActivity.java
inside a onClickListener
CustomDialog.ShowDialog(getSupportFragmentManager(), getResources().getString(R.string.message_register), new CustomDialog.btnOkClick() {
#Override
public void clicked() {
finish();
}
}, isNormalDialog);
It is bad practice to set values inside your Dialog constructor. Instead pass your values as arguments and initialize them on onCreate callback. Furthermore, you shall avoid saving instances of your activity on your fragment, it may lead to memory leaks. Instead I recomend you to create an interface on your CustomDialog or in your BaseDialogFragment that all activitys that uses them must implement. Then you need to implemnt onClickListener interface on your Dialog and inside it you can call mListener.onButtonClickListener(). See the example DialogFragment.
Your CustomDialog would look something like:
public class CustomDialog extends BaseDialogFragment {
private myFragmentInterface mListener;
public static CustomDialog newInstance(String message, Boolean isNormalDialog){
Bundle args = new Bundle();
args.putString(MESSAGE_ARG_KEY, message);
args.putBoolean(TYPE_ARG_KEY, isNormalDialog);
CustomDialog instance = new CustomDialog();
instance.setArguments(args);
}
#override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
message = getArguments().getStirng(MESSAGE_ARG_KEY);
isNormalDialog = getArguments().getBoolean(TYPE_ARG_KEY);
}
#override
public void onAttach(Activity activity){
super.onAttach();
try{
mListener = (myFragmentInterface) activity;
}catch(ClassCastException e){
throw new ClassCastException("activiy must implement myFragmentInterface");
}
}
public void onDetach(){
super.onDetach();
mListener = null;
}
public interface myFragmentInterface{
onButtonClickListener(String... params);
}
}
So in my app i'm in need of using several confirmation dialogFragments of the same type, basically, it has a message, yes/no and a callback for the positive message. I managed to do it, except for the callback part, which i cant figure it out why it is not being called. Any help would be appreciated. Thx.
public class MessageDialogFragment2 extends DialogFragment {
* Config DialogFrag
*/
private static String title = "";
private static String message = "";
private static String positiveButtonValue = "";
private static String negativeButtonValue = "";
private static MessageDialogFragment2 myDialog;
public static void newInstance() {
myDialog = new MessageDialogFragment2();
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
// request a window without the title
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = getActivity().getLayoutInflater().inflate(R.layout.exit_dialog_fragment, container, false);
setCancelable(false);
return v;
}
private static void showDialog(FragmentManager fragmentManager, String dialogId){
myDialog.show(fragmentManager, dialogId);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.inject(this, view);
//
// setValues();
}
#OnClick({R.id.positiveButton, R.id.negativeButton})
public void exit(View view){
switch(view.getId()){
case R.id.positiveButton:
break;
case R.id.negativeButton:
MessageDialogFragment2.this.dismiss();
break;
}
}
public static class MakeDialog{
private Activity activity;
private PositiveCallback positiveCallBack;
private NegativeCallback negativeCallBack;
public MakeDialog(Activity act){
this.activity = act;
newInstance();
}
public MakeDialog setTitle(String title2){
title = title2;
return this;
}
public MakeDialog setMessage(String message2){
message = message2;
return this;
}
public MakeDialog setPositiveButtonMessage(String message){
positiveButtonValue = message;
return this;
}
public MakeDialog setNegativeButtonMessage(String message){
negativeButtonValue = message;
return this;
}
public void show(){
showDialog(activity.getFragmentManager(), DIALOG_ID);
}
public MakeDialog setPositiveCallBack(PositiveCallback pcb){
this.positiveCallBack = pcb;
return this;
}
public MakeDialog setNegativeCallBack(NegativeCallback ncb){
this.negativeCallBack = ncb;
return this;
}
public interface PositiveCallback {
public void doPositiveCallback();
}
public interface NegativeCallback {
public void doNegativeCallback();
}
}
}
Calling it like this:
new MessageDialogFragment2.MakeDialog(this)
.setTitle(getResources().getString(R.string.exit_title))
.setMessage(getResources().getString(R.string.exit_message))
.setPositiveButtonMessage(getResources().getString(R.string.yes))
.setNegativeButtonMessage(getResources().getString(R.string.no))
.setPositiveCallBack(new MessageDialogFragment2.MakeDialog.PositiveCallback() {
#Override
public void doPositiveCallback() {
doSomething();
}
})
.show();
Expected result: doSomething() gets called
Actual result: doSomething() not being called.
PS: Any problem detected in the code not question related can be pointed out. I'm always up for improving my knowledge and write better code!
Obviously, there was something missing. I was not calling the callback inside my DialogFragment class!
#OnClick({R.id.positiveButton, R.id.negativeButton})
public void exit(View view){
switch(view.getId()){
case R.id.positiveButton:
if(positiveCallBack == null)
MessageDialogFragment2.this.dismiss();
else
positiveCallBack.doPositiveCallback();
break;
case R.id.negativeButton:
if(negativeCallBack == null)
MessageDialogFragment2.this.dismiss();
else
negativeCallBack.doNegativeCallback();
break;
}
}
I am trying to implement callback between AsyncTask and Fragment but cannot find correct info how to do it. The issue is that all callback implementations are between activity and asynctask but I need between fragment and asynctask. Could someone give me small working example how to implement it without activity.
My action structure: Fragment call DialogFragment -> choose something and send server request to async task -> async task process everything and update view and some variables. My main problem is that I call prepareData() only once in onCreate and when I walk between other fragment and returns come back I see old data. That is to say there is not enough to update only view in onPost of asynctask. It will be good to have callback which will update the whole variables.
public class TermsAndConditionsFragment extends SherlockFragment implements OnClickListener, OnTouchListener, OnItemClickListener, onTaskListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fm = getSherlockActivity().getSupportFragmentManager();
prepareData();
}
public void prepareData() {
termsAndConditionsM = new TermsAndConditionsManager(getSherlockActivity());
termsAndConditions = termsAndConditionsM.getTermsAndConditions();
if (termsAndConditions != null) {
int totalPayments = Integer.valueOf(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
if (totalPayments > 0) {
paymentsData = termsAndConditionsM.getpayments();
if (paymentsData != null) {
payments = new ArrayList<Payment>();
for (int i = 1; i <= totalPayments; i++) {
paymentValues = new Payment();
paymentValues.setPaymentID(Integer.valueOf(paymentsData.get(ServerAPI.PAYMENT_NO + "_" + i)));
paymentValues.setPaymentDate(paymentsData.get(ServerAPI.PAYMENT_DATE + "_" + i));
paymentValues.setPaymentTotalAmount(paymentsData.get(ServerAPI.PAYMENT_TOTAL + "_" + i));
payments.add(paymentValues);
}
}
}
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = init(inflater, container);
if (payments != null || termsAndConditions != null)
updateTermsAndConditionsView();
return rootView;
}
private View init(LayoutInflater inflater, ViewGroup container) {
rootView = inflater.inflate(R.layout.fragment_terms_and_conditions, container, false);
...
return rootView;
}
public void updateTermsAndConditionsView() {
etHowMuch.setText("£" + termsAndConditions.get(ServerAPI.AMOUNT_OF_CREDIT));
etForHowLong.setText(Helpers.ConvertDays2Date(Integer.valueOf(termsAndConditions.get(ServerAPI.TERM_OF_AGREEMENT_IN_DAYS))));
PaymentAdapter adapter = new PaymentAdapter(getSherlockActivity(), R.layout.custom_loan_item, payments);
lvPayments.setAdapter(adapter);
tvNoOfPayments.setText(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
tvFirstPayment.setText(termsAndConditions.get(ServerAPI.FIRST_PAYMENT_DATE));
tvTotalRepayable.setText("£" + termsAndConditions.get(ServerAPI.TOTAL_REPAYABLE));
}
#Override
public void onClick(View v) {
ft = fm.beginTransaction();
howMuch = etHowMuch.getText().toString();
forHowLong = etForHowLong.getText().toString();
switch (v.getId()) {
case R.id.etHowMuch:
f = new NumberPaymentsPickerFragment();
args = new Bundle();
args.putInt(Const.HOW_MUCH, Integer.valueOf(howMuch.replace("£", "")));
args.putDouble(ServerAPI.PAYMENT_STEP, Const.PAYMENT_STEP);
args.putString(Const.STATE, ServerAPI.TERMS_AND_CONDITIONS);
f.setArguments(args);
f.setTargetFragment(this, DIALOG_FRAGMENT);
f.show(getActivity().getSupportFragmentManager(), Const.HOW_MUCH);
break;
case R.id.etForHowLong:
f = new NumberPaymentsPickerFragment();
args = new Bundle();
args.putInt(Const.FOR_HOW_LONG, Integer.valueOf(Helpers.ConvertDate2Days(forHowLong)));
args.putDouble(ServerAPI.PAYMENT_STEP, Const.PAYMENT_STEP);
args.putString(Const.STATE, ServerAPI.TERMS_AND_CONDITIONS);
f.setArguments(args);
f.setTargetFragment(this, DIALOG_FRAGMENT);
f.show(getActivity().getSupportFragmentManager(), Const.FOR_HOW_LONG);
break;
case R.id.tvPersonalDetails:
sfm.saveCurFragment(ServerAPI.PERSONAL_DETAILS, 0);
ft.replace(android.R.id.content, new PersonalDetailsFragment(), ServerAPI.PERSONAL_DETAILS).addToBackStack(null).commit();
break;
case R.id.tvAgreementDetails:
sfm.saveCurFragment(ServerAPI.AGREEMENT_DETAILS, 0);
ft.replace(android.R.id.content, new AgreementDetailsFragment(), ServerAPI.AGREEMENT_DETAILS).addToBackStack(null).commit();
break;
case R.id.bApply:
break;
}
#Override
public void onUpdateData() {
Log.d(TAG, "Update data");
}
}
DialogFragment:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
...
}
public Dialog onCreateDialog(Bundle savedInstanceState) {
...
return createDialog(v, R.string.for_how_long, etHowMuch, etForHowLong, etPromotionCode);
}
return null;
}
private Dialog createDialog(View view, int titleResID, final EditText howMuchField, final EditText forHowLongField, final EditText promotionCodeField) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(titleResID);
builder.setView(view);
builder.setPositiveButton(R.string.set, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
doShowProgress();
}
private void doShowProgress() {
ExecuteServerTaskBackground task = new
ExecuteServerTaskBackground(getActivity());
task.action = ServerAPI.GET_TERMS_AND_CONDITIONS;
onTaskListener listener = new onTaskListener() {
#Override
public void onUpdateData() {
Log.d(TAG, "Updaaate");
}
};
task.setListener(listener);
task.args = args;
task.execute();
}
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
return builder.create();
}
AsyncTask:
onTaskListener mListener;
public interface onTaskListener {
void onUpdateData();
}
public void setListener(onTaskListener listener){
mListener = listener;
}
public ExecuteServerTaskBackground(Activity activity) {
this.mActivity = activity;
this.mContext = activity.getApplicationContext();
}
#Override
protected void onPreExecute() {
pb = (ProgressBar) mActivity.findViewById(R.id.progressBar1);
pb.setVisibility(View.VISIBLE);
}
#Override
protected Void doInBackground(Void... params) {
ServerAPI server = new ServerAPI(mContext);
if (!args.isEmpty())
server.serverRequest(action, args);
else
server.serverRequest(action, null);
return null;
}
#Override
protected void onPostExecute(Void result) {
mListener.onUpdateData();
//There is I just update view but how to update whole variables throughtout callback?
// tvNoOfPayments = (TextView) mActivity.findViewById(R.id.tvNoOfPaymentsValue);
// tvFirstPayment = (TextView) mActivity.findViewById(R.id.tvFirstPaymentValue);
// tvTotalRepayable = (TextView) mActivity.findViewById(R.id.tvTotalRepayableValue);
//
// lvPayments = (ListView) mActivity.findViewById(R.id.lvData);
//
// termsConditionsM = new TermsAndConditionsManager(mContext);
//
// termsAndConditions = termsConditionsM.getTermsAndConditions();
//
// int totalPayments = Integer.valueOf(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
//
// if (totalPayments > 0) {
// if (termsAndConditions != null) {
// tvNoOfPayments.setText(termsAndConditions.get(ServerAPI.NO_OF_PAYMENTS));
// tvFirstPayment.setText(termsAndConditions.get(ServerAPI.FIRST_PAYMENT_DATE));
// tvTotalRepayable.setText("£" + termsAndConditions.get(ServerAPI.TOTAL_REPAYABLE));
// }
//
// paymentsData = termsConditionsM.getpayments();
//
// if (paymentsData != null) {
// Log.d(TAG, paymentsData.toString());
//
// payments = new ArrayList<Payment>();
//
// for (int i = 1; i <= totalPayments; i++) {
// paymentValues = new Payment();
// paymentValues.setPaymentID(Integer.valueOf(paymentsData.get(ServerAPI.PAYMENT_NO + "_" + i)));
// paymentValues.setPaymentDate(paymentsData.get(ServerAPI.PAYMENT_DATE + "_" + i));
// paymentValues.setPaymentTotalAmount(paymentsData.get(ServerAPI.PAYMENT_TOTAL + "_" + i));
// payments.add(paymentValues);
// }
//
// PaymentAdapter adapter = new PaymentAdapter(mContext, R.layout.custom_loan_item, payments);
// lvPayments.setAdapter(adapter);
// }
//
}
pb.setVisibility(View.GONE);
super.onPostExecute(result);
}
Without taking your code in consideration I will post the most essential to make a functional callback.
TestFragment:
public class TestFragment extends Fragment {
/* Skipping most code and I will only show you the most essential. */
private void methodThatStartsTheAsyncTask() {
TestAsyncTask testAsyncTask = new TestAsyncTask(new FragmentCallback() {
#Override
public void onTaskDone() {
methodThatDoesSomethingWhenTaskIsDone();
}
});
testAsyncTask.execute();
}
private void methodThatDoesSomethingWhenTaskIsDone() {
/* Magic! */
}
public interface FragmentCallback {
public void onTaskDone();
}
}
TestAsyncTask:
public class TestAsyncTask extends AsyncTask<Void, Void, Void> {
private FragmentCallback mFragmentCallback;
public TestAsyncTask(FragmentCallback fragmentCallback) {
mFragmentCallback = fragmentCallback;
}
#Override
protected Void doInBackground(Void... params) {
/* Do your thing. */
return null;
}
#Override
protected void onPostExecute(Void result) {
mFragmentCallback.onTaskDone();
}
}