Как установить ширину ListView в PopupWindow на Android? - PullRequest
4 голосов
/ 03 ноября 2010

Я надувал ListView как contentView в PopupWindow. Если я не установлю ширину и высоту, я не вижу PopupWindow. Если я установлю их так:

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

Макет устанавливается как «fill_parent». Почему?

Для атрибутов макета элемента ListView и ListView установлено значение "wrap_content". Любое предложение? Спасибо.

Ответы [ 4 ]

3 голосов
/ 08 октября 2012

Мое решение переопределено на Измере ListView следующим образом:

@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 голосов
/ 28 августа 2012

Вот как это сделать:

// 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);

Обратите внимание, что setWidth() и setHeight() используют необработанные значения пикселей, поэтому вам необходимо настроить их для разных размеров экрана и различной плотности.

1 голос
/ 02 декабря 2010

Вот реализация класса SimpleListView. Я знаю, что это неэффективно, и я почти уверен, что содержит ошибки, но показывает, как измерить ширину представления списка.

Я также рекомендую прочитать эту статью .

    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 голос
/ 30 ноября 2010

Я столкнулся с той же проблемой.Единственное решение, которое я нашел, - установить ширину PopupWindow равной ширине 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);

К сожалению, это не решает проблему полностью.ListView измеряет себя так, что может обернуть только свой первый дочерний элемент.Если, например, второй дочерний элемент шире первого, второй дочерний элемент будет обрезан.

Я не уверен, но единственный способ изменить способ измерения ListView сам по себе - это создать подкласс и переопределитьметод onMeasure().Я пытаюсь сделать это сейчас и напишу здесь комментарий, если мне это удастся.

...