1. 程式人生 > >android 關於listview item設定高度的問題解決方法

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);
        }
    }
}
效果圖:


如果想設定item的高度為某一個特定的值 比如為200dp,也許你會說很簡單,這麼做就搞定

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

但是很遺憾告訴你這樣是不行的,原因在哪?

我們都知道在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"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="300dp">
    <ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
/>
    <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="今天天氣真好"
android:layout_marginTop="10dp"
android:textColor="#ff00ff"
android:gravity="center"
/>
</LinearLayout>
</LinearLayout>
效果圖:


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