Android:ScrollView中嵌套ViewPager和ListView示例

引言:

我们在实际开发一个款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常出现的问题:

  1. ViewPager如果在Scrollview中使用,并且宽高指定为match_parent或者wrap_content则不会显示内容,但是如果将高度指定为200dp,则ViewPager则会将内容显示到屏幕上
  2. 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方法

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值