In my app I have implemented this custom dialog (which has a fairly complex layout) by extending DialogFragment. I expect this dialog to pop up when I click a button in my layout. (Which I have successfully achieved). But the problem is that the dialog shows up in a janky manner.
My custom dialog class:
public class CustomizeDialog extends DialogFragment implements AdapterView.OnItemSelectedListener {
// field declarations go here
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.customize_dialog, null);
builder.setView(view)
.setTitle("Customize")
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setPositiveButton("Let's go!", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction("fromDialog");
intent.putExtra("ratio",getRatio(paperSizeSpinner.getSelectedItem().toString()));
if(isOrientationSpinnerVisible){
intent.putExtra("isCustom",false);
intent.putExtra("orientation",orientationSpinner.getSelectedItem().toString());
} else {
intent.putExtra("isCustom",true);
}
intentProvider.getIntent(intent);
}
});
widthEditText = view.findViewById(R.id.width_et);
heightEditText = view.findViewById(R.id.height_et);
widthEditText.setEnabled(false);
heightEditText.setEnabled(false);
paperSizeSpinner = view.findViewById(R.id.paper_size_spinner);
orientationSpinner = view.findViewById(R.id.orientation_spinner);
// ArrayList for populating paperSize spinner via paperSizeAdapter
ArrayList<String> paperSizes = new ArrayList<>();
paperSizes.add("A0");
paperSizes.add("A1");
paperSizes.add("A2");
paperSizes.add("A3");
paperSizes.add("A4");
paperSizes.add("A5");
paperSizes.add("Custom");
// ArrayList for populating orientation spinner via orientationAdapter
ArrayList<String> orientation = new ArrayList<>();
orientation.add("Portrait");
orientation.add("Landscape");
// arrayAdapters containing arraylists to populate spinners
ArrayAdapter paperSizeAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_spinner_dropdown_item, paperSizes);
ArrayAdapter orientationAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_spinner_dropdown_item, orientation);
paperSizeSpinner.setAdapter(paperSizeAdapter);
orientationSpinner.setAdapter(orientationAdapter);
paperSizeSpinner.setSelection(4);
paperSizeSpinner.setOnItemSelectedListener(this);
orientationSpinner.setOnItemSelectedListener(this);
return builder.create();
}
// These are some important complex ui functionalities
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (parent.getId() == R.id.paper_size_spinner) {
if (position == 6) {
widthEditText.setEnabled(true);
heightEditText.setEnabled(true);
orientationSpinner.setEnabled(false);
isOrientationSpinnerVisible = false;
} else {
widthEditText.setEnabled(false);
heightEditText.setEnabled(false);
orientationSpinner.setEnabled(true);
isOrientationSpinnerVisible = true;
}
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
// interface used to communicate with the parent activity
public interface IntentProvider {
// this method is used to provide the intent to the parent activity
void getIntent(Intent intent);
}
// instantiating the interface object and throwing error if parent activity does not implement this interface
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
intentProvider = (IntentProvider) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement IntentProvider");
}
}
}
MainActivity class:
public class MainActivity extends AppCompatActivity implements CustomizeDialog.IntentProvider {
// field declarations go here
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.image);
// instantiating the dialog
final CustomizeDialog dialog = new CustomizeDialog();
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// showing the dialog on click
dialog.show(getSupportFragmentManager(),"");
}
});
}
// via this method I receive the intent from the dialog
// I know intent might not be the best option for this function but let's let it be here for now
#Override
public void getIntent(Intent intent) {
ratio = intent.getFloatExtra("ratio",3);
isCustom = intent.getBooleanExtra("isCustom",false);
orientation = intent.getStringExtra("orientation");
launchChooser();
}
}
Let me know in the comments if you want the layout code for the dialog.
What I tried:
Implementing threading so that my dialog is ready in a background thread and show it onButtonClick. But this is not allowed in general as any other thread except UI thread aren't supposed to touch UI related events.
Using onCreateView instead of onCreateDialog to inflate the layout directly.
Making the dialog a global variable, initialized it in onCreate and then show the dialog onButtonClick.
Switched to CONSTRAINT LAYOUT
Using an activity as a dialog by setting the dialog theme to the activity in the manifest file.
Launched my app in a device with better hardware than mine.
BUT NOTHING WORKED
What I want:
Why is my dialog janky? and what I need to do to make the dialog pop up faster?
In case anybody wants here's the link to my app repo on github.
AlertDialog and DialogFragment frameworks are slow because they need to some time to do calculations and fragment stuffs. So a solution to this problem is, using the Dialog framework straight away.
Use the Dialog framework's constructor to initialize a Dialog object like this:
Dialog dialog = new Dialog(context, R.style.Theme_AppCompat_Dialog);
// the second parameter is not compulsory and you can use other themes as well
Define the layout and then use dialog.setContentView(R.layout.name_of_layout).
Use dialog.findViewById(R.id.name_of_view) to reference views from the dialog's layout file
And then implement the logic just like anyone would do in an activity class. Find out the best implementation for your use case by reading the official documentation.
Related
I spent all day trying to make this up, but I can't..
This is the problem: I want an yes/no AlertDialog that doesn't disappear on orientation change, so I decided to use DialogFragment.
So I prepared the code and for the first use, everything with it is just perfect, but if I hit the button (that should show the dialog) once more (second, third and further times) the dialog doesn't show up! Though I can see from logs it actually makes instances and I have no errors, it's there, I just can't see it!
If I fold the app, or turn off / on the screen (I believe it's about calling onResume() method) the dialogs shows up, all of them (depending how much time I hit the button), it seems like a some displaying issue or refreshing problem maybe.. I don't know, so I came here hoping to get some help.
About my code:
I have a ListView with custom adapter, and in that adapter I have the code to show the an AlertDialog (DialogFragment) - as part of an ImageButton onClickListener.
The code for DialogFragment that I use:
public static class cMyDialogFragment extends DialogFragment {
public static cMyDialogFragment newInstance(int title) {
cMyDialogFragment frag = new cMyDialogFragment();
Bundle args = new Bundle();
args.putInt("title", title);
frag.setArguments(args);
return frag;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
int title = getArguments().getInt("title");
this.setCancelable(true);
setRetainInstance(true);
return new AlertDialog.Builder(getActivity())
// .setIcon(R.drawable.alert_dialog_icon)
.setTitle(title)
.setPositiveButton(R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((ActAudiorecords) getActivity()).doPositiveClick();
}
}
)
.setNegativeButton(R.string.no,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((ActAudiorecords) getActivity()).doNegativeClick();
}
}
)
.create();
}
}
The code for calling the dialog to show up (within the custom ListView adapter):
public View getView(final int position, View convertView, ViewGroup parent) {
View vi = convertView;
if (vi == null)
vi = inflater.inflate(R.layout.recordings_row, null);
TextView tvDate = (TextView) vi.findViewById(R.id.tv_Recordings_r_date);
tvDate.setText(ainfo.get(position).getDate());
ImageButton ibtn_play = (ImageButton) vi.findViewById(R.id.ibtnPlay);
final String localPath = dPath + File.separator + ainfo.get(position).getFName();
ImageButton ibtn_remove = (ImageButton) vi.findViewById(R.id.ibtnRecordings_r_remove);
ibtn_remove.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
curFName = ainfo.get(position).getFName();
curID = ainfo.get(position).getID();
showDialog();
}
});
ibtn_play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
play(localPath);
}
});
return vi;
}
The additional functions:
void showDialog() {
DialogFragment newFragment = cMyDialogFragment.newInstance(
R.string.do_you_want_to_remove_the_file);
newFragment.show(getFragmentManager(), "dialog");
}
public void doPositiveClick() {
// Do stuff here.
ps_db.delete(const_audiorecords_tname, "id = " + curID, null);
new File(dPath + File.separator + curFName).delete();
Toast.makeText(ActAudiorecords.this, getString(R.string.audiorecord_has_been_removed), Toast.LENGTH_LONG).show();
ActAudiorecords.this.onCreate(null); //Restarting the Activity to refresh the LV
Log.i("FragmentAlertDialog", "Positive click!");
}
public void doNegativeClick() {
// Do stuff here.
Toast.makeText(ActAudiorecords.this, getString(R.string.the_operation_has_been_cancelled), Toast.LENGTH_LONG).show();
Log.i("FragmentAlertDialog", "Negative click!");
}
I have no onResume() in my code.
I tried to use different codes for DialogFragment but it doesn't matter.
It was all due to the line:
ActAudiorecords.this.onCreate(null);
So after calling onCreate() with null as savedInstance it have been removing link to the DialogFragment (as I can understand), it was the line for refreshing the Activity, I solved the problem by splitting the code in onCreate() to which should be called only once (at a start of Activity) and the part that should be called in every refreshing point (such as GUI settings and etc).
I believe I could also save the current Bundle and pass it to onCreate() instead of null and it would work as good as now, but I thought that calling an function is much better for data updating than calling onCreate() over and over, so that's it, thank you all who wanted to help.
i have an activity that when user click on button , a dialog open. in this dialog there is a spinner that have 3 choices: Blue,Red,Green. and there is a submit button. i want that when user select a color and click on submit, in caller activity, its String color set to selected color in dialog. i try this: but not worked. please help me....
String color;
String dialogColor;
showDialog.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.custom_dialog);
dialog.setTitle("my dialog");
Spinner spinner = (Spinner) dialog.findViewById(R.id.spinner);
final TextView status = (TextView) dialog.findViewById(R.id.status);
Button submit = (Button) dialog.findViewById(R.id.submit);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
dialogColor = parent.getItemAtPosition(position).toString();
status.setText("Color is: "+dialogColor);
color = dialogColor;
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
submit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("Color",dialogColor);
dialog.dismiss();
}
});
dialog.show();
}
});
i use both of direct and with intent ways to assign my color String to selected value. but not worked. where i have mistake?
I think the best way to create custom dialogs now is the Dialog Fragment, because the simple dialog it's limited. For example it's the way to create a dialogs with material design. And you have a differents ways to take info from dialog fragment, the first and the second for example.
This is basic code to create a dialog fragment:
//Method to call and start dialog fragment class
public void ShowPhotoFilesDialog(Activity context,File photo){
//Declaration of classes
Custom_DialogFragment custom_dialogFragment = new Custom_DialogFragment ();
FragmentManager fragmentManager = context.getFragmentManager();
// The device is using a large layout, so show the fragment as a dialog
custom_dialogFragment.show(fragmentManager, "dialog");
}
And this is the basic dialog fragment class:
public class Custom_DialogFragment extends DialogFragment {
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
try {
// The only reason you might override this method when using onCreateView() is
// to modify any dialog characteristics. For example, the dialog includes a
// title by default, but your custom layout might not need it. So here you can
// remove the dialog title, but you must call the superclass to get the Dialog.
Dialog dialog = super.onCreateDialog(savedInstanceState);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
//To hide action bar from layout
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
//Declaration of controls
View v = getActivity().getLayoutInflater().inflate(R.layout.my_custom_layout);
builder.setView(v);
//My code
return builder.create();
}
catch (Exception ex){
Log.e("-- Custom_DialogFragment.onCreateDialog --","",ex);
return null;
}
}
}
Tell me if I helped you, good programming!
The way my dialog is set up currently, it is supposed to take the values from the EditTexts and save in into a database (a process simplified through Sugar ORM), then place the newly created SubjectInfo object into the RecyclerView. The way the notifyDataSetChanged(); is included gives me errors concerning the Thread (basically no thread is waiting upon the change in the data set). SO, I have two paths as I see it, but I'm still confused as to how each approach would work.
Option 1: Somehow revoke the onCreate() method in the SubjectManagerActivity so that the adapter responds to the new database. (How to revoke an onCreate method?)
Option 2: Create a custom Dialog Fragment Activity. Does this navigate back up to recreate the parent activity?
Please help explain how to make the notifyDataSetChanged(); respond, because once that line is removed, there are no errors, but I can't see the new Subject card until I restart the app.
Here is my code:
public class SubjectManagerActivity extends ActionBarActivity {
public static ArrayList<SubjectInfo> subjectList = new ArrayList<SubjectInfo>();
public static FloatingActionButton fabCreateSubject;
private AlertDialog.Builder build;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_subject_manager);
RecyclerView recList = (RecyclerView) findViewById(R.id.subject_card_list);
recList.setHasFixedSize(true);
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
recList.setLayoutManager(llm);
subjectList = getSubjectInfoArrayList();
SubjectAdapter sa = new SubjectAdapter(subjectList);
recList.setAdapter(sa);
fabCreateSubject = (FloatingActionButton) findViewById(R.id.fab_create_subject);
fabCreateSubject.setOnClickListener (new View.OnClickListener() {
#Override
public void onClick(View v) {
build = new AlertDialog.Builder(SubjectManagerActivity.this);
LayoutInflater inflater = getLayoutInflater();
View alertview = inflater.inflate(R.layout.create_subject_dialog, null);
// Pass null as the parent view because its going in the dialog layout
build.setView(alertview);
final EditText inputSubjectName = (EditText) alertview.findViewById(R.id.dialog_edit_subject_card_name);
final EditText inputSubjectGrade = (EditText) alertview.findViewById(R.id.dialog_edit_subject_card_grade);
build.setTitle("Add Subject")
.setPositiveButton("Save", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
String enteredSubjectName = inputSubjectName.getText().toString();
boolean enteredSubjectIsArchived = false;
if((!(inputSubjectName.getText().toString().equals("")))) {
SubjectInfo si = new SubjectInfo(enteredSubjectName, enteredSubjectIsArchived);
si.save();
}
dialog.cancel();
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = build.create();
alert.show();
}
});
}
public ArrayList<SubjectInfo> getSubjectInfoArrayList(){
ArrayList<SubjectInfo> sial= new ArrayList<SubjectInfo>();
List<SubjectInfo> sil = SubjectInfo.listAll(SubjectInfo.class);
sial.addAll(sil);
notifyDataSetChanged;
return sial;
}
It turns out often the most effective way to deal with an issue is to trust in your code and research to make it work! For anyone else with a similar issue, I included this code:
si.save();
subjectList.add(si);
sa.notifyDataSetChanged();
The method notifyDataSetChanged(); had to be directed to an adapter, which is the container of the Thread. Though the adapter isn't being populated from the getSubjectInfoArrayList() method in real-time (when the dialog is prompted), the ArrayList is still acquiring the same value and when the app is reopened, it can populate the adapter from the database (and in turn, the same get( ) method).
I am trying to implement custom DialogFragment. But when I try to show it I am getting NullPointerException. Also as I have noticed onCreateDialog is never implictly called.
What is wrong with it. I have read official manual, and followed all steps in it DialogFragment
Here is my code for custom Dialog Fragment
public class UserInputDialogFragment extends DialogFragment {
InputDialogListener mListener;
private EditText mTextEdit;
public UserInputDialogFragment() {
super();
}
// Use this instance of the interface to deliver action events
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
View mainView = inflater.inflate(R.layout.dialog_input, null);
builder.setView(mainView);
mTextEdit = (EditText) mainView.findViewById(R.id.user_input);
if (mTextEdit==null) {
Log.e("ERROR","Text edit is null");
}
// Add action buttons
builder.setPositiveButton(R.string.ok_btn, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
mListener.onDialogPositiveClick(UserInputDialogFragment.this,mTextEdit.getText().toString());
}
});
builder.setNegativeButton(R.string.cancel_bnt, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mListener.onDialogNegativeClick(UserInputDialogFragment.this,mTextEdit.getText().toString());
UserInputDialogFragment.this.getDialog().cancel();
}
});
return builder.create();
}
public interface InputDialogListener {
public void onDialogPositiveClick(DialogFragment dialog, String userInput);
public void onDialogNegativeClick(DialogFragment dialog, String userInput);
}
public void showAndAddHint(FragmentManager manager,String tag,String hint) {
this.onCreateDialog(null);
mTextEdit.setHint(hint);
this.show(manager,tag);
}
#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 = (InputDialogListener) activity;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(activity.toString()
+ " must implement InputDialogListener");
}
}
}
And I am trying to show dialog this way.
UserInputDialogFragment userInputDialogFragment = new UserInputDialogFragment();
userInputDialogFragment.showAndAddHint(getFragmentManager(),"Please enter phone number",task.phoneNumber);
And here is NullPointerException mTextEdit is null.
public void showAndAddHint(FragmentManager manager,String tag,String hint) {
this.onCreateDialog(null);
mTextEdit.setHint(hint);
this.show(manager,tag);
}
The showAndAddHint method won't work as written. What you should do instead is:
1 - Set a member variable mHint = hint;
2 - Call show() exactly the way you're doing it now.
3 - Read the member variable mHint in on create dialog and use it to set the edit text hint.
Don't call onCreateDialog explicitly because the show method does that for you when needed.
Is it possible to make an 'AlertDialog-like display' that has rating bar inside it when a button is clicked? If yes, will I able to get the value of the rating that is entered? Thanks.
Here is some code I used in an application to show a dialog fragment and get a selection out of it:
public class FeedChooserFragment extends DialogFragment {
/**
* Implement this interface if the activity needs to do something
* after the dialog has been dismissed.
*/
public interface FeedChooserListener {
public void onFeedSelected(NewsFeed feed, Object userData);
}
/**
* Create a new instance of the fragment
*/
public static FeedChooserFragment newInstance(Serializable userData) {
FeedChooserFragment f = new FeedChooserFragment();
Bundle args = new Bundle();
args.putSerializable(Extra.USER_DATA, userData);
f.setArguments(args);
return f;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// This is a list of items, but it could be a custom dialog showing some rating bar
BaseActivity a = (BaseActivity) getActivity();
List<NewsFeed> feed = a.getDataCache().getAllNewsFeed();
adapter = new FeedAdapter(a);
adapter.addAll(feed);
// Here you would create a custom dialog, find the rating bar in the inflated view
// and keep it ready for when the dialog gets dismissed.
AlertDialog.Builder builder = new AlertDialog.Builder(a);
// Here you would set the button listeners instead of the listview listener
builder.setAdapter(adapter, dialogClickListener);
return builder.create();
}
private OnClickListener dialogClickListener = new OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
NewsFeed selectedFeed = adapter.getItem(which);
// This is where we try to notify the calling activity or fragment
if (getActivity() instanceof FeedChooserListener) {
((FeedChooserListener) getActivity()).onFeedSelected(selectedFeed, userData);
}
if (getTargetFragment() instanceof FeedChooserListener) {
((FeedChooserListener) getTargetFragment()).onFeedSelected(selectedFeed, userData);
}
dialog.dismiss();
}
};
private Object userData;
private FeedAdapter adapter;
}
Yes, Its possible, Try something like this...
PopupWindow pw;
//We need to get the instance of the LayoutInflater, use the context of this activity
LayoutInflater inflater = (LayoutInflater) TouchPaint.this
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//Inflate the view from a predefined XML layout
View layout = inflater.inflate(R.layout.popup,
(ViewGroup) findViewById(R.id.popup_element));
//popup : name of the XML file which includes the popup_element(can be a linear layout which includes the rating bar)
pw = new PopupWindow(layout,70, 220, true);
pw.showAtLocation(layout, Gravity.LEFT,100,200);
rb =(RatingBar)layout. findViewById(R.id.RatingBar);
rb.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// To Do code
pw.dismiss();
}
});