كيفية ضبط عرض ListView في popupwindow على Android؟
-
28-09-2019 - |
سؤال
لقد قمت بتضخيم ListView باعتباره ContentView في popupwindow. إذا لم أقم بتعيين العرض والارتفاع ، فلا يمكنني رؤية popupwindow. إذا قمت بتعيينهم مثل هذا:
setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
يتم تعيين التصميم مثل "Fill_Parent". لماذا ا؟
يتم تعيين جميع سمات التخطيط لعنصر ListView و ListView على "WRAP_CONTENT". أي اقتراح؟ شكرًا.
المحلول
ها هي 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();
}
}
}
نصائح أخرى
هذه هي كيفية القيام بذلك:
// 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()
استخدم قيم البيكسل الخام ، لذلك تحتاج إلى ضبط لأحجام الشاشة المختلفة والكثافة المختلفة.
يتم تجاوز الحل الخاص بي من 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;
}
أنا واجهت نفس المشكلة. الحل الوحيد الذي وجدته هو ضبط عرض popupwindow على العرض الدقيق لمقصة القائمة:
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()
طريقة. أحاول أن أفعل ذلك الآن وسأكتب تعليقًا هنا إذا نجحت في ذلك.