滑动ListView自动隐藏页面头部和底部元素的例子

完整工程代码在这:https://github.com/NashLegend/Auto-Hide-ListView

现在很多软件都有这种滑动列表的时候自动隐藏页面头部和底部元素的功能,比如Google+。在刚刚进入Activity的时候,页面是一个列表,底部有一个view,头部一个view,当列表向上滑动的时候,隐藏头尾元素,以显示更多内容,当列表向下滑动的时候,再将头尾元素拉出来。比如Google+。

刚刚进入时是这个样子:

再把列表身上一拉,头尾隐藏,成了这个样子:

再往下拉,就会再变回第一张图的样子。

这个例子实现的就是这个功能

这个例子里面,MainActivity的布局如下,ToolBar是顶部元素,Button为底部元素。

<RelativeLayout 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=".MainActivity">

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

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

    <Button
        android:id="@+id/footer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="@string/ScrollDown" />
</RelativeLayout>
public class MainActivity extends ActionBarActivity {

    ListView listView;
    Toolbar toolbar;
    View header;
    View footer;
    int touchSlop = 10;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        touchSlop = (int) (ViewConfiguration.get(MainActivity.this).getScaledTouchSlop() * 0.9);//滚动过多少距离后才开始计算是否隐藏/显示头尾元素。这里用了默认touchslop的0.9倍。
        listView = (ListView) findViewById(R.id.list_view);
        footer = findViewById(R.id.footer);
        toolbar = (Toolbar) findViewById(R.id.action_bar);
        // 下面这句将这个ToolBar设置为ActionBar,在这个例子里面,这句其实用不着,但是如果用了这句,就得把Theme设置为NoActionBar了,无关这里要说的,具体见上面的链接中的Style
        setSupportActionBar(toolbar);

        //为这个ListView填充元素。
        String[] str = new String[64];
        for (int i = 0; i < str.length; i++) {
            str[i] = "Android " + i;
        }
        //R.layout.simple_layout是一个TextView,详见上面的链接……
        ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, R.layout.simple_layout, str);
        listView.setAdapter(adapter);

        //为ListView添加一个Header,这个Header与ToolBar一样高。这样我们可以正确的看到列表中的第一个元素而不被遮住。
        header = new View(MainActivity.this);
        header.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) getResources().getDimension(R.dimen.abc_action_bar_default_height_material)));
        header.setBackgroundColor(Color.parseColor("#00000000"));
        listView.addHeaderView(header);

        //为ListView设置触摸事件和滚动事件,这是核心
        listView.setOnTouchListener(onTouchListener);
        listView.setOnScrollListener(onScrollListener);
        footer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //为button设置点击事件,点击一次滚动10个item
                listView.smoothScrollByOffset(10);
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    AnimatorSet backAnimatorSet;//这是显示头尾元素使用的动画

    private void animateBack() {
        //先清除其他动画
        if (hideAnimatorSet != null && hideAnimatorSet.isRunning()) {
            hideAnimatorSet.cancel();
        }
        if (backAnimatorSet != null && backAnimatorSet.isRunning()) {
            //如果这个动画已经在运行了,就不管它
        } else {
            backAnimatorSet = new AnimatorSet();
            //下面两句是将头尾元素放回初始位置。
            ObjectAnimator headerAnimator = ObjectAnimator.ofFloat(toolbar, "translationY", toolbar.getTranslationY(), 0f);
            ObjectAnimator footerAnimator = ObjectAnimator.ofFloat(footer, "translationY", footer.getTranslationY(), 0f);
            ArrayList<Animator> animators = new ArrayList<>();
            animators.add(headerAnimator);
            animators.add(footerAnimator);
            backAnimatorSet.setDuration(300);
            backAnimatorSet.playTogether(animators);
            backAnimatorSet.start();
        }
    }

    AnimatorSet hideAnimatorSet;//这是隐藏头尾元素使用的动画

    private void animateHide() {
        //先清除其他动画
        if (backAnimatorSet != null && backAnimatorSet.isRunning()) {
            backAnimatorSet.cancel();
        }
        if (hideAnimatorSet != null && hideAnimatorSet.isRunning()) {
            //如果这个动画已经在运行了,就不管它
        } else {
            hideAnimatorSet = new AnimatorSet();
            ObjectAnimator headerAnimator = ObjectAnimator.ofFloat(toolbar, "translationY", toolbar.getTranslationY(), -toolbar.getHeight());//将ToolBar隐藏到上面
            ObjectAnimator footerAnimator = ObjectAnimator.ofFloat(footer, "translationY", footer.getTranslationY(), footer.getHeight());//将Button隐藏到下面
            ArrayList<Animator> animators = new ArrayList<>();
            animators.add(headerAnimator);
            animators.add(footerAnimator);
            hideAnimatorSet.setDuration(200);
            hideAnimatorSet.playTogether(animators);
            hideAnimatorSet.start();
        }
    }

    View.OnTouchListener onTouchListener = new View.OnTouchListener() {

        float lastY = 0f;
        float currentY = 0f;
        //下面两个表示滑动的方向,大于0表示向下滑动,小于0表示向上滑动,等于0表示未滑动
        int lastDirection = 0;
        int currentDirection = 0;

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    lastY = event.getY();
                    currentY = event.getY();
                    currentDirection = 0;
                    lastDirection = 0;
                    break;
                case MotionEvent.ACTION_MOVE:
                    if (listView.getFirstVisiblePosition() > 0) {
                        //只有在listView.getFirstVisiblePosition()>0的时候才判断是否进行显隐动画。因为listView.getFirstVisiblePosition()==0时,
                        //ToolBar——也就是头部元素必须是可见的,如果这时候隐藏了起来,那么占位置用了headerview就被用户发现了
                        //但是当用户将列表向下拉露出列表的headerview的时候,应该要让头尾元素再次出现才对——这个判断写在了后面onScrollListener里面……
                        float tmpCurrentY = event.getY();
                        if (Math.abs(tmpCurrentY - lastY) > touchSlop) {//滑动距离大于touchslop时才进行判断
                            currentY = tmpCurrentY;
                            currentDirection = (int) (currentY - lastY);
                            if (lastDirection != currentDirection) {
                                //如果与上次方向不同,则执行显/隐动画
                                if (currentDirection < 0) {
                                    animateHide();
                                } else {
                                    animateBack();
                                }
                            }
                        }
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    //手指抬起的时候要把currentDirection设置为0,这样下次不管向哪拉,都与当前的不同(其实在ACTION_DOWN里写了之后这里就用不着了……)
                    currentDirection = 0;
                    lastDirection = 0;
                    break;
            }
            return false;
        }
    };

    AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {

        //这个Listener其实是用来对付当用户的手离开列表后列表仍然在滑动的情况,也就是SCROLL_STATE_FLING

        int lastPosition = 0;//上次滚动到的第一个可见元素在listview里的位置——firstVisibleItem
        int state = SCROLL_STATE_IDLE;

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            //记录当前列表状态
            state = scrollState;
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            if (firstVisibleItem == 0) {
                animateBack();
            }
            if (firstVisibleItem > 0) {
                if (firstVisibleItem > lastPosition && state == SCROLL_STATE_FLING) {
                    //如果上次的位置小于当前位置,那么隐藏头尾元素
                    animateHide();
                }

                //================================
                if (firstVisibleItem < lastPosition && state == SCROLL_STATE_FLING) {
                    //如果上次的位置大于当前位置,那么显示头尾元素,其实本例中,这个if没用
                    //如果是滑动ListView触发的,那么,animateBack()肯定已经执行过了,所以没有必要
                    //如果是点击按钮啥的触发滚动,那么根据设计原则,按钮肯定是头尾元素之一,所以也不需要animateBack()
                    //所以这个if块是不需要的
                    animateBack();
                }
                //这里没有判断(firstVisibleItem == lastPosition && state == SCROLL_STATE_FLING)的情况,
                //但是如果列表中的单个item如果很长的话还是要判断的,只不过代码又要多几行
                //但是可以取巧一下,在触发滑动的时候拖动执行一下animateHide()或者animateBack()——本例中的话就写在那个点击事件里就可以了)
                //BTW,如果列表的滑动纯是靠手滑动列表,而没有类似于点击一个按钮滚到某个位置的话,只要第一个if就够了…

            }
            lastPosition = firstVisibleItem;
        }
    };
}

时间: 2024-12-25 08:02:17

滑动ListView自动隐藏页面头部和底部元素的例子的相关文章

设置div默认顶部显示,并滑动显示或隐藏

html div默认是隐藏的,如果想默认显示,去掉style里面的"display:none;"即可 <div id="msg" style="display:none;z-index:99;width:100%;background:#d2d2d2;margin-left:0;margin-right:0;padding-right:15px;padding-left:15px;padding-top:15px;padding-bottom:15p

页面跳转 和虚拟键盘的自动隐藏

前提:起码要有两个视图控制器. 注意事项: 设置跳转的动画效果 UITextField输入时,虚拟键盘的位置设置(整体布局向上移动,虚拟键盘的自动隐藏) 实现的功能: A:在第一个页面跳转到第二个页面,然后第二个页面点击按钮,进行返回 B:在第一个页面有一个text控件,点击一个按钮,传递到第二个页面,然后显示到第二个 页面的label上面: 效果展示: 步骤: 1.新建两个类,然后勾选xib,在AppDelegate的实现文件把其中的一个视图加载进去 //新建一个视图控制器 MainViewC

数据更新后让ListView自动滚动到底部

在做聊天界面的时候想要发送新的数据后,listview自动滚动到底部,显示出最新的数据.网上找了两个方法,觉得不错,记录一下. 方法一: 给listview添加下面两个属性 android:stackFromBottom="true"android:transcriptMode="alwaysScroll" or mListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL); 方法二: //

页面滚动到底部自动加载下一页功能的实现

页面滚动到底部自动加载下一页功能的实现,效果见本博首页 1.endlesspage.js文件内容 var gPageSize = 10; var i = 1; //设置当前页数,全局变量 var finished = false; var dataUrl = ''; $(function () { //根据页数读取数据 function getData(pagenumber) { //console.log(i); $.get(dataUrl, { pagesize: gPageSize, p:

listview中item 有checkbox多选防止滑动 listview页面 出现checkbox错位问题

checkbox点击切换背景 <CheckBox android:id="@+id/checkbox" android:layout_width="40dp" android:layout_height="40dp" android:button="@null" android:focusable="false" android:background="@drawable/playimage

在html页面中引入公共的头部和底部

参考链接: http://www.cnblogs.com/jason-star/p/3345225.html http://blog.csdn.net/jsxzzliang/article/details/47022055 使用SHTML进行公共头部和底部的引用       SHTML介绍:shtml是一种基于SSI技术的文件,也就是Server Side Include--SSI 服务器端包含指令,一些Web Server如果有SSI功能的话就会对shtml文件特殊招待,服务器会先扫一次sht

在 IE 浏览器中,使用 bootstrap 使得页面滚动条浮动显示,自动隐藏,自动消失

貌似是从 IE10 开始?为了触屏操作优化浏览器的内容显示,IE 浏览器提供了一种可以浮动显示,自动隐藏的滚动条样式,但是这个样式会在某些情况下造成一些困扰,比如下图... 其实默认情况下,桌面版的 IE 应该是传统的滚动条样式:而在应用版的 IE 中,滚动条默认才是浮动显示,自动隐藏. 但是可能会发现,一些使用了 bootstrap 样式的网站,也会出现相同这种情况,谷歌了一下,发现这是因为 bootstrap 设置了一个 CSS 的属性: @-ms-viewport { width: dev

HTML5 开发APP(头部和底部选项卡)

我们开发app有一定固定的样式,比如头部和底部选项卡部分就是公共部分就比如我在做的app进来的主页面就像图片显示的那样 我们该怎么实现呢,实现我们应该建一个主页面index.html,然后建五个子页面,通过mui来实现切换功能. 在index的html部分写下这样的代码 <body> <header class="mui-bar mui-bar-nav" style="padding-right: 15px;background: #00be68;"

移动web页面头部书写

HTTP 标题信息(http-equiv) 和页面描述信息(name) http-equiv:该枚举的属性定义,可以改变服务器和用户代理行为的编译.编译的值取content 里的内容.简单来说即可以模拟 HTTP 协议响应头.最常见的大概属于Content-Type了,设置编码类型.如 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> H5中可以简化为 <me