How do you create a Popup Window that looks like this? - java

I'd like to create a PopupWindow that looks like the blue one above, meaning it points to a view. How is it done?
The Popup Window I have so far doesn't point to anything and also can't be shaped to something similar to above.
popup_window.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="#0D47A1"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is what this button does..."
android:textColor="#ffffff"/>
</LinearLayout>
And in code:
myButton.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View customView = layoutInflater.inflate(R.layout.popup_window,null);
//instantiate popup window
PopupWindow popupWindow = new PopupWindow(customView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
//display the popup window
popupWindow.showAsDropDown(v);
return true;
}
});

I've achieved this with this external library that allows to customize it, it's an alternative to the other answer.
https://github.com/kcrimi/ToolTipDialog
Show a default dialog pop up banner
Align the dialog to a certain vertical location on screen
Point to a specific element on-screen
Highlight specific UI elements by letting them "peek through" a
background shade

Related

How to make Android EditText force its container to scroll

I have an Android app which can create multiple lines each of which contains two spinners and an EditText. If the content gets too wide for the EditText, it breaks into multiple lines and this messes up my layout. So I call setMaxLines(1). But now the EditText scrolls internally. I want the EditText to expand horizontally to fit its content and the containing HorizontalScrollView to scroll. How can I do this?
I tried putting a HorizontalScrollView in a ScrollView and vice versa. Neither way round works. I expected that setting (both) LayoutParams to wrap_content would work but it doesn't.
I haven't included all of the code because it is a big app. Here is the layout XML:-
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- This is an invisible view to request the focus when an EditText gets deleted
in order to stop Android highlighting something else.
It would be nicer to go back into touch mode,
but Android does not provide a way of doing that. -->
<TextView
android:layout_width="1dp"
android:layout_height="1dp"
android:text=""
android:alpha="0"
android:id="#+id/defineinvisible"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/defineclasslayout">
</LinearLayout>
</LinearLayout>
</ScrollView>
</HorizontalScrollView>
The lines containing the EditTexts are added dynamically because the number of them can vary as the app runs.
The code to add a line looks a bit like this (I've left out some irrelevant detail):-
public class OrItem extends LinearLayout implements TextWatcher {
public OrItem(Context context) {
m_nameSelector = new Spinner(m_context);
m_contSelector = new Spinner(m_context);
m_matchString = new EditText(m_context);
m_matchString.setHorizontallyScrolling(false); m_matchString.setMaxLines(1);
m_matchString.setInputType(TYPE_CLASS_TEXT);
}
public setup(...) {
ViewGroup.LayoutParams ww = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
setOrientation(LinearLayout.HORIZONTAL);
addView(m_nameSelector, ww);
addView(m_contSelector, ww);
m_matchString.setText(sa[2]);
m_wasEmpty = sa[2].isEmpty();
m_matchString.addTextChangedListener(this);
addView(m_matchString, ww);
}
}
The OrItem is added under a hierarchy of LinearLayouts that hangs from defineclasslayout.
I tried inserting
but this goes back to line wrapping inside the EditText.
I found workaround for this. I make each individual line a HorizontalScrollView. Then I can scroll a long line. This is less elegant than scrolling the whole view, but it works.
public class OrItem extends HorizontalScrollView implements TextWatcher {
public OrItem(Context context) {
m_layout = new LinearLayout(context);
m_nameSelector = new Spinner(context);
m_contSelector = new Spinner(context);
m_matchString = new EditText(context);
m_matchString.setMaxLines(1);
m_matchString.setInputType(TYPE_CLASS_TEXT);
m_matchString.setHorizontallyScrolling(false);
}
public boolean setup(...) {
ViewGroup.LayoutParams ww = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
addView(m_layout);
m_layout.setOrientation(LinearLayout.HORIZONTAL);
m_layout.addView(m_nameSelector, ww);
m_layout.addView(m_contSelector, ww);
m_matchString.addTextChangedListener(this);
m_layout.addView(m_matchString, ww);
}

How to use a ScrollView?

This is suppose to be a scroll view with all the content added from the Java code when response is received from the API.
The problem is that I can't find a way to display the information like this in a ScrollView. I tried using an ImageButton but I couldn't get the content in it then I tried using a Button but still couldn't achieve the desired effect please can someone suggest a way I could do this.
private Button makeButton(String targetName, final String i, LinearLayout.LayoutParams buttonLayoutParams) {
Button in = new Button(this);
in.setBackground(getResources().getDrawable(R.drawable.rectangle14));
in.setText(targetName);
in.setWidth(360);
in.setHeight(72);
in.setLayoutParams(buttonLayoutParams);
in.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent myIntent = new Intent(HomeActivity.this,XSavingDetailsActivity.class);
myIntent.putExtra("i" ,i);
HomeActivity.this.startActivity(myIntent);
}
});
return in;
}
You should use a RecyclerView .Each and every component within the RecyclerView is a CardView . Also you should learn about Material Design.
Apart from the above some useful links:
https://developer.android.com/guide/topics/ui/layout/cardview.html
https://www.androidhive.info/2016/01/android-working-with-recycler-view/
https://medium.com/#nileshsingh/android-cardview-101-everything-you-should-know-5bbf1c873f5a
Just make the top-level layout a ScrollView:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<TableLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchColumns="1">
<!-- everything else -->
</TableLayout>

ListView with Load More Button and Circle ProgressBar

I spent an hour trying to add "Load More" Button and indeterminate ProgressBar to the footer of my ListView.
The supposed scinario works like this:
When the button is clicked, ProgressBar is shown while AsyncTask is downloading 20 items. when the items are inserted to the ListView, the ProgressBar dismisses and the Button appears again in the footer.
I come to the following solution and would be nice if you have better solution:
layout/progress_bar.xml
<?xml version="1.0" encoding="utf-8"?>
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/load_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
/>
And assuming you have the following fields in the Activity:
private ListView listview;
private Button loadMoreButton;
private ProgressBar progressBar;
after preparing your list view add the footer like this, (at the end of the activity.onCreate()):
loadMoreButton = new Button(this);
loadMoreButton.setText("Load More");
progressBar = (ProgressBar) LayoutInflater.from(this).inflate(R.layout.progress_bar, null);
loadMoreButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//downloadItems();
listview.removeFooterView(loadMoreButton);
listview.addFooterView(progressBar);
}
});
When the data is ready (either first page or subsequent pages), call the following after adapter update.
//adapter.notifyDataSetChanged();
listview.removeFooterView(progressBar);
listview.addFooterView(loadMoreButton);
If you have better solutions, please share it in the answers.
Instead of removing and adding a view as footer.. you can have both button and progressbar in the same footer view and make visibility VISIBLE and Visibility GONE according to the requirement.
I came to a solution without adding/removing the footer. Just put the two views (Load More Button and ProgressBar) in LinearLayout. Add the linearlayout as listview footer for first time only. Then change between the button and progressbar by changing visibility property.
Here is the updated code:
layout/list_footer.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="#+id/load_more_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="Load More"/>
<ProgressBar android:id="#+id/load_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
android:indeterminate="true" />
</LinearLayout>
call this method at the end of Activity.onCreate() to add footer and handle button click (load data, hide button and show progress).
private void prepareListFooter() {
View view = (View) LayoutInflater.from(this).inflate(R.layout.list_footer, null);
loadMoreButton = (Button) view.findViewById(R.id.load_more_button);
progressBar = (ProgressBar) view.findViewById(R.id.load_progress);
listview.addFooterView(view);
loadMoreButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//downloadMore();
loadMoreButton.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
}
});
}
also this code after adapter change to show the button and hide the progress.
adapter.notifyDataSetChanged();
loadMoreButton.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);

How to stack AlertDialog buttons vertically?

I am using builder to create AlertDialogs in Android using the pattern:
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(...);
builder.setMessage(...);
builder.setPositiveButton(button1Text, ...);
builder.setNeutralButton(button2Text, ...);
builder.setNegativeButton(button3Text, ...);
builder.show();
Currently, only two of the buttons are displayed, because the buttons are too wide to fit in the dialog. How can I enforce the buttons to stack vertically?
I am using the Theme.AppCompat.Dialog.Alert theme, which uses ButtonBarLayout to build the buttons. According to this answer, ButtonBarLayout can stack wide buttons vertically automatically, when its mAllowStacking property is set, but it seems to default to false in my case. Is there a way I can set it to true when I build the AlertDialog?
I don't prefer hacks. But this strikes me immediately.
If the button text it too long to all fit horizontally, then it will
automatically get laid out in a vertical column of three buttons.
Just make the button text long.
builder.setPositiveButton(" Yes", { dialog, _ -> {} })
builder.setNeutralButton(" May be", { dialog, _ -> {} })
builder.setNegativeButton(" No", { dialog, _ -> {} })
You can't do that with an AlertDialog . You should create a custom Dialog, and implement that yourself. Something like this would do it
Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.dialog_layout);
dialog.setTitle(...);
dialog.setMessage(...);
dialog.show();
and your layout dialog_layout.xml should be something like
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
orientation="vertical">
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"/>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"/>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</LinearLayout>
What if you did the alert box as a list?
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.pick_color)
.setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// The 'which' argument contains the index position
// of the selected item
}
});
return builder.create();
}
Example taken from here (under adding a list): https://developer.android.com/guide/topics/ui/dialogs.html
Then just take those list options and turn them into what you want.
This is a problem with MaterialAlertDialog as well.
I tried many solutions, but I think the best one is to override the button layout that material dialogs use. Copy paste the content of mtrl_alert_dialog_actions.xml from the material library and do the necessary changes. For example change the ButtonBarLayout to a LinearLayout with vertical orientation.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/buttonPanel"
style="?attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
android:scrollIndicators="top|bottom">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingTop="2dp"
android:paddingBottom="2dp">
<Button
android:id="#android:id/button1"
style="?attr/buttonBarPositiveButtonStyle"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_height="wrap_content"/>
<Button
android:id="#android:id/button2"
style="?attr/buttonBarNegativeButtonStyle"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_height="wrap_content"/>
<Button
android:id="#android:id/button3"
style="?attr/buttonBarNeutralButtonStyle"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</ScrollView>
Then override the layout by adding it to res/values/refs.xml:
<resources xmlns:tools="http://schemas.android.com/tools">
<item name="mtrl_alert_dialog_actions" type="layout" tools:override="true">#layout/mtrl_alert_dialog_actions_custom</item>
</resources>

Why does this popup window only show as a small square?

So I'm making a simple app, where you click a button, and it displays a popup with a random string from the button.
The problem is, when the pop up window is created, its a small box. like 100x100 or something. Here is the popup code. You can see I tried setting 550, and 750, as the size, if I put 70, and 550, it will work perfectly and be really small width, and stretched long wise. But after like 100dp, it goes back to square
public void showPopup(View v){
View popupView = getLayoutInflater().inflate(R.layout.popup_layout, null);
PopupWindow popupWindow = new PopupWindow(popupView, 550, 750);
// Example: If you have a TextView inside `popup_layout.xml`
TextView tv = (TextView) popupView.findViewById(R.id.tv);
String[] myString;
Resources res = getResources();
myString = res.getStringArray(R.array.myArray);
String q = myString[rgenerator.nextInt(myString.length)];
tv.setText(q);
// Initialize more widgets from `popup_layout.xml`
// If the PopupWindow should be focusable
popupWindow.setFocusable(true);
// If you need the PopupWindow to dismiss when when touched outside
popupWindow.setBackgroundDrawable(new ColorDrawable());
// Get the View's(the one that was clicked in the Fragment) location
//v.getLocationOnScreen(location);
// Using location, the PopupWindow will be displayed right under anchorView
popupWindow.showAtLocation(v, Gravity.CENTER, 0, 0);
}
Here is my popup layout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:background="#drawable/blank">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="#+id/tv"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:textColor="#FFFF"
android:layout_gravity="center" />
</FrameLayout>
android:layout_width="wrap_content" android:layout_height="wrap_content"
Try match_parent instead

Categories

Resources