- 現在很多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);
- 兩句話就實現了固定頭部的效果。
