How to set ListView width in PopupWindow on Android?

I inflated ListView as contentView in PopupWindow. If I do not set the width and height, I do not see PopupWindow. If I install them like this:

setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 

The layout is set as "fill_parent". Why?

The layout attributes of the ListView and ListView are set to "wrap_content". Any suggestion? Thanks.

+4
source share
5 answers

This implements the implementation of the SimpleListView class. I know this is inefficient, and I'm sure it contains errors, but shows how to measure the width of the list.

I also recommend reading this article .

  public class SimpleListView extends AdapterView<ListAdapter> { private ListAdapter adapter; private Drawable divider; private int dividerHeight; private boolean clipDivider; public SimpleListView( final Context context ) { this( context, null ); } public SimpleListView( final Context context, final AttributeSet attrs ) { this( context, attrs, android.R.attr.listViewStyle ); } public SimpleListView( final Context context, final AttributeSet attrs, final int defStyle ) { super( context, attrs, defStyle ); final TypedArray array = context.obtainStyledAttributes( attrs, R.styleable.SimpleListView, defStyle, 0 ); final Drawable dividerAttribute = array.getDrawable( R.styleable.SimpleListView_android_divider ); if( dividerAttribute != null ) { setDivider( dividerAttribute ); } final int dividerHeightAttribute = array.getDimensionPixelSize( R.styleable.SimpleListView_android_dividerHeight, 0 ); if( dividerHeightAttribute != 0 ) { setDividerHeight( dividerHeightAttribute ); } array.recycle(); } public Drawable getDivider() { return this.divider; } public void setDivider( final Drawable newDivider ) { if( newDivider != null ) { this.dividerHeight = newDivider.getIntrinsicHeight(); this.clipDivider = newDivider instanceof ColorDrawable; } else { this.dividerHeight = 0; this.clipDivider = false; } this.divider = newDivider; requestLayout(); } public int getDividerHeight() { return this.dividerHeight; } public void setDividerHeight( final int newHeight ) { this.dividerHeight = newHeight; requestLayout(); } @Override public ListAdapter getAdapter() { return this.adapter; } @Override public void setAdapter( final ListAdapter adapter ) { this.adapter = adapter; removeAllViewsInLayout(); requestLayout(); } @Override public View getSelectedView() { throw new UnsupportedOperationException( "SimpleListView.getSelectedView() is not supported" ); } @Override public void setSelection( final int position ) { throw new UnsupportedOperationException( "SimpleListView.setSelection(int) is not supported" ); } @Override protected void onMeasure( final int widthMeasureSpec, final int heightMeasureSpec ) { super.onMeasure( widthMeasureSpec, heightMeasureSpec ); final int widthMode = MeasureSpec.getMode( widthMeasureSpec ); int widthSize = MeasureSpec.getSize( widthMeasureSpec ); final int heightMode = MeasureSpec.getMode( heightMeasureSpec ); int heightSize = MeasureSpec.getSize( heightMeasureSpec ); int innerWidth = 0; int innerHeight = 0; final int itemCount = this.adapter == null ? 0 : this.adapter.getCount(); if( itemCount > 0 && ( widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY ) ) { for( int i = 0; i < itemCount; ++i ) { final View convertView = getChildAt( i ); final View child = this.adapter.getView( i, convertView, this ); if( convertView == null ) { LayoutParams params = child.getLayoutParams(); if( params == null ) { params = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT ); child.setLayoutParams( params ); } addViewInLayout( child, i, params ); } if( child.getLayoutParams() instanceof MarginLayoutParams ) { measureChildWithMargins( child, widthMeasureSpec, 0, heightMeasureSpec, 0 ); } else { measureChild( child, widthMeasureSpec, heightMeasureSpec ); } innerWidth = Math.max( innerWidth, child.getMeasuredWidth() ); innerHeight += child.getMeasuredHeight(); } innerHeight += ( itemCount - 1 ) * this.dividerHeight; } if( widthMode != MeasureSpec.EXACTLY ) { final int newWidthSize = getPaddingLeft() + getPaddingRight() + innerWidth; widthSize = widthMode == MeasureSpec.AT_MOST ? Math.min( widthSize, newWidthSize ) : newWidthSize; } if( heightMode != MeasureSpec.EXACTLY ) { final int newHeightSize = getPaddingTop() + getPaddingBottom() + innerHeight; heightSize = heightMode == MeasureSpec.AT_MOST ? Math.min( heightSize, newHeightSize ) : newHeightSize; } setMeasuredDimension( widthSize, heightSize ); } @Override protected void onLayout( final boolean changed, final int left, final int top, final int right, final int bottom ) { super.onLayout( changed, left, top, right, bottom ); if( this.adapter == null ) { return; } positionItems(); invalidate(); } private void positionItems() { int top = getPaddingTop(); final int left = getPaddingLeft(); for( int i = 0, count = getChildCount(); i < count; ++i ) { final View child = getChildAt( i ); final int width = child.getMeasuredWidth(); final int height = child.getMeasuredHeight(); child.layout( left, top, left + width, top + height ); top += height + this.dividerHeight; } } @Override protected void dispatchDraw( final Canvas canvas ) { final boolean drawDividers = this.dividerHeight > 0 && this.divider != null; if( drawDividers ) { final int left = getPaddingLeft(); final int right = getWidth() - getPaddingRight(); final Rect dividerBounds = new Rect( left, 0, right, 0 ); for( int i = 0, count = getChildCount(); i < count - 1; ++i ) { final View child = getChildAt( i ); dividerBounds.top = dividerBounds.bottom + child.getMeasuredHeight(); dividerBounds.bottom = dividerBounds.top + this.dividerHeight; drawDivider( canvas, dividerBounds ); } } super.dispatchDraw( canvas ); } private void drawDivider( final Canvas canvas, final Rect bounds ) { if( !this.clipDivider ) { this.divider.setBounds( bounds ); } else { canvas.save(); canvas.clipRect( bounds ); } this.divider.draw( canvas ); if( this.clipDivider ) { canvas.restore(); } } } 
+1
source

Here's how to do it:

 // Don't use this. It causes ListView to have strange widths, similar to "fill_parent" //popupWindow.setWindowLayoutMode(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT); // Instead, use this: final int NUM_OF_VISIBLE_LIST_ROWS = 4; listView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); popupWindow.setWidth(listView.getMeasuredWidth()); popupWindow.setHeight((listView.getMeasuredHeight() + 10) * NUM_OF_VISIBLE_LIST_ROWS); 

Note that setWidth() and setHeight() use the values ​​of the original pixel, so you need to adjust different screen sizes and different densities.

+3
source

My solution overrides the onMeasure ListView as follows:

 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int maxWidth = meathureWidthByChilds() + getPaddingLeft() + getPaddingRight(); super.onMeasure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY), heightMeasureSpec); } public int meathureWidthByChilds() { int maxWidth = 0; View view = null; for (int i = 0; i < getAdapter().getCount(); i++) { view = getAdapter().getView(i, view, this); view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); if (view.getMeasuredWidth() > maxWidth){ maxWidth = view.getMeasuredWidth(); } } return maxWidth; } 
+3
source

I ran into the same problem. The only solution I found was to set the width of the PopupWindow to the exact width of the ListView:

 listView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); final PopupWindow popup = new PopupWindow(listView, listView.getMeasuredWidth(), ViewGroup.LayoutParams.WRAP_CONTENT, true); popup.showAsDropDown(anchor); 

Unfortunately, this will not solve the problem completely. The ListView measures itself so that it can only wrap its first child. If, for example, the second child is wider than the first, the second will be circumcised.

I'm not sure, but the only way to change the way ListView itself is is to subclass it and override the onMeasure() method. I am trying to do it now, and I will write a comment here if I succeed.

+1
source

You can put a temporary control in an XML file, for example:

 <?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" > <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:entries="@array/popitems" /> <TextView android:layout_width="wrap_content" android:layout_height="0dp" android:text="My name is ZhengGuangyu" android:visibility="invisible" /> </LinearLayout> 

Pay attention to the text of the TextView . You must put long text on your list.

If your list contains the lines below:

  • an Apple
  • Orange
  • pig
  • cat

then you must write the orange character of the TextView ;

Then use this:

 popupWindow.setWidth(LayoutParams.WRAP_CONTENT); popupWindow.setHeight(LayoutParams.WRAP_CONTENT); 

This works for me, you can try.

0
source

All Articles