ScrollView嵌套ListView,GridView,ViewPager,以及這些控件自動滾動到底部問題的解決

Google是不推薦在ScrollView 中放入一個可滾動的菜單的,比如放置一個ListView、GridView、ViewPager這些控件的,儘量不要讓兩者嵌套,但有時候還是有這個需求,先不管它合不合理。如果直接在ScrollView中嵌套只會出現一行,然後在其中滾動,這樣不是很好,下面是我的整理,希望對大家有幫助,我也是從網上摘抄的,當然加入了一些我自己的東西。

一、在ScrollView中嵌套ListView,有兩張方法

第一種是自定義View,繼承ListView代碼如下:

  1. import android.content.Context;  
  2. import android.util.AttributeSet;  
  3. import android.widget.ListView;  
  4.   
  5. public class MyListView extends ListView {  
  6.   
  7.     public MyListView(Context context) {  
  8.         super(context);  
  9.     }  
  10.     public MyListView(Context context, AttributeSet attrs, int defStyle) {  
  11.         super(context, attrs, defStyle);  
  12.     }  
  13.     public MyListView(Context context, AttributeSet attrs) {  
  14.         super(context, attrs);  
  15.     }  
  16.     @Override  
  17.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  18.          int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,    
  19.                  MeasureSpec.AT_MOST);      
  20.         super.onMeasure(widthMeasureSpec, expandSpec);  
  21.     }  
  22.   
  23. }  
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;

public class MyListView extends ListView {

	public MyListView(Context context) {
		super(context);
	}
	public MyListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}
	public MyListView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		 int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,  
			     MeasureSpec.AT_MOST);    
		super.onMeasure(widthMeasureSpec, expandSpec);
	}

}
第二種

  1. import android.view.View;  
  2. import android.view.ViewGroup;  
  3. import android.widget.ListAdapter;  
  4. import android.widget.ListView;  
  5.   
  6. public class Utility {  
  7.     public static void setListViewHeightBasedOnChildren(ListView listView) {  
  8.         ListAdapter listAdapter = listView.getAdapter();  
  9.         if (listAdapter == null) {  
  10.             // pre-condition  
  11.             return;  
  12.         }  
  13.   
  14.         int totalHeight = 0;  
  15.         for (int i = 0; i < listAdapter.getCount(); i++) {  
  16.             View listItem = listAdapter.getView(i, null, listView);  
  17.             listItem.measure(00);  
  18.             totalHeight += listItem.getMeasuredHeight();  
  19.         }  
  20.   
  21.         ViewGroup.LayoutParams params = listView.getLayoutParams();  
  22.         params.height = totalHeight  
  23.                 + (listView.getDividerHeight() * (listAdapter.getCount() - 1));  
  24.         listView.setLayoutParams(params);  
  25.     }  
  26. }  
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.ListView;

public class Utility {
	public static void setListViewHeightBasedOnChildren(ListView listView) {
		ListAdapter listAdapter = listView.getAdapter();
		if (listAdapter == null) {
			// pre-condition
			return;
		}

		int totalHeight = 0;
		for (int i = 0; i < listAdapter.getCount(); i++) {
			View listItem = listAdapter.getView(i, null, listView);
			listItem.measure(0, 0);
			totalHeight += listItem.getMeasuredHeight();
		}

		ViewGroup.LayoutParams params = listView.getLayoutParams();
		params.height = totalHeight
				+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));
		listView.setLayoutParams(params);
	}
}

第二種在setAdapter之後調用Utility.setListViewHeightBasedOnChildren(listview)這個方法,有些資料說只能item的根佈局要LinearLayout,但是我的跟佈局爲RelativeLayout也可以的。

這兩種都可以,但是在有些情況下中能用第一種,比如你顯示ListView是一個ListFragment就只能用第二種方法了。

二、在ScrollView中嵌套GridView

  1. import android.content.Context;  
  2. import android.util.AttributeSet;  
  3. import android.widget.GridView;  
  4.   
  5. public class NoScrollGridView extends GridView {    
  6.         
  7.     public NoScrollGridView(Context context) {    
  8.         super(context);    
  9.             
  10.     }    
  11.     
  12.     public NoScrollGridView(Context context, AttributeSet attrs) {    
  13.         super(context, attrs);    
  14.     }    
  15.         
  16.     @Override    
  17.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    
  18.         int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);    
  19.         super.onMeasure(widthMeasureSpec, expandSpec);    
  20.     }    
  21.     
  22. }    
import android.content.Context;
import android.util.AttributeSet;
import android.widget.GridView;

public class NoScrollGridView extends GridView {  
	  
    public NoScrollGridView(Context context) {  
        super(context);  
          
    }  
  
	public NoScrollGridView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
      
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);  
        super.onMeasure(widthMeasureSpec, expandSpec);  
    }  
  
}  
二、在ScrollView中嵌套ViewPager,也有兩張方法
第一種自定義ScrollView

  1. import android.content.Context;  
  2. import android.util.AttributeSet;  
  3. import android.view.MotionEvent;  
  4. import android.widget.ScrollView;  
  5.   
  6. /** 
  7.  * 能夠兼容ViewPager的ScrollView 
  8.  *  
  9.  * @Description: 解決了ViewPager在ScrollView中的滑動反彈問題 
  10.  */  
  11. public class ScrollViewExtend extends ScrollView {  
  12.     // 滑動距離及座標  
  13.     private float xDistance, yDistance, xLast, yLast;  
  14.   
  15.     public ScrollViewExtend(Context context, AttributeSet attrs) {  
  16.         super(context, attrs);  
  17.     }  
  18.   
  19.     @Override  
  20.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  21.         switch (ev.getAction()) {  
  22.         case MotionEvent.ACTION_DOWN:  
  23.             xDistance = yDistance = 0f;  
  24.             xLast = ev.getX();  
  25.             yLast = ev.getY();  
  26.             break;  
  27.         case MotionEvent.ACTION_MOVE:  
  28.             final float curX = ev.getX();  
  29.             final float curY = ev.getY();  
  30.   
  31.             xDistance += Math.abs(curX - xLast);  
  32.             yDistance += Math.abs(curY - yLast);  
  33.             xLast = curX;  
  34.             yLast = curY;  
  35.   
  36.             if (xDistance > yDistance) {  
  37.                 return false;  
  38.             }  
  39.         }  
  40.         return super.onInterceptTouchEvent(ev);  
  41.     }  
  42. }  
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ScrollView;

/**
 * 能夠兼容ViewPager的ScrollView
 * 
 * @Description: 解決了ViewPager在ScrollView中的滑動反彈問題
 */
public class ScrollViewExtend extends ScrollView {
	// 滑動距離及座標
	private float xDistance, yDistance, xLast, yLast;

	public ScrollViewExtend(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			xDistance = yDistance = 0f;
			xLast = ev.getX();
			yLast = ev.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			final float curX = ev.getX();
			final float curY = ev.getY();

			xDistance += Math.abs(curX - xLast);
			yDistance += Math.abs(curY - yLast);
			xLast = curX;
			yLast = curY;

			if (xDistance > yDistance) {
				return false;
			}
		}
		return super.onInterceptTouchEvent(ev);
	}
}
第二種是自定義ViewPager

  1. import android.content.Context;  
  2. import android.graphics.PointF;  
  3. import android.support.v4.view.ViewPager;  
  4. import android.util.AttributeSet;  
  5. import android.view.MotionEvent;  
  6.   
  7. /** 
  8.  * 嵌套在ScrollView中的ViewPager,解決衝突 
  9.  * @author m3 
  10.  * 
  11.  */  
  12. public class ChildViewPager extends ViewPager {  
  13.     /** 觸摸時按下的點 **/  
  14.     PointF downP = new PointF();  
  15.     /** 觸摸時當前的點 **/  
  16.     PointF curP = new PointF();  
  17.     OnSingleTouchListener onSingleTouchListener;  
  18.   
  19.     public ChildViewPager(Context context, AttributeSet attrs) {  
  20.         super(context, attrs);  
  21.         // TODO Auto-generated constructor stub  
  22.     }  
  23.   
  24.     public ChildViewPager(Context context) {  
  25.         super(context);  
  26.         // TODO Auto-generated constructor stub  
  27.     }  
  28.   
  29.     @Override  
  30.     public boolean onInterceptTouchEvent(MotionEvent arg0) {  
  31.         // TODO Auto-generated method stub  
  32.         // 當攔截觸摸事件到達此位置的時候,返回true,  
  33.         // 說明將onTouch攔截在此控件,進而執行此控件的onTouchEvent  
  34.         return true;  
  35.     }  
  36.   
  37.     @Override  
  38.     public boolean onTouchEvent(MotionEvent arg0) {  
  39.         // TODO Auto-generated method stub  
  40.         // 每次進行onTouch事件都記錄當前的按下的座標  
  41.         curP.x = arg0.getX();  
  42.         curP.y = arg0.getY();  
  43.   
  44.         if (arg0.getAction() == MotionEvent.ACTION_DOWN) {  
  45.             // 記錄按下時候的座標  
  46.             // 切記不可用 downP = curP ,這樣在改變curP的時候,downP也會改變  
  47.             downP.x = arg0.getX();  
  48.             downP.y = arg0.getY();  
  49.             // 此句代碼是爲了通知他的父ViewPager現在進行的是本控件的操作,不要對我的操作進行干擾  
  50.             getParent().requestDisallowInterceptTouchEvent(true);  
  51.         }  
  52.   
  53.         if (arg0.getAction() == MotionEvent.ACTION_MOVE) {  
  54.             // 此句代碼是爲了通知他的父ViewPager現在進行的是本控件的操作,不要對我的操作進行干擾  
  55.             getParent().requestDisallowInterceptTouchEvent(true);  
  56.         }  
  57.   
  58.         if (arg0.getAction() == MotionEvent.ACTION_UP) {  
  59.             // 在up時判斷是否按下和鬆手的座標爲一個點  
  60.             // 如果是一個點,將執行點擊事件,這是我自己寫的點擊事件,而不是onclick  
  61.             if (downP.x == curP.x && downP.y == curP.y) {  
  62.                 onSingleTouch();  
  63.                 return true;  
  64.             }  
  65.         }  
  66.   
  67.         return super.onTouchEvent(arg0);  
  68.     }  
  69.   
  70.     /** 
  71.      * 單擊 
  72.      */  
  73.     public void onSingleTouch() {  
  74.         if (onSingleTouchListener != null) {  
  75.   
  76.             onSingleTouchListener.onSingleTouch();  
  77.         }  
  78.     }  
  79.   
  80.     /** 
  81.      * 創建點擊事件接口 
  82.      *  
  83.      * @author wanpg 
  84.      *  
  85.      */  
  86.     public interface OnSingleTouchListener {  
  87.         public void onSingleTouch();  
  88.     }  
  89.   
  90.     public void setOnSingleTouchListener(  
  91.             OnSingleTouchListener onSingleTouchListener) {  
  92.         this.onSingleTouchListener = onSingleTouchListener;  
  93.     }  
  94.   
  95. }  
import android.content.Context;
import android.graphics.PointF;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;

/**
 * 嵌套在ScrollView中的ViewPager,解決衝突
 * @author m3
 *
 */
public class ChildViewPager extends ViewPager {
	/** 觸摸時按下的點 **/
	PointF downP = new PointF();
	/** 觸摸時當前的點 **/
	PointF curP = new PointF();
	OnSingleTouchListener onSingleTouchListener;

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

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

	@Override
	public boolean onInterceptTouchEvent(MotionEvent arg0) {
		// TODO Auto-generated method stub
		// 當攔截觸摸事件到達此位置的時候,返回true,
		// 說明將onTouch攔截在此控件,進而執行此控件的onTouchEvent
		return true;
	}

	@Override
	public boolean onTouchEvent(MotionEvent arg0) {
		// TODO Auto-generated method stub
		// 每次進行onTouch事件都記錄當前的按下的座標
		curP.x = arg0.getX();
		curP.y = arg0.getY();

		if (arg0.getAction() == MotionEvent.ACTION_DOWN) {
			// 記錄按下時候的座標
			// 切記不可用 downP = curP ,這樣在改變curP的時候,downP也會改變
			downP.x = arg0.getX();
			downP.y = arg0.getY();
			// 此句代碼是爲了通知他的父ViewPager現在進行的是本控件的操作,不要對我的操作進行干擾
			getParent().requestDisallowInterceptTouchEvent(true);
		}

		if (arg0.getAction() == MotionEvent.ACTION_MOVE) {
			// 此句代碼是爲了通知他的父ViewPager現在進行的是本控件的操作,不要對我的操作進行干擾
			getParent().requestDisallowInterceptTouchEvent(true);
		}

		if (arg0.getAction() == MotionEvent.ACTION_UP) {
			// 在up時判斷是否按下和鬆手的座標爲一個點
			// 如果是一個點,將執行點擊事件,這是我自己寫的點擊事件,而不是onclick
			if (downP.x == curP.x && downP.y == curP.y) {
				onSingleTouch();
				return true;
			}
		}

		return super.onTouchEvent(arg0);
	}

	/**
	 * 單擊
	 */
	public void onSingleTouch() {
		if (onSingleTouchListener != null) {

			onSingleTouchListener.onSingleTouch();
		}
	}

	/**
	 * 創建點擊事件接口
	 * 
	 * @author wanpg
	 * 
	 */
	public interface OnSingleTouchListener {
		public void onSingleTouch();
	}

	public void setOnSingleTouchListener(
			OnSingleTouchListener onSingleTouchListener) {
		this.onSingleTouchListener = onSingleTouchListener;
	}

}
第二個問題在ScrollView嵌套ListView,GridView,如果這些子控件很長超出了屏幕的高度,那麼ScrollView會自動滾到底部,但是我們需要默認在頂部,我們要在ListView和GridView上面的view中加入,一下代碼即可解決:

  1. view.setFocusable(true);  
  2. view.setFocusableInTouchMode(true);  
  3. view.requestFocus();  
view.setFocusable(true);
view.setFocusableInTouchMode(true);
view.requestFocus();
這段代碼在初始化的時候就讓該界面的頂部的某一個控件獲得焦點,滾動條自然就顯示到頂部了,順便附一下我找到答案的地方http://blog.csdn.net/studyalllife/article/details/42970975這上面寫的很混亂,大家可以參考一下,大功告成。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章