Android Filter ListView

1,412 阅读4分钟

今天整理的是之前项目中写的一个首页针对于列表进行条件过滤的效果。相关描述: 此次省略xxx字… 。
还是来看一下效果图吧(稍微有那么一点点大,1.5M左右,注意看好操作之后的变化):

这里写图片描述

废话就不多说了,接下来直奔主题,先看一下我们的界面布局:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#f0f0f0"
    android:descendantFocusability="blocksDescendants">

    <com.lvfq.homepage_master.view.CusScrollView
        android:id="@+id/cus_scroll_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="visible">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <FrameLayout
                android:id="@+id/fl_main_container"
                android:layout_width="match_parent"
                android:layout_height="180dp" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="80dp"
                android:background="#f0f0f0"
                android:orientation="horizontal"
                android:paddingBottom="5dp">

                <TextView
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:background="#fff"
                    android:drawableTop="@mipmap/ic_launcher"
                    android:gravity="center"
                    android:text="模拟占位" />

                <TextView
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_marginLeft="1dp"
                    android:layout_weight="1"
                    android:background="#fff"
                    android:drawableTop="@mipmap/ic_launcher"
                    android:gravity="center"
                    android:text="模拟占位" />

                <TextView
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_marginLeft="1dp"
                    android:layout_weight="1"
                    android:background="#fff"
                    android:drawableTop="@mipmap/ic_launcher"
                    android:gravity="center"
                    android:text="模拟占位" />
            </LinearLayout>

            <LinearLayout
                android:id="@+id/ll_list_parent"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <com.lvfq.homepage_master.view.MaxListView
                    android:id="@+id/main_max_listview"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:background="#fff"
                    android:divider="@null" />
            </LinearLayout>
        </LinearLayout>

    </com.lvfq.homepage_master.view.CusScrollView>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <LinearLayout
            android:id="@+id/ll_toolbar"
            android:layout_width="match_parent"
            android:layout_height="44dp"
            android:gravity="center"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="ToolBar"
                android:textColor="#fff"
                android:textSize="16sp" />

        </LinearLayout>

        <com.lvfq.homepage_master.view.FilterView
            android:id="@+id/main_filter"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:visibility="gone" />
    </LinearLayout>

</FrameLayout>

先来分别介绍一下 布局中的控件,
CusScrollView 是继承于 ScrollView 。 因为 ScrollView 本身 onScrollChanged是不对外提供的,所以我们这里需要自己去提供一个 ScrollChangeListener。

MaxListView 继承于 ListView 。 重写了 onMeasure 方法,解决 ListView 在 ScrollView 中显示不全的问题。

FilterView 组合自定义的一个筛选条件布局。

ScrollView 嵌套 ListView 这里有一个细节问题需要注意一下,那就是 ListView 和 ScrollView 的焦点问题,默认情况 ListView 会自动向上滚动。处理方式就是 在布局文件的根目录下 添加属性:

android:descendantFocusability="blocksDescendants"

标题栏渐变

下面我们来看一下每一个效果的实现,从标题栏渐变开始:
所谓的渐变,就是根据 ScrollView 的滚动位置,去设置 标题栏的 透明度。先看一下我们的 CusScrollView 源码:

public class CusScrollView extends ScrollView {

    private OnScrollChangedListener listener;

    public void setOnScrollChangedListener(OnScrollChangedListener listener){
        this.listener = listener;
    }

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

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

    public CusScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (listener != null) {
            listener.onScrollChanged(this ,l , t , oldl , oldt);
        }
    }
}

渐变的标题栏实现 :

cus_scroll_view.setOnScrollChangedListener(new OnScrollChangedListener() {
            @Override
            public void onScrollChanged(CusScrollView scrollView, int x, int y, int oldx, int oldy) {
                int bannerHeight = DPUtil.dip2px(MainActivity.this, 180 - toolBarHeight);  // 180 是 布局里面写死的 banner 高度。
                if (y > bannerHeight) {
                    ll_toolbar.setBackgroundColor(Color.argb((int) 255, 255, 122, 122));    // 给定的一个颜色值
                } else {
                    if (y <= 0) {
                        //设置 标题栏默认样式
                        setToolBarDefBg();
                    } else {
                        int alpha = (int) (255 * ((float) y / bannerHeight));
                        ll_toolbar.setBackgroundColor(Color.argb(alpha, 255, 122, 122));
                    }
                }

                // 控制筛选条件是否显示。
                int optionHeight = DPUtil.dip2px(MainActivity.this, 80);    // 80 是界面中 模拟占位区域的高度。
                if (y >= bannerHeight + optionHeight - filterHeight - toolBarHeight) {
                    filterView.setVisibility(View.VISIBLE);
                } else {
                    filterView.setVisibility(View.GONE);
                }

            }
        });

上面的一些数值,我都有相应说明,实际可根据自己需求调整。如果没有写死的话,也可以使用 view.getViewTreeObserver().addOnGlobalLayoutListener 去获取相关高度。

ListView的高度

我们上面 CusScrollView 的滚动监听,里面有控制筛选条件的显示隐藏,可以看到只有它滚动到一定的位置之后,才会进行显示的, 那么,当我们 ListView 数据条数不够的时候,我们如何去控制 ScrollView 能滚动的位置?
不知道大家有没有注意到,我们主布局中,ListView 外面是包裹了一层 LinearLayout 的,这个 LinearLayout 的作用到底是什么呢?我们来看一下相关代码:

ll_list_parent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int screenHeight = getResources().getDisplayMetrics().heightPixels;

                int statusHeight = DPUtil.getStatusBarHeight(MainActivity.this);    // 状态栏高度,像素
                initY = (int) ll_list_parent.getY();

                int minHeight = screenHeight - dip2px(MainActivity.this, toolBarHeight + filterHeight) - statusHeight;
                ll_list_parent.setMinimumHeight(minHeight);
                ll_list_parent.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });

其实这里就是为了控制当 ListView 的高度达不到指定要求的时候,我们用 LinearLayout 去把布局给撑起来。让 ScrollView 的高度可以支持 FilterView 的显示。
initY 这个变量值,是我们下面去控制每次进行筛选之后,让ListView 定位到筛选条件的下面。相关代码:

/**
     * 定位列表到第一条
     */
    private void resetRefreshLocal() {
        int offset = dip2px(this, filterHeight + toolBarHeight);   // 84  是 toolbar + 筛选条件的布局高度
        cus_scroll_view.scrollTo(0, initY - offset);// 设置 scrollView 滚动到某一个点,
    }

这里我也顺便说一下 scrollTo 这个方法,我个人的理解是:我们界面的左上角为(0,0)这个点是不会动的,我们在调用 scrollTo(x,y)的时候,就是让我们 View 的 x 和 y 的指定坐标点移动到左上角的(0,0)。举个例子: view.scrollTo(100 , 200) 实际就是为了把当前 view 的 x=100 , y=200这个坐标点移动到 左上角的(0,0)位置。

FilterView

关于 这个组合自定义的 FilterView 也没什么需要讲解的,无非就是一些逻辑判断。代码量稍微有点多,我这里就不直接贴代码了。有需要的朋友可以直接下载 Demo .

Demo下载地址(欢迎 Star)