项目源代码地址:https://git.oschina.net/steve/HoveringScroll
实现原理解析:
我这里使用了不同的颜色,将后面会解说到的几个布局标注了出来。
这里有几个对象A布局、B布局、C布局须要事先说明一下:
A布局示意图:
B布局示意图:
C布局示意图:
1.Activity初始化时的状态,此时C布局在B布局上:
2.当向上滑动屏幕,布局B也会随着滚动布局向上滚动:
3.当向上滑动的距离超过了開始时顶部布局的高度H时,本来在B上的子布局C就会被移除,然后加到A布局上:
代码实现部分:
// 自己定义ScrollView滑动监听。获取实时的Y轴滑动距离
@Override
public void onScroll(int scrollY)
{
Log.d("guxuewu", "scrollY=>" + scrollY);
// 假设滑动的距离大于等于了顶部布局的高度H
if (scrollY >= searchLayoutTop)
{
// 而且固定布局C的父布局不是A布局
if (hoveringLayout.getParent() != search01)
{
// B布局移除布局C
search02.removeView(hoveringLayout);
// A增加布局C
search01.addView(hoveringLayout);
}
}
// 假设滑动距离小于了顶部布局的高度
else
{
// 而且固定布局C的父布局不是B布局
if (hoveringLayout.getParent() != search02)
{
// A布局移除固定布局C
search01.removeView(hoveringLayout);
// B增加固定布局C
search02.addView(hoveringLayout);
}
}
}
代码具体解释
HoveringScrollSampleActivity.java
public class HoveringScrollSampleActivity extends Activity implements OnScrollListener
{
// 自己定义的ScrollView
private HoveringScrollview hoveringScrollview;
// 顶部布局的高度
private int searchLayoutTop;
// 顶部布局
private RelativeLayout rlayout;
// 中间固定条的布局
private LinearLayout hoveringLayout;
// 不尾随ScrollView移动的父布局
private LinearLayout search01;
// 尾随ScrollView移动的父布局
// Search01的布局高度要和search02的设置一样,这样看起来就不会有替换的感觉
private LinearLayout search02;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_samples);
initViews();
}
private void initViews()
{
hoveringLayout = (LinearLayout)findViewById(R.id.hoveringLayout);
hoveringScrollview = (HoveringScrollview)findViewById(R.id.hoveringScrollview);
search01 = (LinearLayout)findViewById(R.id.search01);
search02 = (LinearLayout)findViewById(R.id.search02);
rlayout = (RelativeLayout)findViewById(R.id.rlayout);
hoveringScrollview.setOnScrollListener(this);
}
@Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
// 每次启动,设置顶部布局的高度
if (hasFocus)
{
searchLayoutTop = rlayout.getBottom();
}
}
// 自己定义ScrollView滑动监听,获取实时的Y轴滑动距离
@Override
public void onScroll(int scrollY)
{
Log.d("guxuewu", "scrollY=>" + scrollY);
// 假设滑动的距离大于等于了顶部布局的高度H
if (scrollY >= searchLayoutTop)
{
// 而且固定布局C的父布局不是A布局
if (hoveringLayout.getParent() != search01)
{
// B布局移除布局C
search02.removeView(hoveringLayout);
// A增加布局C
search01.addView(hoveringLayout);
}
}
// 假设滑动距离小于了顶部布局的高度
else
{
// 而且固定布局C的父布局不是B布局
if (hoveringLayout.getParent() != search02)
{
// A布局移除固定布局C
search01.removeView(hoveringLayout);
// B增加固定布局C
search02.addView(hoveringLayout);
}
}
}
// 点击事件监听
public void clickListenerMe(View view)
{
if (view.getId() == R.id.btnQiaBuy)
{
Toast.makeText(this, "抢购成功", Toast.LENGTH_SHORT).show();
}
}
}
HoveringScrollview.java
public class HoveringScrollview extends ScrollView
{
// 滚动监听器
private OnScrollListener onScrollListener;
/**
* 主要是用在用户手指离开本view。本view还在继续滑动,我们用来保存Y的距离,然后做比較
*/
private int lastScrollY;
public HoveringScrollview(Context context, AttributeSet attrs)
{
super(context, attrs);
}
/**
* 设置滚动监听接口
*/
public void setOnScrollListener(OnScrollListener onScrollListener)
{
this.onScrollListener = onScrollListener;
}
/**
* 用于用户手指离开MyScrollView的时候获取MyScrollView滚动的Y距离,然后回调给onScroll方法中
*/
@SuppressLint("HandlerLeak")
private Handler handler = new Handler()
{
public void handleMessage(android.os.Message msg)
{
int scrollY = HoveringScrollview.this.getScrollY();
// 此时的距离和记录下的距离不相等,在隔6毫秒给handler发送消息
if (lastScrollY != scrollY)
{
lastScrollY = scrollY;
// 相当于开启了一个无线循环的消息机制
handler.sendMessageDelayed(handler.obtainMessage(), 20);
}
if (onScrollListener != null)
{
onScrollListener.onScroll(scrollY);
}
};
};
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent ev)
{
// 当用户的手在HoveringScrollview上面的时候,
// 直接将HoveringScrollview滑动的Y方向距离回调给onScroll方法中,
if (onScrollListener != null)
{
onScrollListener.onScroll(lastScrollY = this.getScrollY());
}
switch (ev.getAction())
{
// 当用户抬起手的时候,HoveringScrollview可能还在滑动,
// 所以当用户抬起手我们隔6毫秒给handler发送消息。
// 在handler处理 HoveringScrollview滑动的距离
case MotionEvent.ACTION_UP:
handler.sendMessageDelayed(handler.obtainMessage(), 20);
break;
}
return super.onTouchEvent(ev);
};
/**
* 滚动的回调接口
*/
public interface OnScrollListener
{
/**
* 回调方法, 返回本view滑动的Y方向距离
*/
public void onScroll(int scrollY);
}
}
布局文件activity_samples.xml
<?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"
android:paddingBottom="@dimen/activity_vertical_margin" >
<com.steve.hovering.samples.HoveringScrollview
android:id="@+id/hoveringScrollview"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- 套在ScrollView中的唯一父布局 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<RelativeLayout
android:id="@+id/rlayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" >
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="TOP信息占位
TOP信息
TOP信息
TOP信息占位"
android:textColor="#d19275"
android:textSize="30sp" />
</RelativeLayout>
<!-- 尾随ScrollView移动的父布局 -->
<LinearLayout
android:id="@+id/search02"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="@android:color/holo_green_light" >
<!-- 这个悬浮条必须是固定高度 -->
<!-- android:background="#A8A8A8" -->
<LinearLayout
android:id="@+id/hoveringLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp" >
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="10dp"
android:text="¥188
原价:¥399"
android:textColor="#FF7F00" />
<Button
android:id="@+id/btnQiaBuy"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#FF7F00"
android:onClick="clickListenerMe"
android:padding="10dp"
android:text="马上抢购"
android:textColor="#FFFFFF" />
</LinearLayout>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="1測试内容
2測试内容
3測试内容
4測试内容
5測试内容
6測试内容
7測试内容
8測试内容
9測试内容
10測试内容
11測试内容
12測试内容
13測试内容
14測试内容
15測试内容
16測试内容
測试内容
測试内容
測试内容
測试内容
測试内容
測试内容
測试内容
測试内容
測试内容
測试内容
27測试内容"
android:textSize="40sp" />
</LinearLayout>
</com.steve.hovering.samples.HoveringScrollview>
<!-- 固定不动的用来装固定布局的父布局 -->
<LinearLayout
android:id="@+id/search01"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="@android:color/darker_gray"
android:orientation="vertical" >
</LinearLayout>
</RelativeLayout>
终于效果
学习心得
学习大神的代码总能收益颇多,之前也一直想研究这个效果怎么做出来。无意在论坛看到这个源代码分享,便迫不及待拿来细细研究。
研究后发现自己原来好多实现想法想复杂了,原来这么简单事实上就能够实现这个效果。
所以我们还是得敢于尝试,尝试用最简单的方法去实现想要的效果。