ListView使用技巧

设置ListView需要显示在第几页

当需要指定ListView具体显示的Item时,可以通过如下代码来实现:

mListView.setSelection(position);

但这个方法类似scrollTo,是瞬间完成的移动。除此之外,还可以使用如下代码来实现平滑移动。

mListView.smoothScrollToPosition(position);
mListView.smoothScrollByOffset(offset);
mListView.smoothScrollBy(distance, duration);

ListView滑动监听

ListView的滑动监听,是ListView中最重要的技巧,很多重写的ListView,基本上都是在滑动事件的处理上下功夫,通过判断滑动事件进行不同的逻辑处理。而为了更加精确地监听滑动事件,开发者通常还需要使用GestureDetector手势识别、VelocityTracker滑动速度检测等辅助类来完成更好的监听。一般有两种监听方式,一种是通过OnTouchListener来实现监听,另一种是使用OnScrollListener来实现监听。

OnTouchListener

通过ACTION_DOWN、ACTION_MOVE、ACTION_UP这三个事件发生时的坐标,就可以根据坐标判断用户滑动的方向,并在不同的事件中进行相应的逻辑处理,代码如下:

        mListView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN://触摸时操作
                        break;
                    case MotionEvent.ACTION_MOVE://移动时操作
                        break;
                    case MotionEvent.ACTION_UP://离开时操作
                        break;
                }
                return false;
            }
        });
OnScrollListener

OnScrollListener是AbsListView中的监听事件,它封装了很多与ListView相关的信息,使用也更加灵活。

        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
            private int lastVisibleItemPosition;//上次第一个可视的Item的ID

            /**
             * 当用户没有做手指抛动的状态时,这个方法只会回调2次,否则会回调3次,差别就是手指抛动的这个状态。
             * 通常情况下,我们会在这个方法中通过不同的状态来设置一些标志Flag,来区分不同的滑动状态,供其它
             * 方法处理。
             * @param view
             * @param scrollState
             */
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                switch (scrollState) {
                    case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
                        //滑动停止时
                        Log.d("Test", "SCROLL_STATE_IDLE");
                        break;
                    case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
                        //正在滚动
                        Log.d("Test", "SCROLL_STATE_TOUCH_SCROLL");
                        break;
                    case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
                        //手指抛动时,即手指用力滑动,在离开后ListView由于惯性继续滑动的状态
                        Log.d("Test", "SCROLL_STATE_FLING");
                        break;
                }
            }

            /**
             * 该方法在ListView滚动时会一直回调,通过该方法的后3个参数,可以很方便地进行一些判断
             * @param view
             * @param firstVisibleItem 当前能看见的第一个Item的ID(从0开始)
             * @param visibleItemCount 当前看见的Item总数(包括没有显示完整的Item)
             * @param totalItemCount 整个ListView的Item总数
             */
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                //滚动时一直调用
                Log.d("Test", "onScroll");

                //判断是否滚动到最后一行
                if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {

                }

                //判断滚动的方向
                if (firstVisibleItem > lastVisibleItemPosition) {
                    //上滑
                } else if (firstVisibleItem < lastVisibleItemPosition) {
                    //下滑
                }
                lastVisibleItemPosition = firstVisibleItem;
            }
        });

另外,ListView也给我们提供了一些封装的方法来获得当前可视的Item的位置信息:

        //获取可视区域内最后一个Item的id
        mListView.getLastVisiblePosition();
        //获取可视区域内第一个Item的id
        mListView.getFirstVisiblePosition();

具有弹性的ListView

在IOS系统中,列表都是具有弹性的,即滚动到底端或者顶端后会继续往下或者往上滑动一段距离。网上有很多通过重写ListView来实现弹性效果的方法,比如增加HeadView或者使用ScrollView进行嵌套,方法很多,不过可以使用一种非常简单的方法来实现这个效果。虽然不如那些方法可定制化高、效果丰富,但主要的目的是让我们学会如何从源代码中找到问题的解决方法,效果图如下。

这里写图片描述

我们在查看ListView的源代码时可以发现,ListView中有一个控制滑动到边缘的处理方法,如下所示:

protected boolean overScrollBy(int deltaX, int deltaY,
            int scrollX, int scrollY,
            int scrollRangeX, int scrollRangeY,
            int maxOverScrollX, int maxOverScrollY,
            boolean isTouchEvent)

注意参数:maxOverScrollY,注释中写道——Number of pixels to overscroll by in either direction along the Y axis。由此可以发现,虽然它的默认值是0,但其实只要修改这个参数的值,就可以让ListView具有弹性了。代码如下:

package com.example.huangfei.myapplication;

import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.widget.ListView;

/**
 * Created by huangfeihong on 2016/5/4.
 * 具有弹性的ListView
 */
public class FlexibleListView extends ListView {

    private int mMaxOverDistance = 50;
    private Context mContext;

    public FlexibleListView(Context context) {
        super(context);
        mContext = context;
        init();
    }

    public FlexibleListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init();
    }

    public FlexibleListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        init();
    }

    //让不同分辨率设备的弹性距离基本一致
    private void init() {
        DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
        float density = metrics.density;
        mMaxOverDistance = (int) (density * mMaxOverDistance);
    }

    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mMaxOverDistance, isTouchEvent);
    }
}

自动显示、隐藏布局的ListView

若要实现当我们在ListView上滑动的时候,顶部的布局就会相应的隐藏或显示的效果,我们应该怎么办,效果图如下:

这里写图片描述

要让一个布局显示或者隐藏并带有动画效果,可以通过属性动画来很方便地实现,所以这个效果的关键在于根据ListView的滑动方向来判断是否需要显示或隐藏对应的布局。

布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:headerDividersEnabled="false" />

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@android:color/holo_blue_light"/>
</RelativeLayout>

代码如下:

package com.example.huangfei.myapplication;

import android.animation.ObjectAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

/**
 * 自动显示、隐藏布局的ListView
 */
public class ScrollHideListViewActivity extends Activity {

    private ListView mListView;
    private Toolbar mToolbar;

    private String[] data = new String[30];

    private float mFirstY;
    private float mCurrentY;
    private int mDirection;
    private int mTouchSlop;
    private boolean mShow = true;

    private ObjectAnimator mAnimator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scroll_hide);

        mListView = (ListView) findViewById(R.id.listview);
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        for (int i = 0; i < data.length; i++) {
            data[i] = "Item " + i;
        }

        /**
         * 在开使判断滑动事件之前,要先给ListView增加一个HeaderView,避免第一个Item被Toolbar遮挡.
         * 其中通过使用abc_action_bar_default_height_material属性获取系统Actionbar的高度
         */
        View headerView = new View(this);
        headerView.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, (int) getResources().getDimension(R.dimen.abc_action_bar_default_height_material)));
        mListView.addHeaderView(headerView);

        mListView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_expandable_list_item_1, data));

        //定义系统认为的最低滑动距离
        mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
        ObjectAnimator mAnimator;
        mListView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN://触摸时操作
                        mFirstY = event.getY();
                        break;
                    case MotionEvent.ACTION_MOVE://移动时操作
                        mCurrentY = event.getY();
                        if (mCurrentY - mFirstY > mTouchSlop) {
                            mDirection = 0;//down
                        } else if (mFirstY - mCurrentY > mTouchSlop) {
                            mDirection = 1;//up
                        }

                        if (mDirection == 1) {
                            if (mShow) {
                                toolbarAnim(1);//hide
                                mShow = !mShow;
                            }
                        } else if (mDirection == 0) {
                            if (!mShow) {
                                toolbarAnim(0);//show
                                mShow = !mShow;
                            }
                        }
                        break;
                    case MotionEvent.ACTION_UP://离开时操作
                        break;
                }
                return false;
            }
        });
    }

    private void toolbarAnim(int flag) {
//        if (mAnimator != null && mAnimator.isRunning()) {
//            mAnimator.cancel();
//        }
        if (flag == 0) {
            mAnimator = ObjectAnimator.ofFloat(mToolbar, "translationY", mToolbar.getTranslationY(), 0);
        } else {
            mAnimator = ObjectAnimator.ofFloat(mToolbar, "translationY", mToolbar.getTranslationY(), -mToolbar.getHeight());
        }
        mAnimator.start();
    }
}

这里使用了Toolbar这样一个新控件,Google已经推荐它用来逐渐取代ActionBar了,因为它更灵活。但是在使用的时候,一定要注意使用的theme一定要是NoActionBar的,不然会引起冲突。

代码地址

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值