I've coded a custom dialog for my application but once I try to open it, I got a lag and the following message is displayed in the logcat console:
Skipped 31 frames! The application may be doing too much work on its main thread.
To lower the work, I've moved the Typeface loading to an AsyncTask but it doesn't solve my problem.
public class CustomDialog
{
private Context context = null;
private Dialog dialog = null;
private boolean cancelable = true;
private OnCancelListener cancelListener = null;
private Typeface latoFont = null;
private RelativeLayout layout_root = null;
private TextView text_title = null;
private TextView text_message = null;
private LinearLayout layout_buttons = null;
public CustomDialog(Context context)
{
this.context = context;
this.dialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar);
this.dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.dialog.setContentView(R.layout.dialog);
this.dialog.setCancelable(true);
layout_root = (RelativeLayout) dialog.findViewById(R.id.layout_root);
text_title = (TextView) dialog.findViewById(R.id.text_title);
text_message = (TextView) dialog.findViewById(R.id.text_message);
layout_buttons = (LinearLayout) dialog.findViewById(R.id.layout_buttons);
// Set background color
layout_root.setBackgroundColor(FlatUtils.transparentDark(FlatConstants.DOMIANT_COLOR));
// Set font
new TypeFaceTask(context, new TextView[]{text_title, text_message}).execute();
// Attach listener
layout_root.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view)
{
if (view.getId() != R.id.layout_root) return;
if (cancelable)
{
if (cancelListener != null) cancelListener.onCancel();
dialog.dismiss();
}
}
});
}
public void setTitle(String title)
{
text_title.setVisibility(View.VISIBLE);
text_title.setText(title);
}
public void setMessage(String message)
{
text_message.setVisibility(View.VISIBLE);
text_message.setText(message);
text_message.setMovementMethod(new ScrollingMovementMethod());
}
public void addButton(String value, final OnClickListener listener)
{
// Create button
FlatButton button = new FlatButton(context);
button.setText(value);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, dpToPx(40));
params.setMargins(0, dpToPx(10), 0, 0);
layout_buttons.addView(button, params);
// Set typeface
if (latoFont != null) button.setTypeface(latoFont);
// Attach listener
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view)
{
if (listener != null) listener.onClick(view);
dialog.dismiss();
}
});
}
public void setCancelable(boolean b)
{
cancelable = b;
dialog.setCancelable(b);
}
public void setOnCancelListener(OnCancelListener listener)
{
this.cancelListener = listener;
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog)
{
cancelListener.onCancel();
}
});
}
public void show()
{
dialog.show();
}
public void dismiss()
{
dialog.dismiss();
}
public interface OnCancelListener {
public void onCancel();
}
private int dpToPx(int dp)
{
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return (int)(dp * (metrics.densityDpi / 160f));
}
}
This is way I create a new dialog:
CustomDialog dialog = new CustomDialog(TestActivity.this);
dialog.addButton("Hello 1", null);
dialog.addButton("Hello 2", null);
dialog.addButton("Hello 3", null);
dialog.setTitle("Dummy dialog");
dialog.setMessage("Plop plop");
dialog.show();
So my question is: Is there a way to optimized this code ?
First of all i think you should try extending the Dialog class instead of wrapping it like this...Your code will be cleaner and more reusable and i think will yield some performance improvement. It wont take you much, most of the code will be the same, but its a general rule of thumb if you want a custom Dialog you should extend the existing elements, similar to when you create an Activity or Fragment you extend them :).
Also i dont know what you are actually doing with your layouts but i see you are getting the screen width and height and then setting margins etc...Such calculations can cause your frames to skip and i would advise you to try making the layout through xml. Trust me, just try playing with layout params through xml and i bet you will get the same result when it comes to margins padings and the overal look, and use the different layout folders(and dimens for margins and paddings for ex) for different screen densities and sizes to achieve the same look across all devices. Not to mention this will perform faster.
Edit 1:
Typefaces wont cause such problems.
I see you said you are running this on emulator?! Well thats the issue :) i can guarantee that wont happen on a real device. :)) its a common thing on the emulator. Always develop on real devices, only they can mimic all the real problems and bugs you will encounter.
But still listen to the advice's here and follow "best practices" when programming.
is it phone or emulator? (try to run on phone)
Best practice - to extend DialogFragment(Dialog)
Adding buttons from code is strange :) You can declare yor dialog ui using XML layout, inflate it and set inflated view as content in your dialog
You don't need to load Typeface in asynctask.
So my general suggestion is to learn best practices of creating dialogs.
Related
I am using an AlertDialog to track a logFile which is updating in real time, and need to automatically scroll to the bottom of the view whenever an extra line is added.
I am able to cast the AlertDialog to a TextView (and e.g. using this TextView alter the text size) but any methods involving scrolling don't work.
Code:
LogFileView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
String logFile = "/data/data/com.test/test.log";
String logFileOutput = getFileOutput(logFile);
final AlertDialog dialog = new AlertDialog.Builder(getActivity()).setMessage(logFileOutput).show();
TextView textView = dialog.findViewById(android.R.id.message);
textView.setTextSize(8);
textView.scrollTo(0, textView.getLineCount());
} catch (IOException e) {
e.printStackTrace();
}
}
});
textView.setTextSize(8); will alter the text size on display
textView.scrollTo(0, textView.getLineCount()); will do nothing, and the alert dialog, despite having a scrollbar available, will remain focussed on the first line
Update 1:
I see there are a few requests for the dialog creation code/errors in the console output.
Firstly, I am not actually using a separate layout/class to create the dialog. It is applying the default layout associated with (android.R.id.message) to an instance of android.app.AlertDialog and is only constructed within the onClick method for the onClickListener in the code above.
Based on the feedback I've received so far, the code I've most recently attempted to use looks as follows:
LogFileView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
String logFile = "/data/data/com.test/test.log";
String logFileOutput = getFileOutput(logFile);
final AlertDialog dialog = new AlertDialog.Builder(getActivity()).setMessage(logFileOutput).show();
TextView textView = dialog.findViewById(android.R.id.message);
textView.setTextSize(8);
//textView.scrollTo(0, textView.getLineCount());
textView.setMovementMethod(new ScrollingMovementMethod());
textView.post(new Runnable() {
#Override
public void run() {
textView.scrollTo(0, textView.getLineCount());
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
});
Secondly, there is nothing appearing in the console when the scroll attempt is made - this simply gets ignored at runtime.
The default layout seemed like it would be fine for my purpose, given it appears like a blank TextView with a scrollbar attached, but I think it may be a sensible next step to use a new custom layout and add a ScrollView and see what happens
Use below code. I Tested & verified.
textView.movementMethod = ScrollingMovementMethod() // Add this
textView.text = "your text"
textView.post {
val scrollAmount = textView.layout.getLineTop(textView.lineCount) - textView.height
textView.scrollTo(0, textView.lineCount)
}
Edit:
Equivalent to java:
textView.setMovementMethod(new ScrollingMovementMethod());
textView.setText("your text");
textView.post(new Runnable() {
#Override
public void run() {
int scrollAmount = textView.getLayout().getLineTop(textView.getLineCount()) - textView.getHeight();
textView.scrollTo(0, scrollAmount);
}
});
Try to wrap it with post instead of calling directly to scrollTo,
something like this:
textView.post(new Runnable() {
#Override
public void run() {
textView.scrollTo(0, textView.getLineCount());
}
});
So I have a ListView with list-items that each have different images in them. I have my code setup so that when the user clicks an image, it will show an expanded version of that particular image by using the Dialog class.
However, no matter what code I've tried, it doesn't seem like I can make the Dialog image change! Am I not able to modify layout elements from within an adapter? I could only figure out how to reference my individual list-item images by putting the relevant code within my adapter.
What needs to change, and what am I doing wrong?
Here's the applicable code in my adapter for reference:
viewHolder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i(LOG_TAG, "Calling ImageView OnClickListener");
int imageId = currentWord.getImageResourceId();
Dialog aD = new Dialog(mContext);
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
View popupLayout = layoutInflater.inflate(R.layout.popup_image_layout, null);
ImageView popupImageView = (ImageView) popupLayout.findViewById(R.id.popup_imageView);
Glide
.with(mContext)
.load(imageId)
.apply(new RequestOptions().circleCrop())
.into(popupImageView);
aD.setContentView(R.layout.popup_image_layout);
aD.show();
}
});
Thanks for any of your help!
So I ended up figuring the answer out on my own.
Under aD.setContentView(), I should have had popupLayout as the target, which had already had R.layout.popup_image_layout assigned and inflated in the same line... By referencing the layout anew, the code wasn't actually inflating the layout, so there was nothing able to be shown.
So all that needed to be changed was: modifying aD.setContentView(R.layout.popup_image_layout) to aD.setContentView(popupLayout) and now when I click on the individual images in my ListView items, the proper image for each pops up in an expanded ImageView, which is shown by way of the Dialog class.
UPDATE:
Added some extra code in order to make sure that the Dialog is completely removed after being closed. Otherwise, it is kept in memory and the memory use continues to stack and increase indefinitely upon each subsequent Dialog being opened.
Updated code below:
Dialog aD = null;
final int imageId = currentWord.getImageResourceId();
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
viewHolder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
View popupLayout = layoutInflater.inflate(R.layout.popup_image_layout, null);
final ImageView popupImageView = (ImageView) popupLayout.findViewById(R.id.popup_imageView);
if (aD == null) {
aD = new Dialog(mContext);
aD.getWindow().setBackgroundDrawableResource(R.color.transparent);
}
Log.i(LOG_TAG, "Calling ImageView OnClickListener");
Glide
.with(mContext)
.load(imageId)
.apply(new RequestOptions().circleCrop())
.into(popupImageView);
aD.setContentView(popupLayout);
aD.show();
popupLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
aD.dismiss();
aD = null;
}
});
}
});
I have an activity and a class that implements a popup window. Using this tutorial I implemented the popup. I call the methods init() and popupInit() from the activity and everything else is in the class. My problem is that the popup does not show.
Here is the popup class:
public class PopupAudio implements OnClickListener {
LinearLayout layoutOfPopup;
PopupWindow popupMessage;
Button popRecord, popStopRecord, popPlay, popStopPlaying;
TextView popupText;
public void popupInit() {
popRecord.setOnClickListener(this);
popStopRecord.setOnClickListener(this);
popPlay.setOnClickListener(this);
popStopPlaying.setOnClickListener(this);
popupMessage = new PopupWindow(layoutOfPopup, LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
popupMessage.setContentView(layoutOfPopup);
}
public void init(Context context) {
popRecord = new Button(context);
popRecord.setId(112);
popStopRecord = new Button(context);
popPlay = new Button(context);
popStopPlaying = new Button(context);
layoutOfPopup = new LinearLayout(context);
popRecord.setText("REC");
layoutOfPopup.setOrientation(1);
layoutOfPopup.addView(popRecord);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()) {
case 112:
break;
}
}
}
It is a school project so it is very important. Please help me, I'll be grateful :)
You need to call a method to actually show the popup on some event action or whenever you need it. Here are the different methods from the docs
Here is one example of using showAtLocation().
showAsDropDown(View anchor) may be the simplest depending on your needs. Just pass it the view you want it to attach to. Though, the other two give you more flexibility on where it shows.
Here is what I am aiming for:
I am unsure if I am doing this correctly. There are probably better,more efficient, and cleaner ways to do it, but I need to know how.
This layout was designed in xml and inflated via an inflater. The produced view was then placed into an AlertDialog. Thus, this is seen as an AlertDialog by the user.
My concern is with the tags section at the bottom. I want this to work like how Tumblr tags work. Type a string, hit the button, and a button with that tag name will show up in the blank section below it.
Now, if you click on those buttons (with their respective tag names), they will disappear from the frame.
I have several concerns.
I have trouble implementing listeners. If the AddTag button creates more buttons in the (currently invisible, but present) LinearLayout, then what about the created buttons? How do those buttons implement onClick listeners that will remove themselves from the LinearLayout if they were created in some inner method defined from the AddTag button's onClick method?
I am afraid about having to declare some of these views as FINAL in order to reference them in button methods and inner classes. I am now stuck because of this.
Do I have to define my own layout for the tag buttons? You see, a LinearLayout displays things one after the other, yes? I want to try to recreate how some social networking sites do it. Fill the layout with buttons from top to bottom, left to right. If there is no room left in the current row, go to the next one and add the tag button there. It's basically a dynamic LinearLayout that has autowrapping.
If there are any better ways of implementing this, please let me know a general step by step of what to do. I have not learned Fragments yet, but I think it may be VERY applicable here. Also, should I be creating a class that extends ViewGroup, inflating the XML there, and adding helper methods to handle things? I suppose from a DialogFragment I could then addView(the class I just created) and work from there?
Here is my current code by the way. I am stuck and stumped.
/**
* Opens a view for the user to define their new action and add it to the
* dictionary.
*
* #param view
*/
public void defineNewAction(View view) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
LayoutInflater inflater = this.getLayoutInflater();
View viewToSet = inflater.inflate(
R.layout.define_new_action_window_layout,
null);
final EditText newActionName = (EditText) viewToSet
.findViewById(R.id.set_action_name);
final RadioGroup priorityGroup = (RadioGroup) viewToSet
.findViewById(R.id.radiogroup_set_priority);
final EditText goalTimeHours = (EditText) viewToSet
.findViewById(R.id.set_goal_time_hours);
final EditText goalTimeMinutes = (EditText) viewToSet
.findViewById(R.id.set_goal_time_minutes);
final EditText addTagsInput = (EditText) viewToSet
.findViewById(R.id.add_tags_input);
Button addTagButton = (Button) viewToSet.findViewById(R.id.btn_add_tags);
final ArrayList<String> tags = new ArrayList<String>();
final LinearLayout currentTagsLayout = (LinearLayout) viewToSet
.findViewById(R.id.current_tags);
addTagButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String tag = addTagsInput.getText().toString();
tags.add(tag);
Button newTag = new Button(builder.getContext());
int tagId = tag.hashCode();
if (tagId < 0)
tagId *= -1;
newTag.setId(tagId);
newTag.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Button toRemove = (Button) currentTagsLayout.findViewById(tagId);
currentTagsLayout.removeView(toRemove);
}
});
currentTagsLayout.addView(newTag);
}
});
builder.setTitle("Define your action.");
builder.setView(viewToSet);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
String name = newActionName.getText().toString();
int priority = priorityGroup.getCheckedRadioButtonId();
int goalHours = Integer
.parseInt(goalTimeHours.getText().toString());
int goalMinutes = Integer.parseInt(goalTimeMinutes.getText()
.toString());
}
});
builder.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
I have trouble implementing listeners
There's no trouble. For the functionality you are trying to achieve, you can keep adding buttons and setting OnClickListeners on them. You don't even need to give them an id, or track them in any way. The following code inside your OnClickListener will do:
newTag.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Use the View given to you
currentTagsLayout.removeView(v);
}
});
I am afraid about having to declare some of these views as FINAL
This is how Java works. I haven't noticed any crippling effects of this. You can also declare your variables as global to not have to define them as final. But I don't see why declaring them as final is an issue. Could you provide an example where this is a problem?
Do I have to define my own layout for the tag buttons?
This is something you will have to deal with yourself. It's a design decision. If you need auto-wrapping support, you can look at Android Flow Layout: Link. It's an extended LinearLayout that supports auto-wrap of its contents.
I have not learned Fragments yet, but I think it may be VERY
applicable here
I don't see why they would be.
Note/Aside: Some kind of a check here would be better:
String tag = "";
if (!addTagsInput.getText().toString().equals("")) {
tag = addTagsInput.getText().toString();
} else {
// handle empty string
}
I want to make a button square with the width the same as the heigth.
I try bt.setWidth(bt.getHeight()) but it doesn't work.
If I hardcode the width (bt.setWidth(90)) it works but I don't know the height so I can hardcoded it.
Here is some code. When I click on a button, it opens a dialog and this dialog must contain the square button.
public class MyClass extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myLayout);
}
public void doClick(View view) {
final Dialog dialog = new Dialog(view.getContext());
dialog.setContentView(R.layout.dialogLayout);
dialog.setTitle("Title");
Button bt = (Button) dialog.findViewById(R.id.myButton);
bt.setWidth(bt.getHeight());
bt.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
...
}
});
dialog.show();
}
}
How could I do it?
The direct answer is that until the dialog is displayed, it has not measured it's layout and the button height will be zero. You could extend the Dialog class and override the onMeasure() method or attach a global layout listener to the layout and set the button size in onLayoutComplete().
However, your approach might be wrong. Why can't you do this in the dialog's layout XML?