2010-11-03 8 views
4

J'ai gonflé un ListView en tant que contentView dans un PopupWindow. Si je ne définis pas la largeur & hauteur, je ne peux pas voir le PopupWindow. Si je les mets comme ceci:Comment définir la largeur de la ListView dans un PopupWindow sur Android?

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

La mise en page est Posé comme « fill_parent ». Pourquoi?

Les attributs de disposition des éléments ListView et ListView sont tous définis sur "wrap_content". Une suggestion? Merci.

Répondre

1

Voici l'implémentation de la classe SimpleListView. Je sais que c'est inefficace et je suis sûr qu'il contient des erreurs, mais il montre comment mesurer une largeur d'une vue de liste.

Je recommande également de lire 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

J'ai fait face au même problème. La seule solution que je trouve est de définir la largeur de popupWindow à la largeur exacte du 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); 

Malheureusement, il ne résout pas entièrement le problème. Le ListView se mesure de sorte qu'il ne peut envelopper que son premier enfant. Si, par exemple, le deuxième enfant est plus large que le premier, le deuxième enfant sera coupé.

Je ne suis pas sûr, mais la seule façon de changer la façon dont ListView se mesure est de le sous-classer et de surcharger la méthode onMeasure(). J'essaye de le faire maintenant et j'écrirai un commentaire ici si j'y réussis.

+0

Merci pour le partage! – shiami

+0

J'ai écrit une classe 'SimpleListView' qui étend la classe' AdapterView'. Il a de très mauvaises performances mais il peut changer sa largeur en fonction des largeurs de ses enfants. Et je peux partager le code source si vous le souhaitez. – Michael

+0

Pixie, pouvez-vous partager votre réponse – pengwang

3

Voici comment faire:

// Don't use this. It causes ListView's 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); 

Notez que setWidth() et setHeight() utilisation des valeurs de pixel brut, de sorte que vous devez régler pour différentes tailles d'écran et des densités différentes.

0

Vous pouvez mettre un contrôle temporaire dans le fichier XML, tels que:

<?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> 

S'il vous plaît faites attention au texte de la TextView. Vous devriez mettre le texte le plus long de votre listview.

Si votre listview contient les chaînes ci-dessous:

  • pomme
  • d'orange
  • porc
  • chat

alors vous devriez écrire l'orange au TextView;

Utilisez ensuite ceci:

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

Il fonctionne pour moi, vous pouvez avoir un essai.

3

Ma solution est de passer outre onMeasure ListView comme ceci:

@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; 
} 
Questions connexes