Android开发——实现固定在ScrollView顶部的View,类似于新浪微博的评论列表的顶部

  • 现在很多App都实现了这个功能,例如新浪微博评论页面的评论、转发、赞的数字可以固定在屏幕上方。我个人很喜欢这种设计,所以利用一点空余时间简单实现了一个类似的功能。
  • 先来看一下上面这张图的效果

  • 这个是新浪微博的一个页面,整体布局大致分了三块:正文内容、转发评论赞的数字条、评论列表
  • 其中数字条是可以跟着ScrollView一起滑动,但在滑到最顶部时固定在最上面,而下面的评论内容可以继续滑动。
  • 下面是自己实现的效果图:

  • 实现原理:

      当滚动条划过头部时,把需要固定的头部从父布局中移除,然后添加到最外层布局的顶部。

      当滚动条返回时又把最外层的头部移,然后重新添加到原来的父布局里面。

      整个实现代码,不算上布局,也就100行左右。

 

  • 详细实现逻辑:

    首先建一个自定义View叫MyHoveringScrollView继承自FrameLayout,在布局里MyHoveringScrollView处于最外层。

    由于FrameLayout本身是不支持滚动条的,所以在FrameLayout内部有一个自定义的ScrollView。

    在初始化的时候,通过getChildAt(0)把子布局拿到,然后清空整个布局,然后实例化一个自己的ScrollView,把之前拿到的子布局添加到ScrollView里面,

    最后把ScrollView添加到MyHoveringScrollView里面。

public void init() {
        post(new Runnable() {
            @Override
            public void run() {
                mContentView = (ViewGroup) getChildAt(0);
                removeAllViews();

                MyScrollView scrollView = new MyScrollView(getContext(), MyHoveringScrollView.this);
                scrollView.addView(mContentView);
                addView(scrollView);
            }
        });
    }
  • 可能注意到了两点:

    1、我用了post()。因为在构造方法里面布局还没有生成,getChildAt(0)是拿不到东西的,但是post()会把动作放到队列里,等布局完成后再从队列里取出来,所以这里是个小窍门。

    2、我把MyHoveringScrollView传入到了ScrollView里面,这么做其实是为了让ScrollView回调MyHoveringScrollView的方法。(比较懒,不想写接口……)

  • 然后通过setTopView()方法,把需要固定在顶部的ID传进来:
public void setTopView(final int id) {
        post(new Runnable() {
            @Override
            public void run() {
                mTopView = (ViewGroup) mContentView.findViewById(id);

                int height = mTopView.getChildAt(0).getMeasuredHeight();
                ViewGroup.LayoutParams params = mTopView.getLayoutParams();
                params.height = height;
                mTopView.setLayoutParams(params);
                mTopViewTop = mTopView.getTop();
                mTopContent = mTopView.getChildAt(0);
            }
        });
    }
  • 注意为什么要调用mTopView.setLayoutParams(),因为头部的布局高度必须得固定,如果是wrap_content,虽然也不会有什么错误,但效果不太好,可以自己试一下。
  • 接下来,在ScrollView里面重写onScrollChanged()方法,并回调给MyHoveringScrollView的onScroll方法:
private static class MyScrollView extends ScrollView {
        private MyHoveringScrollView mScrollView;
        public MyScrollView(Context context, MyHoveringScrollView scrollView) {
            super(context);
            mScrollView = scrollView;
        }
        @Override
        protected void onScrollChanged(int l, int t, int oldl, int oldt) {
            super.onScrollChanged(l, t, oldl, oldt);
            mScrollView.onScroll(t);
        }
    }
public void onScroll(final int scrollY) {
        post(new Runnable() {
            @Override
            public void run() {
                if (mTopView == null
                        ) return;
                if (scrollY >= mTopViewTop
                        && mTopContent.getParent() == mTopView) {
                    mTopView.removeView(mTopContent);
                    addView(mTopContent);
                } else if (scrollY < mTopViewTop
                        && mTopContent.getParent() == MyHoveringScrollView.this) {
                    removeView(mTopContent);
                    mTopView.addView(mTopContent);
                }
            }
        });
    }
  • 如果scrollY >= mTopViewTop就是头部应该被固定在顶部的时候
  • 如果scrollY < mTopViewTop就是头部应该取消固定,还原到原来父布局的时候
  • 至此,功能就实现了!
  • 怎么使用呢?首先先写布局:
<com.hide.myhoveringscroll.app.MyHoveringScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/view_hover"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

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

        <TextView
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:gravity="center"
            android:text="这是头部" />

        <FrameLayout
            android:id="@+id/top"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="#AAff0000"
                android:orientation="horizontal"
                android:padding="20dp" >

                <TextView
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:layout_weight="1"
                    android:gravity="center"
                    android:text="这是固定部分"
                    android:textSize="16sp" />

                <Button
                    android:id="@+id/btn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="点我一下" />
            </LinearLayout>
        </FrameLayout>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="10dp"
            android:text="内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/n内容/nn内容/n内容/n内容/n内容/n />
    </LinearLayout>

</com.hide.myhoveringscroll.app.MyHoveringScrollView>
  • 其中:MyHoveringScrollView在最外层,充当ScrollView的角色(所以子布局只能有一个)
  • android:id="@+id/top也就是需要固定在顶部的布局
  • 最后回到Activity:
view_hover = (MyHoveringScrollView) findViewById(R.id.view_hover);
view_hover.setTopView(R.id.top);
  • 两句话就实现了固定头部的效果。

 

posted @ 2016-05-04 10:12  酸奶布丁  阅读(2570)  评论(0编辑  收藏  举报