I'm starting to doubt that in the context of a PopupWindow
that -2, -2 actually means WRAP_CONTENT
rather I think that its just interpreting it as width = -2 height = -2
This is from the Android Source
public PopupWindow(View contentView, int width, int height, boolean focusable) {
if (contentView != null) {
mContext = contentView.getContext();
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
setContentView(contentView);
setWidth(width);
setHeight(height);
setFocusable(focusable);
}
where setWidth(int width)
is just a simple mWidth = width;
I think what you are looking for instead is the setWindowLayoutMode (int widthSpec, int heightSpec) method. That is where you should pass in the WRAP_CONTENT
If all else fails, measure the width and height of the TextView
and then use setWidth
and setHeight
as expected.
Edit:
Just tried it out for myself and added this line of code to make it work
// -2 means WRAP_CONTENT THIS TRIGGERS THE PROBLEM
popup = new PopupWindow(getPopupContent(), 200, 100);
popup.setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
// When you specify the dimensions everything goes fine
//popup = new PopupWindow(getPopupContent(), 200, 100);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
I left the original comments in there so you can see for yourself where everything should go.
Edit 2:
Okay, so I took your code you posted and verbatim tried it on my device, and you were right, it didn't work because when Android is determining which views to layout to put it in, it relies on the width and height you provided. What that means is since you are using 0 and 0 as your width and height, Android will come along and say, "Oh look, the box is only a 0 by 0 box so I can display it as a popup below the content." This isn't what is desired though! The thing is, you know that the box is going to be bigger than that, so lets start putting in some different numbers and seeing the results that come from it.
popup = new PopupWindow(getPopupContent(), 1, 1);
Now when I go and click the bottom box, notice that it jumps above. (See screenshot) That's because Android KNOWS that the width and height are 1 (as I set in the constructor) and that the available screen space below the list item is 0. Well, if there's not enough room to display it there, then it must above then!
But wait! What if like in my current example the string adds on more content each time? Well, this is where things get interesting. You see, in my next screenshot that the popup, even though it should be displayed up since it is 6 lines long now, is rather displayed at the bottom! Oh crap, right! That's because it is measuring against the 1 1
I used in the constructor. Well what are some solutions to this then?
Solution One: My preferred way would be to take a guess at what the average max height of the TextView
would be and then simply toss in one generically big number.
popup = new PopupWindow(getPopupContent(), 300, 300); //just guessing it won't get bigger than that
Solution Two: This is more proper, but you are sacrificing a little speed to do it. Using the Paint
class to measure what the text content size is going to be and passing that into the the setWidth()
and setHeight()
before you display the popup. I went ahead and built an almost complete solution, but I didn't measure in padding and stuff (see the comment)
private int maxWidth;
private int maxHeight;
private Paint p = new Paint();
private Rect bounds = new Rect();
private View getPopupContent() {
maxWidth = 0;
maxHeight = 0;
TextView popupContent = new TextView(this);
popupContent.setText(popupText += "
" + DEMO);
popupContent.setTextColor(Color.parseColor("#5000ae"));
popupContent.setBackgroundColor(Color.parseColor("#ff00ff"));
popupContent.setPadding(10, 20, 20, 10);
//the measure can only work line by line so I split it up by line
String[] temp = popupText.split("
");
for (String s : temp){
//measure each line of string and get its width and height
p.getTextBounds(s, 0, s.length(), bounds);
//keep a running total of the longest width
maxWidth = (bounds.width() > maxWidth) ? bounds.width() : maxWidth;
//add up each of the heights
maxHeight += bounds.height();
}
//also take in account the padding too... if you REALLY want it to be completely robust
//probably adding another 20 or 30 to the maxHeight should be good
return popupContent;
}
And then in the onClick()
I added these two lines
popup.setHeight(maxHeight);
popup.setWidth(maxWidth);