ScrollView ViewPager ListView三者共存的问题

当ScrollView作为外层容器,嵌套ViewPager并包含一个ListView时,ListView无法展开且ScrollView无法滑动。解决方法包括:通过设置Adapter计算并设置ListView高度以使其展开,同时动态调整ViewPager高度;自定义ScrollView,重写onInterceptTouchEvent方法避免滑动冲突。
摘要由CSDN通过智能技术生成

场景描述:

ScrollView是整个界面的外层滑动控件,嵌套在里面的布局是ViewPager,ViewPager里面其中一个Page是ListView控件。

问题描述:

ViewPager中的ListView获取到数据后不能显示,也就是不能展开,外层ScrollView不能滑动.

解决思路:

1、让ListView展开。(网上的说法是ScrollView与ListView嵌套不可取,但开发中设计界面如此,不用也不成,闲话少说),想让ListView撑开,只能依靠设置Adapter每个Item高度,然后计算所有Item占用的总高度,从而得到ListView的高度,重新设置ListView的高度(工具类都是网上的,我又多说话了还是粘在下面)。

package com.goodwin.finance.util;

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

/**
 * 文件名: com.goodwin.finance.util.ViewUtil
 * 作者:  熊杰 Wilson
 * 日期: 14-9-30
 * 时间: 14:28
 * 开发工具: IntelliJ IDEA 12.1.1
 * 开发语言: Java,Android
 * 开发框架:
 * 版本: v0.1
 * <strong>软件中所有与布局相关的工具类</strong>
 * <p></p>
 */
public class ViewUtil {
  public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null) {
      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);
  }

  /**
   * 获取Listview的高度,然后设置ViewPager的高度
   * @param listView
   * @return
   */
  public static int setListViewHeightBasedOnChildren1(ListView listView) {
    //获取ListView对应的Adapter
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null) {
      // pre-condition
      return 0;
    }

    int totalHeight = 0;
    for (int i = 0, len = listAdapter.getCount(); i < len; i++) { //listAdapter.getCount()返回数据项的数目
      View listItem = listAdapter.getView(i, null, listView);
      listItem.measure(0, 0); //计算子项View 的宽高
      totalHeight += listItem.getMeasuredHeight(); //统计所有子项的总高度
    }

    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    //listView.getDividerHeight()获取子项间分隔符占用的高度
    //params.height最后得到整个ListView完整显示需要的高度
    listView.setLayoutParams(params);
    return params.height;
  }
}

用法是在获取到数据填充ListView适配器后,调用工具类的方法。

infoListAdapter = new TheInfoListAdapter(this);
  infoListAdapter.setInfoList(infoList);
  lvInfoList.setAdapter(infoListAdapter);
  infoListAdapter.notifyDataSetChanged();
  int listViewHeight = ViewUtil.setListViewHeightBasedOnChildren1(lvInfoList);
  ViewGroup.LayoutParams params = vpImgpager.getLayoutParams();
  params.height = listViewHeight;
  vpZifuduo.setLayoutParams(params);

2、为什么调用的是工具类的第2个方法,是因为第一个方法调用之后,它只影响到ListView的高度,并不影响它外层ViewPager的高度,因此我重载了这个方法,将这个高度给返回,在改变ListView高度的同时,也将ViewPager的高度重新动态设置。这样也就解决了外层ScrollView不能滑动的问题。

注意下细节:

ListView的适配器Item用LinearLayout进行布局,给它指定一个固定高度(这个高度值固定对兼容来说无伤大雅),这样便于计算ListView的高度。

3、附带的另外一个问题,ViewPager的左右滑动与ScrollView的上下滑动问冲突

解决方法是自定义一个ScrollView,然后重写一个onInterceptTouchEvent()方法

以下是我本例中重写的ScrollView:

package com.goodwin.finance.common.view;

import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ScrollView;

public class MyScrollView extends ScrollView {
  private float xDistance, yDistance, xLast, yLast;

  private OnScrollListener onScrollListener;
  /**
   * 主要是用在用户手指离开MyScrollView,MyScrollView还在继续滑动,我们用来保存Y的距离,然后做比较
   */
  private int lastScrollY;

  public MyScrollView(Context context) {
    this(context, null);
  }

  public MyScrollView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public MyScrollView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  }

  /**
   * 设置滚动接口
   * @param onScrollListener
   */
  public void setOnScrollListener(OnScrollListener onScrollListener) {
    this.onScrollListener = onScrollListener;
  }

  /**
   * 用于用户手指离开MyScrollView的时候获取MyScrollView滚动的Y距离,然后回调给onScroll方法中
   */
  private Handler handler = new Handler() {

    public void handleMessage(android.os.Message msg) {
      int scrollY = MyScrollView.this.getScrollY();

      //此时的距离和记录下的距离不相等,在隔5毫秒给handler发送消息
      if(lastScrollY != scrollY){
        lastScrollY = scrollY;
        handler.sendMessageDelayed(handler.obtainMessage(), 5);
      }
      if(onScrollListener != null){
        onScrollListener.onScroll(scrollY);
      }

    };

  };

  /**
   * 重写onTouchEvent, 当用户的手在MyScrollView上面的时候,
   * 直接将MyScrollView滑动的Y方向距离回调给onScroll方法中,当用户抬起手的时候,
   * MyScrollView可能还在滑动,所以当用户抬起手我们隔5毫秒给handler发送消息,在handler处理
   * MyScrollView滑动的距离
   */
  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    if(onScrollListener != null){
      onScrollListener.onScroll(lastScrollY = this.getScrollY());
    }
    switch(ev.getAction()){
      case MotionEvent.ACTION_UP:
        handler.sendMessageDelayed(handler.obtainMessage(), 5);
        break;
    }
    return super.onTouchEvent(ev);
  }


  /**
   *
   * 滚动的回调接口
   *
   */
  public interface OnScrollListener{
    /**
     * 回调方法, 返回MyScrollView滑动的Y方向距离
     */
    public void onScroll(int scrollY);
  }

  /**
   *解决ViewPager与ScrollView手势冲突的问题
   */
  @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);
  }

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值