2

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

2 Answers2

1

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>
VictorPanda
  • 141
  • 4
  • (int)event.getY() and (int)event.getX() ,whats event here? It's showing up as an error –  May 24 '17 at 21:01
  • I don't know the target view what you actually need, that touch it to show the popup window, so I override the activity `public boolean onTouchEvent(MotionEvent event)` ,so the param event is that event in `(int)event.getX()`, if your target view is `mapInsideContainer `, you can use `public void setOnTouchListener(OnTouchListener l)`. – VictorPanda May 24 '17 at 23:06
  • doesnt work either. is there an easier way to calculate –  May 25 '17 at 01:45
0

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:

enter image description here

And for x = 150 and y = 200, I get:

enter image description here

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.

Jason Powell
  • 361
  • 4
  • 12
  • if you can help with details that will be useful as the transparent activity part is confusing and i have no idea how that would solve the problem for popup window not displaying right above the point of touch –  May 25 '17 at 01:46
  • @JusticeBauer I edited my answer to provide some details. I'll edit more tonight but I need to go now. – Jason Powell May 25 '17 at 23:00
  • @JusticeBauer I added more details. – Jason Powell May 26 '17 at 05:01
  • thank yoy jason. will test it out soon and let you know , how it goes! –  May 28 '17 at 14:14
  • what is xcord here? –  Jun 01 '17 at 19:26
  • @MarissaNicholas xCoord and yCoord are the touch coordinates passed to the activity. My suggestion was to override onTouchEvent() to get the coordinates, then start the transparent activity and pass it those values with the Intent. – Jason Powell Jun 02 '17 at 02:02