android 关于listview item设置高度的问题解决方法

关于listview,做andriod开发都必须知道的,我写了一个简单的adapter,在这不考虑什么缓存机制就单单为了显示一下而已:

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity" ;
    private ListView listview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.content_main);
        listview = (ListView) findViewById(R.id.listview);
        listview.setAdapter(new MyAdapter());
    }
    class MyAdapter extends BaseAdapter{
        @Override
        public int getCount() {
            return 20;
        }
        @Override
        public Object getItem(int position) {
            return null;
        }
        @Override
        public long getItemId(int position) {
            return 0;
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            return View.inflate(MainActivity.this,R.layout.item,null);
        }
    }
}
效果图:

android 关于listview item设置高度的问题解决方法_第1张图片

如果想设置item的高度为某一个特定的值 比如为200dp,也许你会说很简单,这么做就搞定

xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="300dp">
            android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"
        />
            android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="今天天气真好"
        android:layout_marginTop="10dp"
        android:textColor="#ff00ff"
        android:gravity="center"
        />

但是很遗憾告诉你这样是不行的,原因在哪?

我们都知道在xml中带layout_xxx这样的最后都会封装成LayoutParam 这个是父view决定给子view的宽度和高度,我们到ListView的源码中

private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
        boolean selected) {
    View child;


    if (!mDataChanged) {
        // Try to use an existing view for this position
        child = mRecycler.getActiveView(position);
        if (child != null) {
            // Found it -- we're using an existing child
            // This just needs to be positioned
            setupChild(child, position, y, flow, childrenLeft, selected, true);

            return child;
        }
    }

    // Make a new view for this position, or convert an unused view if possible
    child = obtainView(position, mIsScrap);

    // This needs to be positioned and measured
    setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);

    return child;
}
这是listview 添加和计算每个item的方法 在AbsListView中有个obtainView()方法,

View obtainView(int position, boolean[] isScrap) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView");

    isScrap[0] = false;

    // Check whether we have a transient state view. Attempt to re-bind the
    // data and discard the view if we fail.
    final View transientView = mRecycler.getTransientStateView(position);
    if (transientView != null) {
        final LayoutParams params = (LayoutParams) transientView.getLayoutParams();

        // If the view type hasn't changed, attempt to re-bind the data.
        if (params.viewType == mAdapter.getItemViewType(position)) {
            final View updatedView = mAdapter.getView(position, transientView, this);

            // If we failed to re-bind the data, scrap the obtained view.
            if (updatedView != transientView) {
                setItemViewLayoutParams(updatedView, position);
                mRecycler.addScrapView(updatedView, position);
            }
        }

        isScrap[0] = true;

        // Finish the temporary detach started in addScrapView().
        transientView.dispatchFinishTemporaryDetach();
        return transientView;
    }

    final View scrapView = mRecycler.getScrapView(position);
    final View child = mAdapter.getView(position, scrapView, this);
    if (scrapView != null) {
        if (child != scrapView) {
            // Failed to re-bind the data, return scrap to the heap.
            mRecycler.addScrapView(scrapView, position);
        } else {
            isScrap[0] = true;

            // Finish the temporary detach started in addScrapView().
            child.dispatchFinishTemporaryDetach();
        }
    }

    if (mCacheColorHint != 0) {
        child.setDrawingCacheBackgroundColor(mCacheColorHint);
    }

    if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
        child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
    }

    setItemViewLayoutParams(child, position);

    if (AccessibilityManager.getInstance(mContext).isEnabled()) {
        if (mAccessibilityDelegate == null) {
            mAccessibilityDelegate = new ListItemAccessibilityDelegate();
        }
        if (child.getAccessibilityDelegate() == null) {
            child.setAccessibilityDelegate(mAccessibilityDelegate);
        }
    }

    Trace.traceEnd(Trace.TRACE_TAG_VIEW);

    return child;
}
查看这个方法
setItemViewLayoutParams(child, position);

这个方法的源码:

private void setItemViewLayoutParams(View child, int position) {
    final ViewGroup.LayoutParams vlp = child.getLayoutParams();
    LayoutParams lp;
    if (vlp == null) {
        lp = (LayoutParams) generateDefaultLayoutParams();
    } else if (!checkLayoutParams(vlp)) {
        lp = (LayoutParams) generateLayoutParams(vlp);
    } else {
        lp = (LayoutParams) vlp;
    }

    if (mAdapterHasStableIds) {
        lp.itemId = mAdapter.getItemId(position);
    }
    lp.viewType = mAdapter.getItemViewType(position);
    if (lp != vlp) {
      child.setLayoutParams(lp);
    }
}
主要的逻辑在这几行代码

if (vlp == null) {
    lp = (LayoutParams) generateDefaultLayoutParams();
} else if (!checkLayoutParams(vlp)) {
    lp = (LayoutParams) generateLayoutParams(vlp);
} else {
    lp = (LayoutParams) vlp;
}
第一个if是判断这个params是否等于null,等于null的话就给它一个默认的,默认的是这个

@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
    return new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT, 0);
}
接着上面的分析 第二个if判断

!checkLayoutParams(vlp)
 
  
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    return p instanceof AbsListView.LayoutParams;
}
查看这个是不是
AbsListView.LayoutParams类型的 很显然我们没有对它做任何的事  显然不是这个类型的,那么系统会给他创建一个
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
    return new LayoutParams(p);
}

这就是为什么我们在xml中设置高度无效的原因,因为它在底层已经给我们设置了,你在外面设置导致无效


解决这个问题有二种办法:

1:在xml外层套一层布局,

xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="300dp">
            android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"
        />
            android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="今天天气真好"
        android:layout_marginTop="10dp"
        android:textColor="#ff00ff"
        android:gravity="center"
        />

效果图:

android 关于listview item设置高度的问题解决方法_第2张图片

2:

在adapter中的getView()方法中添加这个

@Override
public View getView(int position, View convertView, ViewGroup parent) {
   View view =  View.inflate(MainActivity.this,R.layout.item,null);
    AbsListView.LayoutParams param = new AbsListView.LayoutParams(300,200);
    view.setLayoutParams(param);
    return view;
}

搞定,OK



你可能感兴趣的:(android)