安卓RecylerView嵌套和事件处理

最近遇到了一个需求:RecylerView的某一项为listView,即listView嵌套,且要求内部ListView可以滑动,高度固定。

如果直接简单的写完,会发现有两个问题:

1.内部listView高度显示一行

2.内部listview无法进行滑动

以上两个问题可以用以下方法加以解决:

针对问题1:

解决方法1:在Adapter里面的onCreateViewHolder()方法里面加入以下方法,动态的设置该Item的高度。

    private void setListViewHeightBasedOnChildren(ListView listview){
        int totalHeight=0;
        for(int i=0,len=myListAdapter.getCount();i<len;i++){
                View listItem=myListAdapter.getView(i,null,listview);
                listItem.measure(0,0);
                totalHeight += listItem.getMeasuredHeight();
        }
        ViewGroup.LayoutParams params=listview.getLayoutParams();
        params.height=totalHeight + listview.getDividerHeight() * myListAdapter.getCount();
        listview.setLayoutParams(params);
    }

上面代码设置内部ListView的高度为其实际高度,如果有需求高度固定,则可以修改代码,将totalHeight改为固定值即可,此处可以很灵活的设置。

解决方法2:方法1可以解决大部分问题,但是在我的项目中有个特殊的问题,即内部listview的item为textView,其中的文字个数是变化的,行数不定,在这种情况下,用getMeasuredHeight是无法获得多行情况下的textview实际高度的。这种情况下可以采用方法2,即:让内部listView继承自自定义ViewAutoHeightListView,具体代码如下:

public class AutoHeightListView extends ListView {

    public AutoHeightListView(Context context) {
        super(context);
    }

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }
}

关键代码为复写onMeasure方法,将HeightMeasureSpec修改一下。原理墙裂推荐 http://www.cnblogs.com/xyhuangjinfu/p/5435201.html    原理简单概括以下就是,父类传递给Listview的高度测量模式默认为unSpecified,且在该模式下其计算自身高度为第一个子view的高度。上面代码将其修改为at_most模式,在该模式下其自身高度的计算为计算所有子View高度和。 

针对问题2:

原因在于ListView类似于ScrollView,两个嵌套的情况下,外部ListView会把用户的操作消费掉,无法传递分发给内部ListView,解决方法就从此处入手:将外部ListView改成自定义View,继承ListView并重写onIntercept()方法,将其返回值改为false,即外部listview不拦截触摸事件。

public class ParentRecylerView extends RecyclerView {
    public ParentRecylerView(Context context) {
        super(context);
    }
    public ParentRecylerView(Context context, AttributeSet attrs, int defStyle){
        super(context,attrs,defStyle);
    }
    public ParentRecylerView(Context context, AttributeSet attrs){
        super(context,attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }
}

经过以上两个设置,就可以实现本文提出的需求了。

原文地址:https://www.cnblogs.com/jymblog/p/6812609.html