I would like to be able to either blur or dim the background when I show my popup window using popup.showAtLocation, and unblur/dim the background when popup.dismiss is called.
I have tried applying layout params FLAG_BLUR_BEHIND and FLAG_DIM_BEHIND to my activity, but this appears to just blur and dim the background as soon my app is started.
How can I do blurring/dimming just with popups?
The question was about the Popupwindow class, yet everybody has given answers that use the Dialog class. Thats pretty much useless if you need to use the Popupwindow class, because Popupwindow doesn't have a getWindow() method.
I've found a solution that actually works with Popupwindow. It only requires that the root of the xml file you use for the background activity is a FrameLayout. You can give the Framelayout element an android:foreground tag. What this tag does is specify a drawable resource that will be layered on top of the entire activity (that is, if the Framelayout is the root element in the xml file). You can then control the opacity (setAlpha()) of the foreground drawable.
You can use any drawable resource you like, but if you just want a dimming effect, create an xml file in the drawable folder with the <shape> tag as root.
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#000000" />
</shape>
(See http://developer.android.com/guide/topics/resources/drawable-resource.html#Shape for more info on the shape element).
Note that I didn't specify an alpha value in the color tag that would make the drawable item transparent (e.g #ff000000). The reason for this is that any hardcoded alpha value seems to override any new alpha values we set via the setAlpha() in our code, so we don't want that.
However, that means that the drawable item will initially be opaque (solid, non-transparent). So we need to make it transparent in the activity's onCreate() method.
Here's the Framelayout xml element code:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainmenu"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:foreground="#drawable/shape_window_dim" >
...
... your activity's content
...
</FrameLayout>
Here's the Activity's onCreate() method:
public void onCreate( Bundle savedInstanceState)
{
super.onCreate( savedInstanceState);
setContentView( R.layout.activity_mainmenu);
//
// Your own Activity initialization code
//
layout_MainMenu = (FrameLayout) findViewById( R.id.mainmenu);
layout_MainMenu.getForeground().setAlpha( 0);
}
Finally, the code to dim the activity:
layout_MainMenu.getForeground().setAlpha( 220); // dim
layout_MainMenu.getForeground().setAlpha( 0); // restore
The alpha values go from 0 (opaque) to 255 (invisible).
You should un-dim the activity when you dismiss the Popupwindow.
I haven't included code for showing and dismissing the Popupwindow, but here's a link to how it can be done: http://www.mobilemancer.com/2011/01/08/popup-window-in-android/
Since PopupWindow just adds a View to WindowManager you can use updateViewLayout (View view, ViewGroup.LayoutParams params) to update the LayoutParams of your PopupWindow's contentView after calling show..().
Setting the window flag FLAG_DIM_BEHIND will dimm everything behind the window. Use dimAmount to control the amount of dim (1.0 for completely opaque to 0.0 for no dim).
Keep in mind that if you set a background to your PopupWindow it will put your contentView into a container, which means you need to update it's parent.
With background:
PopupWindow popup = new PopupWindow(contentView, width, height);
popup.setBackgroundDrawable(background);
popup.showAsDropDown(anchor);
View container = (View) popup.getContentView().getParent();
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams p = (WindowManager.LayoutParams) container.getLayoutParams();
// add flag
p.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
p.dimAmount = 0.3f;
wm.updateViewLayout(container, p);
Without background:
PopupWindow popup = new PopupWindow(contentView, width, height);
popup.setBackgroundDrawable(null);
popup.showAsDropDown(anchor);
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams p = (WindowManager.LayoutParams) contentView.getLayoutParams();
// add flag
p.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
p.dimAmount = 0.3f;
wm.updateViewLayout(contentView, p);
Marshmallow Update:
On M PopupWindow wraps the contentView inside a FrameLayout called mDecorView. If you dig into the PopupWindow source you will find something like createDecorView(View contentView).The main purpose of mDecorView is to handle event dispatch and content transitions, which are new to M. This means we need to add one more .getParent() to access the container.
With background that would require a change to something like:
View container = (View) popup.getContentView().getParent().getParent();
Better alternative for API 18+
A less hacky solution using ViewGroupOverlay:
1) Get a hold of the desired root layout
ViewGroup root = (ViewGroup) getWindow().getDecorView().getRootView();
2) Call applyDim(root, 0.5f); or clearDim()
public static void applyDim(#NonNull ViewGroup parent, float dimAmount){
Drawable dim = new ColorDrawable(Color.BLACK);
dim.setBounds(0, 0, parent.getWidth(), parent.getHeight());
dim.setAlpha((int) (255 * dimAmount));
ViewGroupOverlay overlay = parent.getOverlay();
overlay.add(dim);
}
public static void clearDim(#NonNull ViewGroup parent) {
ViewGroupOverlay overlay = parent.getOverlay();
overlay.clear();
}
In your xml file add something like this with width and height as 'match_parent'.
<RelativeLayout
android:id="#+id/bac_dim_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#C0000000"
android:visibility="gone" >
</RelativeLayout>
In your activity oncreate
//setting background dim when showing popup
back_dim_layout = (RelativeLayout) findViewById(R.id.share_bac_dim_layout);
Finally make visible when you show your popupwindow and make its visible gone when you exit popupwindow.
back_dim_layout.setVisibility(View.VISIBLE);
back_dim_layout.setVisibility(View.GONE);
Another trick is to use 2 popup windows instead of one. The 1st popup window will simply be a dummy view with translucent background which provides the dim effect. The 2nd popup window is your intended popup window.
Sequence while creating pop up windows:
Show the dummy pop up window 1st and then the intended popup window.
Sequence while destroying:
Dismiss the intended pop up window and then the dummy pop up window.
The best way to link these two is to add an OnDismissListener and override the onDismiss() method of the intended to dimiss the dummy popup window from their.
Code for the dummy popup window:
fadepopup.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"
android:id="#+id/fadePopup"
android:background="#AA000000">
</LinearLayout>
Show fade popup to dim the background
private PopupWindow dimBackground() {
LayoutInflater inflater = (LayoutInflater) EPGGRIDActivity.this
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View layout = inflater.inflate(R.layout.fadepopup,
(ViewGroup) findViewById(R.id.fadePopup));
PopupWindow fadePopup = new PopupWindow(layout, windowWidth, windowHeight, false);
fadePopup.showAtLocation(layout, Gravity.NO_GRAVITY, 0, 0);
return fadePopup;
}
I've found a solution for this
Create a custom transparent dialog and inside that dialog open the popup window:
dialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar);
emptyDialog = LayoutInflater.from(context).inflate(R.layout.empty, null);
/* blur background*/
WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
lp.dimAmount=0.0f;
dialog.getWindow().setAttributes(lp);
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
dialog.setContentView(emptyDialog);
dialog.setCanceledOnTouchOutside(true);
dialog.setOnShowListener(new OnShowListener()
{
#Override
public void onShow(DialogInterface dialogIx)
{
mQuickAction.show(emptyDialog); //open the PopupWindow here
}
});
dialog.show();
xml for the dialog(R.layout.empty):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent" android:layout_width="match_parent"
style="#android:style/Theme.Translucent.NoTitleBar" />
now you want to dismiss the dialog when Popup window dismisses. so
mQuickAction.setOnDismissListener(new OnDismissListener()
{
#Override
public void onDismiss()
{
if(dialog!=null)
{
dialog.dismiss(); // dismiss the empty dialog when the PopupWindow closes
dialog = null;
}
}
});
Note: I've used NewQuickAction plugin for creating PopupWindow here. It can also be done on native Popup Windows
For me, something like Abdelhak Mouaamou's answer works, tested on API level 16 and 27.
Instead of using popupWindow.getContentView().getParent() and casting the result to View (which crashes on API level 16 cause there it returns a ViewRootImpl object which isn't an instance of View) I just use .getRootView() which returns a view already, so no casting required there.
Hope it helps someone :)
complete working example scrambled together from other stackoverflow posts, just copy-paste it, e.g., in the onClick listener of a button:
// inflate the layout of the popup window
LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
if(inflater == null) {
return;
}
//View popupView = inflater.inflate(R.layout.my_popup_layout, null); // this version gives a warning cause it doesn't like null as argument for the viewRoot, c.f. https://stackoverflow.com/questions/24832497 and https://stackoverflow.com/questions/26404951
View popupView = View.inflate(MyParentActivity.this, R.layout.my_popup_layout, null);
// create the popup window
final PopupWindow popupWindow = new PopupWindow(popupView,
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT,
true // lets taps outside the popup also dismiss it
);
// do something with the stuff in your popup layout, e.g.:
//((TextView)popupView.findViewById(R.id.textview_popup_helloworld))
// .setText("hello stackoverflow");
// dismiss the popup window when touched
popupView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
popupWindow.dismiss();
return true;
}
});
// show the popup window
// which view you pass in doesn't matter, it is only used for the window token
popupWindow.showAtLocation(view, Gravity.CENTER, 0, 0);
//popupWindow.setOutsideTouchable(false); // doesn't seem to change anything for me
View container = popupWindow.getContentView().getRootView();
if(container != null) {
WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams p = (WindowManager.LayoutParams)container.getLayoutParams();
p.flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND;
p.dimAmount = 0.3f;
if(wm != null) {
wm.updateViewLayout(container, p);
}
}
Maybe this repo will help for you:BasePopup
This is my repo, which is used to solve various problems of PopupWindow.
In the case of using the library, if you need to blur the background, just call setBlurBackgroundEnable(true).
See the wiki for more details.(Language in zh-cn)
BasePopup:wiki
findViewById(R.id.drawer_layout).setAlpha((float) 0.7);
R.id.drawer_layout is the id of the layout of which you want to dim the brightness.
You can use android:theme="#android:style/Theme.Dialog" to do that.
Create an activity and in your AndroidManifest.xml define the activity as:
<activity android:name=".activities.YourActivity"
android:label="#string/your_activity_label"
android:theme="#android:style/Theme.Dialog">
ok, so i follow uhmdown's answer for dimming background activity when pop window is open. But it creates problem for me. it was dimming activity and include popup window (means dimmed-black layered on both activity and popup also, it can not be separate them).
so i tried this way,
create an dimming_black.xml file for dimming effect,
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#33000000" />
</shape>
And add as background in FrameLayout as root xml tag, also put my other controls in LinearLayout like this layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#drawable/ff_drawable_black">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom"
android:background="#color/white">
// other codes...
</LinearLayout>
</FrameLayout>
at last i show popup on my MainActivity with some extra parameter set as below.
//instantiate popup window
popupWindow = new PopupWindow(viewPopup, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, true);
//display the popup window
popupWindow.showAtLocation(layout_ff, Gravity.BOTTOM, 0, 0);
Result:
it works for me, also solved problem as commented by BaDo. With this Actionbar also can be dimmed.
P.s i am not saying uhmdown's is wrong. i learnt form his answer and try to evolve for my problem. I also confused whether this is a good way or not.
Any suggestions is also appreciated also sorry for my bad English.
Since You are trying to pop up your dialog window by blurring the background screen, You must use this set of lines. You need to fetch the dialog attributes first, then set up some alpha values for the dialog attributes.
Now, your dialog with blur background is ready. But the important factor is to set a Flag FLAG_DIM_BEHIND for the window.
Now the result is yours. Hope it will helpful for someone...
WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
lp.dimAmount=0.6f;
dialog.getWindow().setAttributes(lp);
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
This code work
pwindo = new PopupWindow(layout, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
pwindo.showAtLocation(layout, Gravity.CENTER, 0, 0);
pwindo.setOutsideTouchable(false);
View container = (View) pwindo.getContentView().getParent();
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams p = (WindowManager.LayoutParams) container.getLayoutParams();
p.flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND;
p.dimAmount = 0.3f;
wm.updateViewLayout(container, p);
Related
My app is a full screen app. It goes full width and does not show a status bar, uses CutoutMode and hides controls like (Home and Back). This also works well.
However, when I create a PopUpWindow, these settings don't seem to take effect. See the following image:
I would have expected that the quadrangles marked in red on the left and right would also be in the corresponding shade of gray.
My code to initialize the PopUp Window looks like this:
val inflater =
view.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater
?: return
// Inflate a custom view using layout inflater
val popUpView = inflater.inflate(R.layout.view_change_name, null)
//popUpView.animation = AnimationUtils.loadAnimation(view.context, R.anim.fade_in_animation)
// Initialize a new instance of popup window
val popupWindow = PopupWindow(
popUpView, // Custom view to show in popup window
LinearLayout.LayoutParams.MATCH_PARENT, // Width of popup window
LinearLayout.LayoutParams.MATCH_PARENT, // Window height
true
)
popupWindow.animationStyle = R.style.PopUpWindowAnimation
popupWindow.isFocusable = true
popupWindow.isOutsideTouchable = true
popupWindow.update(
0,
0,
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
//Set the location of the window on the screen
popupWindow.showAtLocation(view, Gravity.BOTTOM, 0, 0)
My method to get the full screen in my activity works like this.
fun Activity.fullscreen() {
with(WindowInsetsControllerCompat(window, window.decorView)) {
systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE
hide(Type.systemBars())
}
}
So my idea was to get a decorView in the PopUpWindow. But I think the PopUpWindow has not a decorView. I'm running out of ideas. Does anyone have an approach or solution?
How stupid. After I post this question I found an answer.
Use
popupWindow.isClippingEnabled = false
Documentation
#param enabled false if the window should be allowed to extend outside of the screen
My popup window should be auto dismissed when click outside. I already read this topic, and have set background drawable to the window. Here is my code:
protected int showPopupWindow(final int popupWidth) {
hideCalendarCellPopupWindow();
final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(LAYOUT_INFLATER_SERVICE);
final View popupView = inflater.inflate(R.layout.popup_calendar_cell, null);
mCalendarCellPopupWindow = new PopupWindow(popupView, popupWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
mCalendarCellPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.WHITE));
mCalendarCellPopupWindow.setOutsideTouchable(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mCalendarCellPopupWindow.setElevation(...);
}
mCalendarCellPopupWindow.showAtLocation(....);
}
private hideCalendarCellPopupWindow() {
if (mCalendarCellPopupWindow != null) {
mCalendarCellPopupWindow.dismiss();
mCalendarCellPopupWindow = null;
}
}
A problem have appeared on Android 10, as you can touch outside of the phone screen and slide the finger inside the screen, meanwhile "recent apps" could be shown with such gesture.
So my issue is when I slide the finger from the bottom to the top a little bit and than go back to the bottom - popup window is not dismissed, moreover it cannot be dissmissed no more, as its property isShowing() returns false. I have tried to call popupWindow.dismiss() method inside onPause() but it is not called as well in such situation.
This screencast could explain the issue more precies: https://youtu.be/w2cQMvFMYkk
What could be the workaround?
If you want to dismiss your popup outside, you may set as following:
popupWindow.setFocusable(true);
popupWindow.setTouchable(true);
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
popupWindow.setOutsideTouchable(true);
popupWindow.update();
I want to display a popup window in my code such that, no matter where I touch on the screen, the popup should show up right above the place I touch. Not sure, how to achieve this. This is my current popup window code:
LayoutInflater inflater = (LayoutInflater) getApplicationContext().getSystemService(LAYOUT_INFLATER_SERVICE);
View customView = inflater.inflate(R.layout.popup,null);
final PopupWindow popupWindow = new PopupWindow(
customView,
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
// Set an elevation value for popup window
// Call requires API level 21
if(Build.VERSION.SDK_INT>=21){
popupWindow.setElevation(5.0f);
}
final TextView placename = (TextView) customView.findViewById(R.id.popup_id) ;
Button closeButton = (Button) customView.findViewById(R.id.directions);
placename.setText(place.getName());
popupWindow.setBackgroundDrawable(new BitmapDrawable());
popupWindow.setOutsideTouchable(true);
// Set a click listener for the popup window close button
closeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Dismiss the popup window
popupWindow.dismiss();
}
});
popupWindow.setFocusable(true);
popupWindow.showAtLocation(mapInsideContainer, Gravity.CENTER, 0, 0);
popupWindow.showAsDropDown(customView);
This is my popup.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#android:color/background_light">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="1dp"
android:background="#android:color/white">
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="20dp">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:id="#+id/popup_id" />
<Button
android:id="#+id/directions"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#color/black_overlay"
android:textColor="#android:color/white"
android:text="Directions" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
Currently it shows on the center of the screen due to Gravity.CENTER, but I would like to show it right above the place I touch dynamically on screen. Any ideas? Thanks
Bonus points if you could guide me through creating a chat bubble like popup with title on top and a "places" button at the bottom with the bubble pointer at the position clicked on screen
this is what i did, Hope help you
customView.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
popupWindow.showAtLocation(getWindow().getDecorView(), Gravity.NO_GRAVITY, (int)event.getX(), (int)event.getY() - customView.getMeasuredHeight());
And Override public boolean onTouchEvent(MotionEvent event)
#Override
public boolean onTouchEvent(MotionEvent event) {
popup(event);
return super.onTouchEvent(event);
}
EDIT:
My Writing as Follow, And it's work for me.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
popup(event);
return super.onTouchEvent(event);
}
private void popup(MotionEvent event) {
LayoutInflater inflater = (LayoutInflater) getApplicationContext().getSystemService(LAYOUT_INFLATER_SERVICE);
View customView = inflater.inflate(R.layout.popup,null);
final PopupWindow popupWindow = new PopupWindow(
customView,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT);
// Set an elevation value for popup window
// Call requires API level 21
if(Build.VERSION.SDK_INT>=21){
popupWindow.setElevation(5.0f);
}
final TextView placename = (TextView) customView.findViewById(R.id.popup_id) ;
Button closeButton = (Button) customView.findViewById(R.id.directions);
placename.setText("Name");
popupWindow.setBackgroundDrawable(new BitmapDrawable());
popupWindow.setOutsideTouchable(true);
// Set a click listener for the popup window close button
closeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Dismiss the popup window
popupWindow.dismiss();
}
});
// popupWindow.setFocusable(true);
popupWindow.setOutsideTouchable(true);
customView.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
popupWindow.showAtLocation(getWindow().getDecorView(), Gravity.NO_GRAVITY, (int)event.getX(), (int)event.getY() - customView.getMeasuredHeight());
getWindow().getDecorView().setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/parent"
android:background="#color/colorAccent"
android:fitsSystemWindows="true">
</android.support.constraint.ConstraintLayout>
Instead of using PopupWindow, try creating a transparent Activity, similar to this.
Then, you can create custom drawables for the chat bubbles (since the bubble pointer will need to be in different places depending on where the user clicked). Override onTouchEvent() and get the coordinates of the users touch, then launch the transparent activity and pass it the coordinates in the intent, so that you can place the bubble view in the appropriate spot.
This adds extra overhead because you are launching a new Activity, but it gives you full power to customize the appearance and behavior however you want.
Here's some details. This is what I've done when I want customized popup menus.
For the transparent activity, create a new activity and assign a theme to it in the manifest:
<activity
android:name=".PopupActivity"
android:theme="#style/Transparent" />
And in styles.xml:
<style name="Transparent" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">#color/translucent</item>
</style>
You can define #color/translucent to be completely transparent, I add some grey to it to make the view underneath appear to dim when the popup appears.
In the layout for the activity:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_popup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimary"
android:layout_margin="60dp">
Here I'm using RelativeLayout, but you can use whatever layout you want, the important thing is the margins. Now the activity looks like this, where the background activity is visible behind this one:
Next you'll use the touch coordinates to dynamically alter the margins to make the rectangle appear wherever you want.
We'll need to know the screen size and density and the height of the status bar at the top of the screen, so in the onCreate() method of your main activity, add:
//Determine the screen dimensions and density
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
screenWidth = metrics.widthPixels;
screenHeight = metrics.heightPixels;
screenDensity = metrics.density;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
int statusBarHeight = (int) (24 * density);
if (resourceId > 0) {
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
}
These values won't change, so store them in static variables so other classes can get them when needed.
Now, in the onCreate() method of the transparent activity, compute the margins based on the touch event coordinates:
int popupSize = 100;
int rMargin, lMargin, tMargin, bMargin;
lMargin = xCoord - popupSize;
rMargin = screenWidth - xCoord - popupSize;
tMargin = yCoord - popupSize;
bMargin = screenHeight - yCoord - popupSize - statusBarHeight;
RelativeLayout layout = (RelativeLayout) findViewById(R.id.activity_popup);
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) layout.getLayoutParams();
params.setMargins(lMargin, tMargin, rMargin, bMargin);
layout.setLayoutParams(params);
This creates a square that is centered on the touch coordinates. For instance, given x = 700 and y = 1500, I get:
And for x = 150 and y = 200, I get:
You can see that in this case, if the coordinates are too close to the edge, you'll get a negative margin and the square will be cropped. It will take more math to correct this.
What I would do is divide the screen area into four quadrants and calculate which quadrant the touch event was in. Then, for instance if the touch was in the top-left quadrant, align the square's top-left corner with the touch coordinates. That way the square will never be cropped by the screen edge.
As far as creating a bubble with a pointer, make 4 custom drawables of the bubble, each one with the pointer at a different corner. Then, depending on the quadrant of the touch event, load the appropriate drawable so the pointer is always in the right direction.
This post is getting a little too long for that though.
I'm creating an AlertDialog with customized view and window background. Setting a ColorDrawable works as expected, but setting a BitmapDrawable from resources makes the dialog appear right at the top of the screen (instead of centered). (Note: I'm talking of the background behind the dialog (normally a transparent grey, not the dialog's background itself!)
Dialog background (#drawable/dialog_bg):
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#color/white" />
<corners android:radius="10dp" />
</shape>
Dialog layout:
<?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:background="#drawable/dialog_bg"
android:orientation="vertical">
<!-- dialog contents -->
</LinearLayout>
Code to show dialog with ColorDrawable: -> works
private void showDialog() {
final AlertDialog dialog;
#SuppressLint("InflateParams") final ViewGroup dialogView = (ViewGroup) activity.getLayoutInflater().inflate(R.layout.my_dialog, null);
dialog = new AlertDialog.Builder(activity).setView(dialogView).create();
// this works:
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.show();
}
Code to show dialog with BitmapDrawable from resources (loading a simple PNG): -> removes centering
private void showDialog() {
final AlertDialog dialog;
#SuppressLint("InflateParams") final ViewGroup dialogView = (ViewGroup) activity.getLayoutInflater().inflate(R.layout.my_dialog, null);
dialog = new AlertDialog.Builder(activity).setView(dialogView).create();
// this sets the background, but un-centers the dialog:
BitmapDrawable drawable = (BitmapDrawable) ResourcesCompat.getDrawable(activity.getResources(), R.drawable.my_bg, null);
dialog.getWindow().setBackgroundDrawable(drawable);
dialog.show();
}
Setting a ColorDrawable works as expected: The background behind the Dialog is colored and the dialog is still centered on screen.
Setting a BitmapDrawable does not work: The background is set but the dialog is moved to the top of the screen.
Things that also didn't work:
loading the drawable with ContextCompat.getDrawable() (which is the same as ResourcesCompat.getDrawable() with the current theme instead of null)
using DisplayMetrics and dialog.getWindow().getAttributes().y (and .x respectively) to calculate margins myself: (height - y) / 2 -> just returns the "normal" dialog margin
setting the gravity to CENTER on either dialog.getWindow().setGravity() or dialog.getWindow().getAttributes().gravity -> this just doesn't change anything
setting the gravity to FILL on either dialog.getWindow().setGravity() or dialog.getWindow().getAttributes().gravity -> this removes dialog margins, but still at the top (even further at the top and left, as margins are removed)
So, does anybody know how to set a background from PNG behind the dialog and keeping its centering on the screen?
We had WindowManger for Dialog to Specify custom window attributes:
The layout params you give here should generally be from values previously retrieved with {#link #getAttributes()}; you probably do not want to blindly create and apply your own, since this will blow away any values set by the framework that you are not interested in.
Just add these property according to your requirement :
/**
* Retrieve the current window attributes associated with this panel.
*
* #return WindowManager.LayoutParams Either the existing window
* attributes object, or a freshly created one if there is none.
*/
WindowManager.LayoutParams params = dialog.getWindow().getAttributes();
params.gravity = Gravity.TOP;
/**
* Set the width and height layout parameters of the window. The default
* for both of these is MATCH_PARENT; you can change them to WRAP_CONTENT
* or an absolute value to make a window that is not full-screen.
*
* #param width The desired layout width of the window.
* #param height The desired layout height of the window.
*
* #see ViewGroup.LayoutParams#height
* #see ViewGroup.LayoutParams#width
*/
dialog.getWindow().setLayout((int) (getScreenWidth(activity)), ViewGroup.LayoutParams.MATCH_PARENT);
dialog.getWindow().setBackgroundDrawableResource(R.drawable.message_email_selected);
/**
* Specify custom window attributes. <strong>PLEASE NOTE:</strong> the
* layout params you give here should generally be from values previously
* retrieved with {#link #getAttributes()}; you probably do not want to
* blindly create and apply your own, since this will blow away any values
* set by the framework that you are not interested in.
*
* #param a The new window attributes, which will completely override any
* current values.
*/
dialog.getWindow().setAttributes(params);
As Example :
final Dialog dialog = new Dialog(getActivity());
WindowManager.LayoutParams params = dialog.getWindow().getAttributes();
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(R.layout.dialog_view);
// dialog.getWindow().setBackgroundDrawable(null);
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
dialog.getWindow().setLayout(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
dialog.getWindow().setAttributes(params);
dialog.show();
//Access dialog views
TextView txt_cancel = (TextView) dialog.findViewById(R.id.txt_cancel);
So I just solved this issue, although I have to admit it's a bit hacky.
First, I disabled fading behind the dialog
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
and manually "faded" the activity by adding a view to it, overlaying the activity with semi-transparent black:
final ViewGroup dimBackgroundView = new FrameLayout(activity);
float dimAlpha = 0.5f;
dimBackgroundView.setBackgroundColor(Color.BLACK);
dimBackgroundView.setAlpha(dimAlpha);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
activityLayout.addView(dimBackgroundView, params);
This also requires me to manually darken the statusbar on supporting devices (SDK 21+):
final int statusBarColor;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
statusBarColor = activity.getWindow().getStatusBarColor();
activity.getWindow().setStatusBarColor(
Color.rgb((int) (Color.red(statusBarColor) + 255 * dimAlpha),
(int) (Color.green(statusBarColor) + 255 * dimAlpha),
(int) (Color.blue(statusBarColor) + 255 * dimAlpha)));
} else {
statusBarColor = Color.BLACK;
}
Afterwards, I added the intended background to the activity (dialogBgView on top of the semi-transparent black view) and went on adding the dialog as normal.
Since I now added all these views to the activity, I need to remove them on dialog dismissal:
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
#Override
public void onDismiss(DialogInterface dialogInterface) {
// remove dim
activityLayout.removeView(dimBackgroundView);
// restore original statusbar color
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().setStatusBarColor(statusBarColor);
}
// remove background image
activityLayout.removeView(dialogBgView);
}
});
It works, but it's really not a nice solution. So if anyone discovers a better way, please feel free to post it here.
Helloo
I have simple question about popupwindow.
If click on button i want open activity as popupwindow with numberpicker . Values in numberpicker i need pass from activity. How to achieve pass values i think i should extend PopupWindow class and create custom PopupWindow or is another solutiion. Thank the cose is as follow
ViewGroup parent = (ViewGroup) view.getParent();
final View v = getLayoutInflater().inflate(R.layout.activity_duration, parent, false);
np = (NumberPicker)findViewById(R.id.durationPicker);
popupWindowDuration = new PopupWindow(v, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, true);
popupWindowDuration.showAtLocation(findViewById(R.id.main_content), Gravity.CENTER, 0, 0); here
Already set at manifest
<activity android:theme="#android:style/Theme.Dialog">
And second is how to set background transparent . Becase this code hide previous popupwindows.
Set following code in your second activity's onCreate method below setContentView
getWindow().getDecorView().setBackground(new ColorDrawable(Color.TRANSPARENT));