先看看效果:
布局文件
<com.example.stickview.MyScrollView 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="com.example.stickview.MainActivity"
android:id="@+id/scroll_view">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:textSize="30sp"
android:background="#dd33aa"
android:gravity="center"
android:text="top"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="2000dp"
android:layout_marginTop="250dp"
android:background="@drawable/bg_shade">
LinearLayout>
<LinearLayout
android:id="@+id/stick_view_space"
android:layout_width="match_parent"
android:layout_height="50dip"
android:layout_marginTop="200dp"
android:orientation="vertical">
LinearLayout>
<LinearLayout
android:id="@+id/stick_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#cc1133">
<TextView
android:layout_width="fill_parent"
android:layout_height="50dip"
android:background="#00ff33"
android:gravity="center"
android:text="这 是 滚 动 的 等 一 下 固 定 在 上 面的视图" />
LinearLayout>
FrameLayout>
com.example.stickview.MyScrollView>
这里需要注意的是将需要置顶悬浮的View放在FrameLayout的最上层,不然可能会被其他的View挡住
package com.example.stickview;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.ViewTreeObserver;
import android.widget.LinearLayout;
public class MainActivity extends AppCompatActivity implements ScrollListener{
LinearLayout viewSpace;
LinearLayout stickView;
MyScrollView myScrollView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myScrollView = (MyScrollView) findViewById(R.id.scroll_view);
myScrollView.registerListener(this);
viewSpace = (LinearLayout) findViewById(R.id.stick_view_space);
stickView = (LinearLayout) findViewById(R.id.stick_view);
stickView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
//当布局文件加载完成时,会调用该方法
@Override
public void onGlobalLayout() {
//初始化stickView的位置
onScroll(0,0);
}
});
}
@Override
public void onScroll(int top, int left) {
/**
* 根据scrollView的滑动距离,来动态移动stickView
* setTranslationY(x) 当x为正数的时候stickView是向下移动的
* 开始时 top< viewSpace.getTop() 所以stickView 的位置一直与viewSpace位置重合,随着ScrollView一起滑动
* 当用户手动向上滑动MyScrollView的时候,stickView(或viewSpace)处于顶部的时候,此时top > viewSpace.getTop()
* stickView就会相对scrollView向下运动,又因为scrollView是向下运动的,所以相对手机屏幕就是静止的啦,看起来就是悬浮在顶部
*/
stickView.setTranslationY(Math.max(top, viewSpace.getTop()));
}
}
最难理解的是这一句,也是现实这个功能最重要的一句代码
stickView.setTranslationY(Math.max(top, viewSpace.getTop()));
代码里面已经注释,不懂的可以看看
下面是一个实现滚动回调的ScrollView,代码简单
package com.example.stickview;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ScrollView;
/**
* User : guanhuan
* Date : 2016/8/3
* 将onScrollChanged暴露给外面调用
*/
public class MyScrollView extends ScrollView{
private ScrollListener scrollListener;
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyScrollView(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);
Log.e("Tag", "l = " + l + ", t = " + t + ", oldl =" + oldl + ",oldt =" + oldt);
if (scrollListener != null) {
scrollListener.onScroll(t, l);
}
}
public void registerListener(ScrollListener scrollListener){
this.scrollListener = scrollListener;
}
}
实现这样的功能还有其他的实现,比如使用两个同样的view,一个放在顶部的位置,一个嵌套在ScrollView的里面,通过计算ScrollView的滑动距离,来隐藏或者显示其中的某一View,但是感觉这个实现有点麻烦。还是觉得我的这种实现更加简单易用。