Android:ScrollView中巢狀ViewPager和ListView示例
阿新 • • 發佈:2019-02-11
引言:
我們在實際開發一個款Android App時,經常會遇到Scrollview和ViewPager和ListView同時使用的場景,如下圖所示的需求:
下面我們通過程式碼來模擬一下這種場景:
佈局檔案:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width ="match_parent"
android:layout_height="match_parent"
tools:context="com.myapplication.viewpagerdemo.MainActivity"
>
<ScrollView
android:id="@+id/scrollView_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:id="@+id/content_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width ="match_parent"
android:layout_height="match_parent"
/>
<LinearLayout
android:id="@+id/linearLayout_points"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#0000ff"
android:gravity="center"
android:orientation="horizontal"
/>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00fff0"
/>
</LinearLayout>
</ScrollView>
</FrameLayout>
MainActivity中的程式碼:
package com.myapplication.viewpagerdemo;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ScrollView;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
/**
* viewPager動態新增圓點,選中該pager時候對應圓點變色
*/
public class MainActivity extends AppCompatActivity {
private ScrollView scrollView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化ViewPager所需要pager的圖片和指示圓點
initImageList();
//初始化ViewPager,及其介面卡
initViewPager();
//設定輪播監聽,指示圓點在選中和不選中之間切換
setViewPagerListener();
//判斷是否有使用者手勢
isLoop();
//自動輪播
autoBanner();
//初始化ListView,並填充資料;初始化ScrollView
initListView();
}
//判斷是否自動輪播
boolean isLoopFlag = true;
//手觸控Viewpager區域按下移動不能自動切換,擡起之後在自動切換(防止自動切換和手移動出現衝突)
public void isLoop() {
viewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isLoopFlag = false;
break;
case MotionEvent.ACTION_MOVE:
isLoopFlag = false;
break;
case MotionEvent.ACTION_UP:
isLoopFlag = true;
break;
default:
break;
}
return false;
}
});
}
//自動播放
private void autoBanner() {
if (isLoopFlag) {
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
if (imageList.size() > 1) {//多於1個,才迴圈
//子執行緒不能更新Ui,把主執行緒請到子執行緒家裡來
runOnUiThread(new Runnable() {
@Override
public void run() {
int index = viewPager.getCurrentItem();
index = (index + 1) % imageList.size();
viewPager.setCurrentItem(index, true);
//第二個引數布林值:True to smoothly scroll to the new item, false to transition immediately
}
});
}
}
};
//開始一個定時任務,首次執行延遲3000毫秒呼叫run方法,執行後每2500毫秒執行一次
timer.schedule(timerTask, 3000, 2500);
}
}
//pager上面的資料來源
private List<ImageView> imageList = new ArrayList<>();
//指示原點的父佈局,可以動態的新增圓點ImageView物件到檢視當中
private LinearLayout linearLayout;
//用來存放指示圓點的物件
private List<ImageView> pointList = new ArrayList<>();
private void initImageList() {
TypedArray array = getResources().obtainTypedArray(R.array.pagerImageList);
for (int i = 0; i < array.length(); i++) {
ImageView imageView = new ImageView(this);
imageView.setImageDrawable(array.getDrawable(i));
ViewGroup.LayoutParams lp
= new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
, ViewGroup.LayoutParams.MATCH_PARENT);
imageView.setLayoutParams(lp);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageList.add(imageView);
}
linearLayout = (LinearLayout) findViewById(R.id.linearLayout_points);
for (int i = 0; i < array.length(); i++) {
ImageView imageView = new ImageView(this);
imageView.setLayoutParams(new ViewGroup.LayoutParams(25, 25));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
// imageView.setImageResource(R.drawable.dot_1);
imageView.setImageResource(R.drawable.switch_point_selector);
pointList.add(imageView);
linearLayout.addView(imageView);
}
}
private void setViewPagerListener() {
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// switchPoint(position);
for (int i = 0; i < imageList.size(); i++) {
pointList.get(i % imageList.size()).setEnabled(false);
}
pointList.get(position % imageList.size()).setEnabled(true);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private ViewPager viewPager;
private final int VIEWPAGER_DEFAULT_HEIGHT = 200;
private void initViewPager() {
viewPager = (ViewPager) findViewById(R.id.viewPager);
viewPager.setAdapter(adapter);
//介面卡,重寫四方法,也可以單獨寫一個類,
private PagerAdapter adapter = new PagerAdapter() {
@Override
public int getCount() {
return imageList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
//做了兩件事,第一:將當前檢視新增到container中,第二:返回當前View
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(imageList.get(position));
return imageList.get(position);
}
//從當前container中刪除指定位置(position)的View;
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
//super.destroyItem(container, position, object);
container.removeView(imageList.get(position));
}
};
//展示listView的三要素
private ListView listView;
private List<String> list = new ArrayList<>();
private ArrayAdapter<String> arrayAdapter;
private void initListView() {
listView = (ListView) findViewById(R.id.listView);
//初始化資料來源
for (int i = 'A'; i < 'z'; i++) {
list.add("春風十里不如你 >" + (char) i);
}
//初始化介面卡
arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
listView.setAdapter(arrayAdapter);
//動態算出ListView的LayoutParams,並設定到ListView中
ViewGroup.LayoutParams lp = getListViewParams();
listView.setLayoutParams(lp);
scrollView = (ScrollView) findViewById(R.id.scrollView_main);
scrollView.smoothScrollTo(0, 0);
}
}
一、常遇到問題:
Scrollview中巢狀ViewPager和ListView常出現的問題:
- ViewPager如果在Scrollview中使用,並且寬高指定為match_parent或者wrap_content則不會顯示內容,但是如果將高度指定為200dp,則ViewPager則會將內容顯示到螢幕上
- ListView同ViewPager類似,必須指定固定高度之後,才可以顯示相應的高度,否則只顯示一個Item的內容高度,然後在ListView區域上下滑動時,Scrollview並沒有效果,而是實現的是ListView的上下滾動事件。
解決思路:想辦法在java程式碼中算出ListView和ViewPager的實際所佔用的高度,然後通過setLayoutParams方法動態設定控制元件的高度。
二、確定ViewPager的高度:
ViewPager不能單純的獲取子View的高度來計算(因為如果使用ViewPager的話,介面卡有時候用到的是FragmentPagerAdapter,不太方便計算其中的高度), 因為我們根據當前手機的寬高以及螢幕密度指定一個ViewPager高度的預設值,如下程式碼所示:
/**
* ViewPager如果放到ScrollView當中,需要在Java程式碼中通過LayoutParams動態的
* 設定一個固定值的高,否則ViewPager中的內容無法顯示
*/
//1,獲取螢幕密度
float density = getResources().getDisplayMetrics().density;
// 2、獲取ViewPager在不同螢幕密度上的手機的高度
int viewPagerHeight = (int) (VIEWPAGER_DEFAULT_HEIGHT * density);
// 3、通過setLayoutParams方式,給ViewPager動態設定高度
LinearLayout.LayoutParams lp
= new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
, viewPagerHeight);
viewPager.setLayoutParams(lp);
三、確定ListView的高度:
解決思路是給定高度,但是我們直接在xml中寫個固定高度顯然是不合適的,我們要依據ListView中資料的多少,計算出高度。
/**
* 動態的算出ListView實際的LayoutParams
* 最關鍵的是算出LayoutParams.height
*/
private ViewGroup.LayoutParams getListViewParams() {
//通過ListView獲取其中的介面卡adapter
ListAdapter listAdapter = listView.getAdapter();
//宣告預設高度為0
int totalHeight = 0;
//遍歷listView中所有Item,累加所有item的高度就是ListView的實際高度(後面會考慮分割線的高度)
for (int i = 0; i < listAdapter.getCount(); i++) {
View item = listAdapter.getView(i, null, listView);
item.measure(0, 0);
totalHeight += item.getMeasuredHeight();
}
ViewGroup.LayoutParams lp = listView.getLayoutParams();
lp.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
return lp;
}
注意:
1. 第一次啟動此App時,頁面預設顯示scrollView的頂端view,即ViewPager;因此我們需要在程式碼中呼叫一下Scrollview的smoothScrollTo(0,
0)方法,將Scrollview滑動到最頂端
2. ListView中在返回item的檢視時,item的佈局根元素必須是LinearLayout,因為只有LinearLayout才有measure方法