1. 程式人生 > >淺談android中的ListView之解決ScrollView和ListView巢狀衝突(實際上一切都是浮雲,閒的蛋疼)(一)

淺談android中的ListView之解決ScrollView和ListView巢狀衝突(實際上一切都是浮雲,閒的蛋疼)(一)

     相信大家都已經可以熟練使用ListView和GridView,大神們估計都在使用RecyclerView了。如果還在使用ListView,你肯定有這樣的一個深刻的感受,那就是在做一個APP的時候使用ListView和GridView很頻繁,並且經常會遇到一個頁面中除了有ListView或GridView可能還有一些其他的內容,但是可能內容很多,你第一時間就會想到讓它整體滑動即可,那就是在總的佈局外面包裹一個ScrollView。也就是出現了ScrollView中巢狀一個ListView的場景,或者你的ScrollView巢狀多個ListView或者GridView的時候。我們自認為出現場景應該是整體內容會滑動,但是你會驚訝的發現並不是這樣的,你會發現如果是嵌套了一個ListView或者GridView的時候,ListView只會顯示一個Item項,GridView也會只顯示一行。看來我們還是錯了,具體是什麼樣子的下面我們通過一個demo來看看,然後分析一下為什麼會出現這樣的結果,以及最後解決的辦法。

 我就簡單寫了一個ListView只要能說明問題即可,至於ListView的優化等問題不是本篇部落格的重點,將會在後續的部落格出現。

佈局檔案(注意:仔細看清楚ListView的layout高度的屬性是warp_content)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/parent_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <include
        android:id="@+id/include_header_home"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        layout="@layout/header_home" />

    <ScrollView
        android:id="@+id/scrollview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="380dp"
                android:scaleType="fitXY"
                android:src="@drawable/center_image" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" >

                <LinearLayout
                    android:id="@+id/linear"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="#FFF"
                    android:orientation="horizontal" >

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#44000000" />

                    <TextView
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/all_category_arrow_down"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="商家分類" />

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#44000000" />

                    <TextView
                        android:id="@+id/test"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/all_category_arrow_down"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="智慧排序" />

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#77000000" />

                    <TextView
                        android:id="@+id/wei_express"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/slip_off_img_bd_express"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="微送" />
                </LinearLayout>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="0.1dp"
                    android:background="#44000000" />
            </LinearLayout>

            <ListView
                android:id="@+id/listview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:divider="#33000000"
                android:dividerHeight="0.1dp" >
            </ListView>
        </LinearLayout>
    </ScrollView>

</LinearLayout>

MainActivity.java

package com.mikyou.listviewtest;

import com.mikyou.utils.SystemStatusManager;

import android.app.Activity;
import android.database.DataSetObserver;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;

public class MainActivity extends Activity {
	private ListView  mListView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setTranslucentStatus();
		setContentView(R.layout.activity_main);
		initData();
		initView();
	}
	private void initView() {
		mListView=(ListView) findViewById(R.id.listview);
		mListView.setAdapter(new MyAdapter());
	}
	class MyAdapter extends BaseAdapter{

		@Override
		public int getCount() {
			return 7;
		}

		@Override
		public Object getItem(int position) {
			return null;
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View view=View.inflate(MainActivity.this, R.layout.home_list_item, null);
			return view;
		}

	}
	private void initData() {

	}
	private void setTranslucentStatus() {//沉浸標題欄效果
		// TODO Auto-generated method stub
		if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
			Window win=getWindow();
			WindowManager.LayoutParams winParams=win.getAttributes();
			final int bits=WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
			winParams.flags |=bits;
			win.setAttributes(winParams);
		}
		SystemStatusManager tintManager = new SystemStatusManager(this);
		tintManager.setStatusBarTintEnabled(true);
		tintManager.setStatusBarTintResource(0);
		tintManager.setNavigationBarTintEnabled(true);
	}
}


執行的結果:

注意:你會發現只會顯示一個,而我的設定adapter的時候明明設定的是7個,為什麼只顯示一個呢??這是為什麼,這就是意味ListView和ScrollView的巢狀帶來的問題,因為ListView和GridView本身就是繼承於ScrollView,而ScrollView中再次巢狀一個ScrollVewi就會出現高度測量的問題,我們都知道ListView也可以上下滑動,當ListView中的內容很多的時候,螢幕不足以顯示的時候才會滑動顯示。那為什麼只會顯示一項呢?其實仔細分析一下很簡單就是ListView高度不夠,如果高度達到一定的話,就會出現其他的Item項,注意看我們的佈局ListView的Wrap_content,也就是測量裡面的內容的高度來得到的,也就對應著android中的VIew測量的模式中的AT_MOST,這種測量模式實際上系統是不會給你真正的測的,而是根據測量其內部的內容來給出一個適合的尺寸,是系統只會測EXACTLY模式即為對應match_parent和指定明確的尺寸。我們還知道ListView每個Item載入模式通過getView方法一個一個的加載出來的,也就是當你第一個Item載入完後,它只測量第一個Item,所以只顯示第一個Item,因為高度不夠,只能顯示第一個。如果不信,我們可以作如下的兩個實驗,第一打印出此時ListVIew的高度看看是不是此時ListView的高度是不是等於第一個Item的高度,第二就是我不指定warp_content,我指定一個明確尺寸看看。

第一個實驗:比較第一個Item高度和ListVIew總高度,來說明此時ScrollView測量第一個Item來作為整個ListView的高度。

通過給ListView新增getViewTreeObserver().addOnGlobalLayoutListener事件,然後在回撥方法中去得到高度,注意:因為我們直接或者間接在Activity中的OnCreate方法中去得到高度是為0,因為此時Activity中的View窗體樹並沒有繪製完畢,該方法就是通過監聽整個View的樹繪製完畢後才會回撥,所以在該方法才能得到高度

package com.mikyou.listviewtest;

import com.mikyou.utils.SystemStatusManager;

import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;

public class MainActivity extends Activity {
	private ListView  mListView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setTranslucentStatus();
		setContentView(R.layout.activity_main);
		initData();
		initView();
	}
	private void initView() {
		mListView=(ListView) findViewById(R.id.listview);
		mListView.setAdapter(new MyAdapter());
		//
		mListView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

			@Override
			public void onGlobalLayout() {
				View firstItemView=mListView.getAdapter().getView(0, null, mListView);//得到第一個Item
				firstItemView.measure(0, 0);
				System.out.println("第一個Item的高度:"+firstItemView.getMeasuredHeight());
				System.out.println("mListView的高度:"+mListView.getHeight());				
			}
		});
	}
	class MyAdapter extends BaseAdapter{

		@Override
		public int getCount() {
			return 7;
		}

		@Override
		public Object getItem(int position) {
			return null;
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View view=View.inflate(MainActivity.this, R.layout.home_list_item, null);
			return view;
		}

	}
	private void initData() {

	}
	private void setTranslucentStatus() {//沉浸標題欄效果
		// TODO Auto-generated method stub
		if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
			Window win=getWindow();
			WindowManager.LayoutParams winParams=win.getAttributes();
			final int bits=WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
			winParams.flags |=bits;
			win.setAttributes(winParams);
		}
		SystemStatusManager tintManager = new SystemStatusManager(this);
		tintManager.setStatusBarTintEnabled(true);
		tintManager.setStatusBarTintResource(0);
		tintManager.setNavigationBarTintEnabled(true);
	}
}


在Logcat中打印出來的結果是:

所以第一個實驗證明咱們的觀點是正確的

那麼接下進行第二個實驗:通過修改佈局檔案中的ListView的Layout_Height屬性,我們指定明確尺寸看看會發生什麼?

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/parent_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <include
        android:id="@+id/include_header_home"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        layout="@layout/header_home" />

    <ScrollView
        android:id="@+id/scrollview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="380dp"
                android:scaleType="fitXY"
                android:src="@drawable/center_image" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" >

                <LinearLayout
                    android:id="@+id/linear"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="#FFF"
                    android:orientation="horizontal" >

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#44000000" />

                    <TextView
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/all_category_arrow_down"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="商家分類" />

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#44000000" />

                    <TextView
                        android:id="@+id/test"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/all_category_arrow_down"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="智慧排序" />

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#77000000" />

                    <TextView
                        android:id="@+id/wei_express"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/slip_off_img_bd_express"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="微送" />
                </LinearLayout>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="0.1dp"
                    android:background="#44000000" />
            </LinearLayout>

            <ListView
                android:id="@+id/listview"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:divider="#33000000"
                android:dividerHeight="0.1dp" >
            </ListView>
        </LinearLayout>
    </ScrollView>

</LinearLayout>

執行結果和Logcat列印的結果:

    大家會看到這時候就出現了2個Item,但是最後一個Item的還是顯示不夠完全,這因為我們的高度還是不夠,我們在佈局檔案指定了ListView的高度為300dp,會發現logcat 輸出的高度600,這個就是另外的知識了,因為最後打印出來以及View繪製的尺寸都是Px為單位的,但是這個dp實際上就是邏輯單位,這也是為了相容和適配Android中眾多不同手機解析度。由於在不同的手機解析度中PX和DP換算率也不一樣,由於我的模擬器是1280*768的尺寸,他們換算率為1:2,也就是1dp等於2px,我們設定了300dp,所以打印出來就是600,這樣能解釋為什麼這第二Item顯示不完全,因為一個Item需要321px,兩個就是641px,而我們設定300dp也就是600px小於641px所以肯定顯示不完全了。

   通過如下分析,本以為可以找到一個解決辦法,它也確實可以顯示出其他的Item,但是有個很大的問題就是你知道每個Item的高度嗎?由於每個不同ListView中的Item的樣式不一樣,高度自然就不一樣,難道我們每次都去寫死它的高度嗎?而且如果從網路中獲取資料,Item的個數也是在變得,整個ListVIew的高度也是在變的。那麼這個方法就斷了嗎?其實不然,我們再順著這個思路想想,這個思路就是“只要去修改ListView的高度就能實現顯示其他的Item”,但是現在問題就是如何去得到一個合適的ListView的高度呢,其實最後的思路就是這樣的:“動態的測量ListView的高度然後動態的去修改ListView的高度就能實現顯示其他的Item”。

  測量的大致思路如下:

   就是通過ListView物件去getAdapter去得到介面卡中的每個Item的然後通過一個迴圈遍歷地去測量每個Item的高度,然後通過累加這些Item的高度,最後得到總的高度,再把這個高度設定給ListView即可,這裡我將這個功能的實現封裝一個工具方法,只要簡單地將ListVIew物件傳入即可。

測量高度的方法:

package com.mikyou.utils;

import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ListAdapter;
import android.widget.ListView;

public class MikyouMetricListViewHeightUtils {
	public static void setListViewHeight(ListView lv){
		if (lv==null) {
			return ;
		}
		ListAdapter adapter=lv.getAdapter();
		if (adapter==null) {
			return ;
		}
		int totalHeight=0;
		for (int i = 0; i < adapter.getCount(); i++) {
			View listItem=adapter.getView(i, null, lv);
			listItem.measure(0, 0);
			totalHeight+=listItem.getMeasuredHeight();
		}
		LayoutParams params=lv.getLayoutParams();
		params.height=totalHeight+(lv.getDividerHeight()*(lv.getCount()-1));//這裡還將分割線的高度考慮進去了,統計出所有分割線佔有的高度和
		lv.setLayoutParams(params);
	}
}


通過使用這個方法後直接將佈局檔案寫成Warp_content即可,現在不需要直接指定了明確尺寸了,看看最後的實現效果:

package com.mikyou.listviewtest;


import com.mikyou.utils.MikyouMetricListViewHeightUtils;
import com.mikyou.utils.SystemStatusManager;

import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;

public class MainActivity extends Activity {
	private ListView  mListView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setTranslucentStatus();
		setContentView(R.layout.activity_main);
		initData();
		initView();
	}
	private void initView() {
		mListView=(ListView) findViewById(R.id.listview);
		mListView.setAdapter(new MyAdapter());
		/**
		 * @author zhongqihong
		 * 解決ScrollView與ListView衝突的問題
		 * 該方法需要在setAdapter之後呼叫
		 * */
		MikyouMetricListViewHeightUtils.setListViewHeight(mListView);
		
		
		
		
		mListView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

			@Override
			public void onGlobalLayout() {
				View firstItemView=mListView.getAdapter().getView(0, null, mListView);//得到第一個Item
				firstItemView.measure(0, 0);
				System.out.println("第一個Item的高度:"+firstItemView.getMeasuredHeight());
				System.out.println("mListView的高度:"+mListView.getHeight());				
			}
		});
	}
	class MyAdapter extends BaseAdapter{

		@Override
		public int getCount() {
			return 7;
		}

		@Override
		public Object getItem(int position) {
			return null;
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View view=View.inflate(MainActivity.this, R.layout.home_list_item, null);
			return view;
		}

	}
	private void initData() {

	}
	private void setTranslucentStatus() {//沉浸標題欄效果
		// TODO Auto-generated method stub
		if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
			Window win=getWindow();
			WindowManager.LayoutParams winParams=win.getAttributes();
			final int bits=WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
			winParams.flags |=bits;
			win.setAttributes(winParams);
		}
		SystemStatusManager tintManager = new SystemStatusManager(this);
		tintManager.setStatusBarTintEnabled(true);
		tintManager.setStatusBarTintResource(0);
		tintManager.setNavigationBarTintEnabled(true);
	}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/parent_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <include
        android:id="@+id/include_header_home"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        layout="@layout/header_home" />

    <ScrollView
        android:id="@+id/scrollview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="380dp"
                android:scaleType="fitXY"
                android:src="@drawable/center_image" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" >

                <LinearLayout
                    android:id="@+id/linear"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="#FFF"
                    android:orientation="horizontal" >

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#44000000" />

                    <TextView
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/all_category_arrow_down"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="商家分類" />

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#44000000" />

                    <TextView
                        android:id="@+id/test"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/all_category_arrow_down"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="智慧排序" />

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#77000000" />

                    <TextView
                        android:id="@+id/wei_express"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/slip_off_img_bd_express"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="微送" />
                </LinearLayout>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="0.1dp"
                    android:background="#44000000" />
            </LinearLayout>

            <ListView
                android:id="@+id/listview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:divider="#33000000"
                android:dividerHeight="0.1dp" >
            </ListView>
        </LinearLayout>
    </ScrollView>

</LinearLayout>

最後實現的結果:

注意:最後分析一下結果,我們可以看到我們的7個Item全部完全顯示,並且Logcat顯示的ListVIew的高度為2253px,一個Item為321px,有7個Item就是2247px,但是我們還算了分割線一個分割線佔的高度為1px,7個Item佔有6個分割線,所以最後的高度:2247px+6px=2253px.

OK,大功告成,以後大家可以使用我這個工具類可以方便測量ListView的高度,並且下次遇到這種問題就可以很輕鬆的解決了。

但是,有的人就反映說上面的方法效率不高,不過確實效率不高,因為一旦拿到的資料有很多的話,還有手動地去測量每個ListView的Item的高度,所以後來又找到兩個方法,方法一:就是通過自定一個類然後就去繼承ListView然後去重寫OnMeasure方法,然後去呼叫一個makeMeasureSpec方法,實際上和我們上面的方法思想一樣,因為這樣還是修改它的測量方法,還是動態測量高度從而得到真實高度,從而解決衝突。但是這個方法有一個Bug就是不能支援巢狀佈局除了LinearLayout之外的類似RelativeLayout等其他佈局,原始碼是這樣說的:

 /**
         * Creates a measure specification based on the supplied size and mode.
         *
         * The mode must always be one of the following:
         * <ul>
         *  <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>
         *  <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>
         *  <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
         * </ul>
         *
         * <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's
         * implementation was such that the order of arguments did not matter
         * and overflow in either value could impact the resulting MeasureSpec.
         * {@link android.widget.RelativeLayout} was affected by this bug.
         * Apps targeting API levels greater than 17 will get the fixed, more strict
         * behavior.</p>
         *
         * @param size the size of the measure specification
         * @param mode the mode of the measure specification
         * @return the measure specification based on size and mode
         */

它大致的意思:就是這個方法的作用就是通過傳入的指定的測量的尺寸和測量的模式來得到一個明確的測量方式,但是傳入的模式只支援UNSPECIFIED模式和EXACTLY模式

AT_MOST模式,並且需要注意的是在API17以及以下更低的版本中,傳入的引數的順序不重要,但是傳入的值溢位的話則會影響實際的測量,其中的相對佈局就會出現這種Bug

package com.mikyou.myview;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;

public class MyListView extends ListView{

	public MyListView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	public MyListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
	}

	public MyListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}
	/**
	 * 重寫OnMeasure方法
	 * onMeasure方法是測量view和它的內容,決定measured width和measured height的,
	 * 這個方法由 measure(int, int)方法喚起,子類可以覆寫onMeasure來提供更加準確和有效的測量。
          其中兩個輸入引數:
  widthMeasureSpec
  heightMeasureSpec
  分別是parent提出的水平和垂直的空間要求。
  這兩個要求是按照View.MeasureSpec類來進行編碼的。
  參見View.MeasureSpec這個類的說明:這個類包裝了從parent傳遞下來的佈局要求,傳遞給這個child。
  每一個MeasureSpec代表了對寬度或者高度的一個要求。
  每一個MeasureSpec有一個尺寸(size)和一個模式(mode)構成。
  MeasureSpecs這個類提供了把一個的元組包裝進一個int型的方法,從而減少物件分配。當然也提供了逆向的解析方法,從int值中解出size和mode。
	 * 
	 * */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		int expandSpec = MeasureSpec.makeMeasureSpec(  //這個makeMeasureSpec方法表示就是針對明確的測量模式,來得到相應的尺寸
				Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); //第一個引數就是傳入的尺寸size,表示Integer表示最大空間值進行2位移位運算,第二引數指定為AT_MOST表示是"warp_content"的測量模式
		super.onMeasure(widthMeasureSpec, expandSpec);//指定了我們明確的測量尺寸
	}
}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/parent_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <include
        android:id="@+id/include_header_home"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        layout="@layout/header_home" />

    <ScrollView
        android:id="@+id/scrollview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="380dp"
                android:scaleType="fitXY"
                android:src="@drawable/center_image" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" >

                <LinearLayout
                    android:id="@+id/linear"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="#FFF"
                    android:orientation="horizontal" >

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#44000000" />

                    <TextView
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/all_category_arrow_down"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="商家分類" />

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#44000000" />

                    <TextView
                        android:id="@+id/test"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/all_category_arrow_down"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="智慧排序" />

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#77000000" />

                    <TextView
                        android:id="@+id/wei_express"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/slip_off_img_bd_express"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="微送" />
                </LinearLayout>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="0.1dp"
                    android:background="#44000000" />
            </LinearLayout>


            <com.mikyou.myview.MyListView 
                  android:id="@+id/listview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:divider="#33000000"
                android:dividerHeight="0.1dp"
                ></com.mikyou.myview.MyListView>
        </LinearLayout>
    </ScrollView>

</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/parent_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <include
        android:id="@+id/include_header_home"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        layout="@layout/header_home" />

    <ScrollView
        android:id="@+id/scrollview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="380dp"
                android:scaleType="fitXY"
                android:src="@drawable/center_image" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical" >

                <LinearLayout
                    android:id="@+id/linear"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="#FFF"
                    android:orientation="horizontal" >

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#44000000" />

                    <TextView
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/all_category_arrow_down"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="商家分類" />

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#44000000" />

                    <TextView
                        android:id="@+id/test"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/all_category_arrow_down"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="智慧排序" />

                    <View
                        android:layout_width="0.1dp"
                        android:layout_height="match_parent"
                        android:background="#77000000" />

                    <TextView
                        android:id="@+id/wei_express"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:drawableRight="@drawable/slip_off_img_bd_express"
                        android:gravity="center"
                        android:padding="15dp"
                        android:text="微送" />
                </LinearLayout>

                <View
                    android:layout_width="match_parent"
                    android:layout_height="0.1dp"
                    android:background="#44000000" />
            </LinearLayout>


            <com.mikyou.myview.MyListView 
                  android:id="@+id/listview"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:divider="#33000000"
                android:dividerHeight="0.1dp"
                ></com.mikyou.myview.MyListView>
        </LinearLayout>
    </ScrollView>

</LinearLayout>
package com.mikyou.listviewtest;


import com.mikyou.myview.MyListView;
import com.mikyou.utils.MikyouMetricListViewHeightUtils;
import com.mikyou.utils.SystemStatusManager;

import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;

public class MainActivity extends Activity {
	private MyListView  mListView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setTranslucentStatus();
		setContentView(R.layout.activity_main);
		initData();
		initView();
	}
	private void initView() {
		mListView=(MyListView) findViewById(R.id.listview);
		mListView.setAdapter(new MyAdapter());
		/**
		 * @author zhongqihong
		 * 解決ScrollView與ListView衝突的問題
		 * 該方法需要在setAdapter之後呼叫
		 * */
		//MikyouMetricListViewHeightUtils.setListViewHeight(mListView);
		
		
		
		
		mListView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

			@Override
			public void onGlobalLayout() {
				View firstItemView=mListView.getAdapter().getView(0, null, mListView);//得到第一個Item
				firstItemView.measure(0, 0);
				System.out.println("第一個Item的高度:"+firstItemView.getMeasuredHeight());
				System.out.println("mListView的高度:"+mListView.getHeight());				
			}
		});
	}
	class MyAdapter extends BaseAdapter{

		@Override
		public int getCount() {
			return 7;
		}

		@Override
		public Object getItem(int position) {
			return null;
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View view=View.inflate(MainActivity.this, R.layout.home_list_item, null);
			return view;
		}

	}
	private void initData() {

	}
	private void setTranslucentStatus() {//沉浸標題欄效果
		// TODO Auto-generated method stub
		if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
			Window win=getWindow();
			WindowManager.LayoutParams winParams=win.getAttributes();
			final int bits=WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
			winParams.flags |=bits;
			win.setAttributes(winParams);
		}
		SystemStatusManager tintManager = new SystemStatusManager(this);
		tintManager.setStatusBarTintEnabled(true);
		tintManager.setStatusBarTintResource(0);
		tintManager.setNavigationBarTintEnabled(true);
	}
}


執行結果和上面的一樣:

但是,說到這裡,我想說的是我們之前所做的努力實際上都是自己自作自受,沒事閒的蛋疼而已,實際上在真正開發中人家Goole發話了,不會在ScrollView中去放一個ListView的不信來看看Google官方的原話:

Google員工 Roman Guy早已回覆:“There is no need to put a ListView in a ScrollView since ListView already supports scrolling. Do NOT put a ListView inside a ScrollView. ListView already handles scrolling, you’re only going to run into trouble. “ 更詳細的,大家可以看看他在 google i/o 上的ListView視訊講解。大家不要抱著僥倖的心理,要用正確的方法去做正確的事情。大家是不是頓時就懵逼了,是不是心中湧出一萬個草泥馬。但是如果不巢狀的話怎麼去實現這樣的一個效果呢,實際上在設計ListView的時候就可以實現對不同樣式的顯示,也就是整個介面就只用一個ListView來顯示,不搞那麼多巢狀那麼麻煩,意味我們需要用ListView中的第一個Item的位置來顯示我們其他內容,所以整體思路就出來,實際上就是通過對ListView中的position的控制,讓那些其他內容佔據ListView第一個Item的位置即[position為0的位置,然後接下來其他的位置顯示真正每個Item的資訊下面我們還是通過這個Demo來實現下吧:

首先我們把那些頂部其他的內容抽取成一個佈局檔案,然後我的主佈局只有一個ListView:

<?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="wrap_content"
    android:orientation="vertical" >

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="380dp"
        android:scaleType="fitXY"
        android:src="@drawable/center_image" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <LinearLayout
            android:id="@+id/linear"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#FFF"
            android:orientation="horizontal" >

            <View
                android:layout_width="0.1dp"
                android:layout_height="match_parent"
                android:background="#44000000" />

            <TextView
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:drawableRight="@drawable/all_category_arrow_down"
                android:gravity="center"
                android:padding="15dp"
                android:text="商家分類" />

            <View
                android:layout_width="0.1dp"
                android:layout_height="match_parent"
                android:background="#44000000" />

            <TextView
                android:id="@+id/test"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:drawableRight="@drawable/all_category_arrow_down"
                android:gravity="center"
                android:padding="15dp"
                android:text="智慧排序" />

            <View
                android:layout_width="0.1dp"
                android:layout_height="match_parent"
                android:background="#77000000" />

            <TextView
                android:id="@+id/wei_express"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:drawableRight="@drawable/slip_off_img_bd_express"
                android:gravity="center"
                android:padding="15dp"
                android:text="微送" />
        </LinearLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="0.1dp"
            android:background="#44000000" />
    </LinearLayout>
    </LinearLayout>

主佈局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/parent_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <include
        android:id="@+id/include_header_home"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        layout="@layout/header_home" />

    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:divider="#33000000"
        android:dividerHeight="0.1dp" >
    </ListView>

</LinearLayout>


MainActivity

package com.mikyou.listviewtest;


import com.mikyou.myview.MyListView;
import com.mikyou.utils.MikyouMetricListViewHeightUtils;
import com.mikyou.utils.SystemStatusManager;

import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;

public class MainActivity extends Activity {
	private ListView  mListView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setTranslucentStatus();
		setContentView(R.layout.activity_main);
		initData();
		initView();
	}
	private void initView() {
		mListView=(ListView) findViewById(R.id.listview);
		mListView.setAdapter(new MyAdapter());
		/**
		 * @author zhongqihong
		 * 解決ScrollView與ListView衝突的問題
		 * 該方法需要在setAdapter之後呼叫
		 * */
		//MikyouMetricListViewHeightUtils.setListViewHeight(mListView);




		mListView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

			@Override
			public void onGlobalLayout() {
				View firstItemView=mListView.getAdapter().getView(0, null, mListView);//得到第一個Item
				firstItemView.measure(0, 0);
				System.out.println("第一個Item的高度:"+firstItemView.getMeasuredHeight());
				System.out.println("mListView的高度:"+mListView.getHeight());				
			}
		});
	}
	class MyAdapter extends BaseAdapter{

		@Override
		public int getCount() {
			return (1+7);//為什麼是1+7呢??因為我要把ListView的其中一個Item空間作為那些頂部內容佔用,我們這是把第一個Item給它用,所以就相當於在原來基礎上新增一個Item
		}

		@Override
		public Object getItem(int position) {
			return null;
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {

			if (position==0) {//如果position為0即第一項的時候,所以載入顯示頂部內容
				View view2=View.inflate(MainActivity.this, R.layout.top_listview, null);
				/**
				 * @author zhongqihong
				 * //並且要return,為什麼呢??因為我們都知道每個Item的加載出來都要去呼叫一次getView的方法,
				 * 所以當我已經加載出第一個Item即我們頂部內容時候,
				 * 說明getView此次載入的使命已經完成了,所以要return掉,以終止執行防止它執行接下來其他的方法
				 * */
				return view2;
			}else {//表示從第二個開始顯示正常的ListView的Item
				View view=View.inflate(MainActivity.this, R.layout.home_list_item, null);
				return view;
			}

		}

	}
	private void initData() {

	}
	private void setTranslucentStatus() {//沉浸標題欄效果
		// TODO Auto-generated method stub
		if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
			Window win=getWindow();
			WindowManager.LayoutParams winParams=win.getAttributes();
			final int bits=WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
			winParams.flags |=bits;
			win.setAttributes(winParams);
		}
		SystemStatusManager tintManager = new SystemStatusManager(this);
		tintManager.setStatusBarTintEnabled(true);
		tintManager.setStatusBarTintResource(0);
		tintManager.setNavigationBarTintEnabled(true);
	}
}


最後實現效果:

注意:其實最後一種方法才是最正確的最高效率的方法,根本就不需要考慮所謂巢狀衝突的問題。建議以後大家還是採用這種方法來實現。

相關推薦

androidListView解決ScrollViewListView衝突(實際上一切浮雲)()

     相信大家都已經可以熟練使用ListView和GridView,大神們估計都在使用RecyclerView了。如果還在使用ListView,你肯定有這樣的一個深刻的感受,那就是在做一個APP的時候使用ListView和GridView很頻繁,並且經常會遇到一個頁面中

Android的非同步載入ListView圖片的快取及優化三

     隔了很久沒寫部落格,現在必須快速脈動回來。今天我還是接著上一個多執行緒中的非同步載入系列中的最後一個使用非同步載入實現ListView中的圖片快取及其優化。具體來說這次是一個綜合Demo.但是個人覺得裡面還算有點價值的就是裡面的圖片的快取的實現。因為老實說它確實能

AndroidAndroid的MVP

個人開發的微信小程式,目前功能是書籍推薦,後續會完善一些新功能,希望大家多多支援! 前言 為什麼使用MVP,網上有很多說法,最主要就是減輕了Activity的責任,相比於MVC中的Activity承擔的責任太多,因此有必要講講MVP。 MVP入門 在MVC框

android圖片處理圖形變換特效Matrix(四)

今天,我們就來談下android中圖片的變形的特效,在上講部落格中我們談到android中圖片中的色彩特效來實現的。改變它的顏色主要通過ColorMatrix類來實現。 現在今天所講的圖片變形的特效主要就是通過Matrix類來實現,我們通過上篇部落格知道,改變色彩特效,主要

android圖片處理色彩特效處理ColorMatrix(三)

在android開發中對圖片處理很是頻繁,其中對圖片的顏色處理就是很常見的一種。我們經常看到一些類似美圖秀秀,美顏相機的app,為什麼那麼黑的人拍出來是確實那麼地白呢?長的那麼那個(醜)的人,用美顏相機拍出來的看起來也有那麼回事(拍出來就感覺挺漂亮)。就像網上有個段子,有錢

Android的組播(多播)

-1 ip協議 strong 多個 接受 端口 ui線程 nbsp 數據 組播使用UDP對一定範圍內的地址發送相同的一組Packet,即一次可以向多個接受者發出信息,其與單播的主要區別是地址的形式。IP協議分配了一定範圍的地址空間給多播(多播只能使用這個範圍內

Android幸運快三平臺出租的meta-data及其應用

key 引用 平臺 name 如何 Coding pri sch xxxxx 在日常幸運快三平臺出租 haozbbs.com Q1446595067 的Android開發中,AndroidManifest中總會出現一些標簽,或是第三方SDK配置信息,或是系統配置,不禁讓人

Android的 Fragment、生命週期回撥方法 以及使用

        4onActivityCreated()              當Activity中的onCreate方法執行完後呼叫。 注意了:從這句官方的話可以看出:當執行onActivityCreated()的時候 activity的onCreate才剛完成。所以在onActivityCrea

Android的Handler機制

Handler是Android中提供的一種非同步回撥機制,也可以理解為執行緒間的訊息機制。為了避免ANR,我們通常會把一些耗時操作(比如:網路請求、I/O操作、複雜計算等)放到子執行緒中去執行,而當子執行緒需要修改UI時則子執行緒需要通知主執行緒去完成修改UI的

AndroidSerializableParcelable使用區別

Android中序列化有兩種方式:Serializable以及Parcelable。其中Serializable是Java自帶的,而Parcelable是安卓專有的。 一、Serializable序列化 serializable使用比較簡單,只需要對某個類實現Serializable 介面即可。 Ser

android手機聯絡人字母索引表的實現

實際上字母索引表的效果,可以說在現在的眾多APP中使用的非常流行,比如支付寶,微信中的聯絡人,還有購物,買票的APP中選擇全國城市,切換城市的時候,這時候的城市也就是按照一個字母索引的順序來顯示,看起來是很方便的.其實這種字母索引表的效果最開始是出現在微信的聯絡人中.因為覺

AndroidCallback(回撥)的使用

今天專案的Bug基本修改完成了,於是就對自己還未了解的回撥函式進行了學習。回撥其實就是在一定的時間裡做“一件事”,至於“這件事”具體做的是什麼不會管,只管做“這件事“,比如Boss叫員工去吃飯,但每個員工可能吃不同的食物。只不過,回撥是對介面而言。簡單來說就是,A物件呼叫

android載入高清大圖及圖片壓縮方式(二)

  這一講就是本系列的第二篇,一起來聊下關於android中載入高清大圖的問題,我們都知道如果我們直接載入原圖的話,一個是非常慢,需要等待一定時間,如果沒有在一定的時間內給使用者響應的話,將會極大影響使用者的體驗。另一個是如果你的手機記憶體小的話,可能會直接崩潰。這也就是直

Android的LOG檢視ANR(

轉自:http://yinger-fei.iteye.com/blog/1533788 手機中pull處理trace.txt 進去 data/anr 再pull 一:什麼是ANR                   ANR:Application Not Resp

Android的MVVM模式

大家好啊,我是kele。眾所周知,Android的設計模式主要有三個:MVC,MVP,MVVM。今天主要來談一下MVVM模式,簡單說明它的好處以及它和MVP在實現方面的區別。 DataBinding android { ....

android僅僅使用一個TextView實現高仿京東,淘寶各種倒計時

  今天給大家帶來的是僅僅使用一個TextView實現一個高仿京東、淘寶、唯品會等各種電商APP的活動倒計時。最近公司一直加班也沒來得及時間去整理,今天難得休息想把這個分享給大家,只求共同學習,以及自己後續的複習。為什麼會想到使用一個TextView來實現呢?因為最近公司在

AndroidMVP模式與MVC模式的區別

一、概述 對於MVP(Model View Presenter),大多數人都能說出一二:“MVC的演化版本”,“讓Model和View完全解耦”等等。本篇博文僅是為了做下記錄,提出一些自己的看法,和幫助大家如何針對一個Activity頁面去編寫針對MVP風

Android進階

熱文導讀 | 點選標題閱讀作者:斜槓Allen地址:http://www.apkbus.com/

C#的值類型引用類型

title log 創建 編譯 設計 編寫 通過 發布 構造 在C#中,值類型和引用類型是相當重要的兩個概念,必須在設計類型的時候就決定類型實例的行為。如果在編寫代碼時不能理解引用類型和值類型的區別,那麽將會給代碼帶來不必要的異常。很多人就是因為沒有弄清楚這兩個概念從而在編