现在主流的App中许多加入了吸顶效果,用于在数据较多的展现时与ListView、ScrollView套用。使得用户在滑动读取数据的时候把产品需要持续展示的控件及信息一直固定在屏幕上方,以便用户操作和交互。
先上图:
首先,我的实现思想是自定义控件ScrollView,检测这个ScrollView Y方向上的滑动距离,回调到程序中进行使用。根据滑动的距离来对吸顶栏进行相应的显示与隐藏。在实现过程中我是用了两个吸顶栏,作为切换来使用,使用户直观感觉是吸顶效果。网上也有用一个第三方的比较强大的Scrolling Tricks来实现,这里我们就不一一赘述了。
首先是布局文件:
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
android:id="@+id/parent_layout"
tools:context=".MainActivity">
<com.example.administrator.myapplication.com.example.administrator.cunstoms.MyScrollView
android:id="@+id/scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="@drawable/top"
android:scaleType="fitXY"
/>
<include
android:id="@+id/top"
layout="@layout/top_layout"
/>
<com.example.administrator.myapplication.com.example.administrator.cunstoms.MyListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<include
android:id="@+id/top_include"
layout="@layout/top_layout"
/>
</FrameLayout>
</com.example.administrator.myapplication.com.example.administrator.cunstoms.MyScrollView>
</LinearLayout>
top_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="吸顶栏"
android:textSize="20sp"
android:gravity="center"
android:background="#fff"
/>
</LinearLayout>
代码如下:
1.自定义的ScrollView
package com.example.administrator.myapplication.com.example.administrator.cunstoms;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.AbsListView;
import android.widget.ScrollView;
/**
* Created by Administrator on 2015/11/21.
* 返回Y距离的自定义ScrollView
*/
public class MyScrollView extends ScrollView {
private OnScrollListener onScrollListener;
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
//设置滚动接口
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
@Override
protected int computeVerticalScrollRange() {
return super.computeVerticalScrollRange();
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (onScrollListener!=null){
onScrollListener.onScroll(t);
}
}
public interface OnScrollListener{
/*
* 回调方法
* 回调该控件Y方向的变化距离
* **/
public void onScroll(int scrollY);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
在这个文件中我们可以看到其实这个自定义的控件其实并没有太多逻辑性的东西,也没有太多关于数学测量的知识。主要是通过
onScrollChanged()方法获取了这个ScrollView的Y轴上滑动的距离。并且定义了一个回调接口<span style="font-family: Arial, Helvetica, sans-serif;">OnScrollListener,通过</span><span style="font-family: Arial, Helvetica, sans-serif;">onScroll(int scrollY)方法来回调Y方向上的距离改变值,这里的距离改变值是以控件的ScrollView顶部为起点,滑出屏幕的长度就是这里的距离改变值。</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">2.MainActivity.java</span>
<span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="java">package com.example.administrator.myapplication;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.ViewTreeObserver;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import com.example.administrator.myapplication.com.example.administrator.cunstoms.MyListView;
import com.example.administrator.myapplication.com.example.administrator.cunstoms.MyScrollView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements MyScrollView.OnScrollListener {
private MyScrollView mScrollView;
private MyListView mListView;
private LinearLayout top;
private LinearLayout topInclude;
private List<String> data;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
mScrollView= (MyScrollView) findViewById(R.id.scrollview);
mListView= (MyListView) findViewById(R.id.listview);
top= (LinearLayout) findViewById(R.id.top);
topInclude= (LinearLayout) findViewById(R.id.top_include);
ArrayAdapter adapter=new ArrayAdapter(this,android.R.layout.simple_list_item_1,data);
mListView.setAdapter(adapter);
mScrollView.setOnScrollListener(this);
findViewById(R.id.parent_layout).getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
onScroll(mScrollView.getScrollY());
}
});
}
private void initData() {
data=new ArrayList<String>();
for (int i=0;i<40;i++){
data.add("Data-"+i);
}
}
@Override
public void onScroll(int scrollY) {
int mBuyLayout2ParentTop = Math.max(scrollY, top.getTop());
topInclude.layout(0, mBuyLayout2ParentTop, topInclude.getWidth(), mBuyLayout2ParentTop + topInclude.getHeight());
}
}
上面的布局和下面的布局重合起来,layout()这个方法是确定View的大小和位置的,然后将其绘制出来,里面的四个参数分别是View的四个点的坐标,他的坐标不是相对屏幕的原点,而且相对于他的父布局来说的。
我们在主页面最外层的ViewGroup添加了布局状态改变的监听器,当绘制完了屏幕会回调到方法onGlobalLayout()中,我们在onGlobalLayout()方法中手动调用了下onScroll()方法,刚开始mScrollView.getScrollY()等于0,所以说当scrollY小于top.getTop()的时候,上面的购买布局的上边缘到mScrollView的上边缘的距离等于top.getTop()(即下面布局的上边缘到mScrollView的上边缘)所以刚开始上面的购买布局和下面的购买布局重合了。