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);
}
}
Related
My problem is that i have a recyclerview implemented on a fragment that uses Room. RecyclerView, Adapter and Database need to be together in one class (i guess).
My problem is that if I put them into StudentActivity,
recyclerView = findViewById(R.id.SubjectRecyclerView);
will be null, because the Fragment won't be available by the time this function called.
If i put them into StudentSubjectsFragment,
everything would be good, but NewSubjectDialogFragment tells me that StudentActivity needs to implement NewSubjectDialogFragment because of this:
private NewSubjectDialogListener listener;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentActivity activity = getActivity();
if (activity instanceof NewSubjectDialogListener) {
listener = (NewSubjectDialogListener) activity;
} else {
throw new RuntimeException("Activity must implement the NewSubjectDialogListener interface!");
}
}
If at the
FragmentActivity activity = getActivity();
line somehow I would be able to call the getActivity() function of StudentSubjectsFragment class, I think it would solve my problem, and then the class that would implement the NewSubjectDialogFragment would be the StudentSubjectsFragment instead of StudentActivity.
I tried this:
FragmentActivity activity = getActivity().getSupportFragmentManager().findFragmentByTag("StudentSubjectsFragment").getActivity();
But activity is null.
StudentSubjectsFragment class:
public class StudentSubjectsFragment extends Fragment
implements NewSubjectDialogFragment.NewSubjectDialogListener,
SubjectAdapter.SubjectClickListener {
public static final String TAG = "StudentSubjectsFragment";
private RecyclerView recyclerView;
private SubjectAdapter adapter;
private SubjectDatabase database;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.student_subjects, container, false);
FloatingActionButton fab = rootView.findViewById(R.id.fabbb);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new NewSubjectDialogFragment().show(getActivity().getSupportFragmentManager(), NewSubjectDialogFragment.TAG);
}
});
database = Room.databaseBuilder(
getActivity(),
SubjectDatabase.class,
"subject-list"
).build();
recyclerView = rootView.findViewById(R.id.SubjectRecyclerView);
adapter = new SubjectAdapter(this);
loadItemsInBackground();
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setAdapter(adapter);
return rootView;
}
private void loadItemsInBackground() {
new AsyncTask<Void, Void, List<Subject>>() {
#Override
protected List<Subject> doInBackground(Void... voids) {
return database.subjectDao().getAll();
}
#Override
protected void onPostExecute(List<Subject> subjects) {
adapter.update(subjects);
}
}.execute();
}
#Override
public void onItemChanged(final Subject item) {
new AsyncTask<Void, Void, Boolean>() {
#Override
protected Boolean doInBackground(Void... voids) {
database.subjectDao().update(item);
return true;
}
#Override
protected void onPostExecute(Boolean isSuccessful) {
Log.d("StudentActivity", "Subject update was successful");
}
}.execute();
}
#Override
public void onItemDeleted(final Subject item) {
new AsyncTask<Void, Void, Boolean>() {
#Override
protected Boolean doInBackground(Void... voids) {
database.subjectDao().deleteItem(item);
return true;
}
}.execute();
}
#Override
public void onSubjectCreated(final Subject newItem) {
new AsyncTask<Void, Void, Subject>() {
#Override
protected Subject doInBackground(Void... voids) {
newItem.id = database.subjectDao().insert(newItem);
return newItem;
}
#Override
protected void onPostExecute(Subject subject) {
adapter.addItem(subject);
}
}.execute();
}
}
StudentActivity class:
public class StudentActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_student);
ViewPager vpProfile = findViewById(R.id.vpStudent);
vpProfile.setAdapter(new StudentPagerAdapter(getSupportFragmentManager()));
}
}
NewSubjectDialogFragment class:
public class NewSubjectDialogFragment extends DialogFragment {
private EditText nameEditText;
public static final String TAG = "NewSubjectDialogFragment";
public interface NewSubjectDialogListener {
void onSubjectCreated(Subject newItem);
}
private NewSubjectDialogListener listener;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentActivity activity = getActivity().getSupportFragmentManager().findFragmentByTag("StudentSubjectsFragment").getActivity();
if (activity instanceof NewSubjectDialogListener) {
listener = (NewSubjectDialogListener) activity;
} else {
throw new RuntimeException("Activity must implement the NewSubjectDialogListener interface!");
}
}
private boolean isValid() {
return nameEditText.getText().length() > 0;
}
private Subject getSubject() {
Subject subject = new Subject();
subject.name = nameEditText.getText().toString();
return subject;
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(requireContext())
.setTitle(R.string.new_subject_item)
.setView(getContentView())
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
if (isValid()) {
listener.onSubjectCreated(getSubject());
}
}
})
.setNegativeButton(R.string.cancel, null)
.create();
}
private View getContentView() {
final View contentView = LayoutInflater.from(getContext()).inflate(R.layout.dialog_new_subject_item, null);
nameEditText = contentView.findViewById(R.id.SubjectNameEditText);
return contentView;
}
}
I think there is a way to change NewSubjectDialogFragment to work with StudentSubjectsFragment but I am a beginner in android developing and don't know how to do it.
Solution of Mike M. worked perfectly.
The working code parts are the following:
NewSubjectDialogFragment:
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Fragment fragment = getParentFragment();
if (fragment instanceof NewSubjectDialogListener) {
listener = (NewSubjectDialogListener) fragment;
} else {
throw new RuntimeException("Fragment must implement the NewSubjectDialogListener interface!");
}
}
StudentSubjectFragment:
FloatingActionButton fab = rootView.findViewById(R.id.fabbb);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new NewSubjectDialogFragment().show(getChildFragmentManager(), NewSubjectDialogFragment.TAG);
}
});
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.
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.
This is a very strange behavior and I don't know how to fix it.
I have an Activity as a Presenter (In a MVP Architecture).
When the activity starts, I attach a Fragment as a View. The fragment itself is very simple.
public class CurrentSaleFragment extends BaseFragment {
private MainMVP.SalesPresenterOps salesPresenterOps;
private SaleAdapter adapter;
private ListView lv;
#BindView(R.id.btn_sell)
FloatingActionButton btnAdd;
public static CurrentSaleFragment newInstance(){
CurrentSaleFragment fragment = new CurrentSaleFragment();
Bundle arguments = new Bundle();
arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_quick_sale );
fragment.setArguments(arguments);
return fragment;
}
#Override
protected void init() {
super.init();
lv = (ListView)view.findViewById(R.id.lv_sale);
}
#OnClick(R.id.btn_sell)
public void addToSale(View view){
mPresenter.moveToFragment(SellProductFragment.newInstance());
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
salesPresenterOps = (MainMVP.SalesPresenterOps)context;
}
#Override
public void onDetach() {
salesPresenterOps = null;
super.onDetach();
}
}
The BaseFragment from which this fragmend extends :
public class BaseFragment extends Fragment implements MainMVP.RequiredViewOps, View.OnClickListener,
LoaderRequiredOps{
protected View view;
protected MainMVP.PresenterOps mPresenter;
protected final static String LAYOUT_RES_ID = "layout_res_id";
#Override
public void showOperationResult(String message, final long rowId) {
Snackbar.make(view, message, Snackbar.LENGTH_LONG).setAction(
R.string.see, new View.OnClickListener() {
#Override
public void onClick(View v) {
onOperationResultClick(rowId);
}
}
).show();
}
#Override
public void showSnackBar(String msg) {
Snackbar.make(view, msg, Snackbar.LENGTH_SHORT).show();
}
#Override
public void showAlert(String msg) {}
protected void onOperationResultClick(long rowId){}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mPresenter = (MainMVP.PresenterOps)context;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
this.view = inflater.inflate(getArguments().getInt(LAYOUT_RES_ID), null);
init();
return view;
}
protected void addToClickListener(View ... params){
for (View v : params){
v.setOnClickListener(this);
}
}
protected void init() {
if (view != null){
ButterKnife.bind(this, view);
}
}
#Override
public void onDetach() {
mPresenter = null;
Log.d(getClass().getSimpleName(), "Fragment was detached");
super.onDetach();
}
#Override
public void onClick(View v) {}
#Override
public void onPreLoad() {
Dialogs.buildLoadingDialog(getContext(), "Loading...").show();
}
#Override
public void onLoad() {}
#Override
public void onDoneLoading() {
Dialogs.dismiss();
}
}
When I enter the method 'moveToFragment()' I just replace CurrentSaleFragment for a new Fragment:
protected void addFragment(BaseFragment fragment){
mView = fragment;
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_holder,
fragment, null).addToBackStack(null).commit();
}
Then the new fragment is attached:
public class SellProductFragment extends BaseFragment{
private ListView listView;
private ProductListAdapter adapter;
private MainMVP.SalesPresenterOps mSalesPresenter;
public static SellProductFragment newInstance(){
SellProductFragment fragment = new SellProductFragment();
Bundle arguments = new Bundle();
arguments.putInt(LAYOUT_RES_ID, R.layout.fragment_inventory);
fragment.setArguments(arguments);
return fragment;
}
private void reload(){
final Loader loader = new Loader(this);
loader.execute();
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
mSalesPresenter = (MainMVP.SalesPresenterOps)context;
}
#Override
protected void init() {
super.init();
listView = (ListView)view.findViewById(R.id.lv_inventory);
reload();
FloatingActionButton button = (FloatingActionButton)view.findViewById(R.id.btn_add);
addToClickListener(button);
}
#Override
public void onLoad() {
adapter = new ProductListAdapter(getActivity().getApplicationContext(), R.layout.row_product_item,
mSalesPresenter.getProducts());
try{
updateListView();
}catch (Exception e){
Log.w(getClass().getSimpleName(), e.getMessage());
}
}
private void updateListView(){
if (adapter != null && listView != null){
listView.setAdapter(adapter);
}else{
throw new RuntimeException();
}
}
}
See that This fragment also extends from BaseFragment and implements LoaderRequiredOps. The interface is used to 'load' any data. It adds a dialog and updated the adapter when the loading is done:
public class Loader extends AsyncTask<Void, Void, Void> {
private LoaderRequiredOps presenter;
public Loader(LoaderRequiredOps presenter){
this.presenter = presenter;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
presenter.onPreLoad();
}
#Override
protected Void doInBackground(Void... params) {
presenter.onLoad();
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
presenter.onDoneLoading();
presenter = null;
}
}
Now, when I try to execute the method reload() from the SellProductFragment i get the 'Only the original thread that created a view hierarchy can touch its views.'
This does not happen if the SellProductFragment is attached first instead of CurrentSaleFragment.
What is happening here?
Your Async Loader class calls the presenters method onLoad() from a background thread during doInBackground().
My guess is that in the onLoad() method of the presenter, a view is referenced.
In order to change the view at this point, post the view logic as a Runnable to the UI thread (you said your presenter is the activity, so this should be possible from the onLoad method).
#Override
public void onLoad() {
runOnUiThread(new Runnable() {
#Override
public void run() {
// Your ui code here...
}
});
// Rest of your code here...
}
For an unknown reason, an unidentified configuration allows to execute the setting of an adapter for a ListView on the doInBackground() method.
Moved it to onPostExecute() and now it's working
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;
}
}