本文借鉴了网上一些功能实现,由于出处已无从获得了,在这里谢谢大神们的分析。
这里,我使用的是一种比较简洁干净的实现方式。
转载麻烦表明出处http://blog.csdn.net/qq_naruto/article/details/39499963
最近和朋友的一个项目中,需要用到这样一个东西,就是类似于美团网的团购详情页(如下图)的。购买一栏可以停靠在顶部,也可以跟着上面的内容往下拉。因为我也水平比较次,所以在网上寻找各种解决方案。目前大部分的实现都是自定义ScrollView的方式。这样的最大问题就是不能嵌套listView,有的时候,我们需要处理这样的结构化的数据。通过设置自定义ListView设置其高为一个很大的值也是可以解决的。但是这样处理的最大弊端就是ListView会退化成LinearLayout而失去了可以缓存View减少内存消耗的目的。而这个问题也真是我需要解决的问题。
我的 解决思路是(如下图):将ListView和要停靠的控件C放在FrameLayout中,B部分作为ListView 的 HeaderView添加到listView中。然后设置监听器,当ListView滚动时,时时调整B的位置,使B 与 headerView中的B位置保持一致。当headerView中的B要离开屏幕时,B则不变了。
这样比自定义ScrollView做各种操作的好处是,我们可以尽量少的改动已有代码而实现这功能。同时,能够容易理解。
好,扯了这么多上代码吧。
以下是java代码,注释写得很清楚。实在看不明白可以下载工程看看,链接在文后。
package com.example.meituandemo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.SimpleAdapter;
/**
* @blog http://blog.csdn.net/qq_naruto
*
* @author 艾迦号
*
*/
public class MainActivity extends Activity {
private ListView mListView;//普通的ListView
private View mTopView; //与停靠的View一致的View
private View mHeader; //ListView的头
private View mTopLayout; //停靠的View
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.scrollView);
mHeader = LayoutInflater.from(this).inflate(R.layout.header, null);
mTopView = mHeader.findViewById(R.id.buy);
mTopLayout = (LinearLayout) findViewById(R.id.top_buy_layout);
mTopLayout.setVisibility(View.INVISIBLE);//因为停靠的View最开始的时候会在顶部而与它对应的不在顶部,此时需要将停靠的View隐藏
//正常的设置ListView
List<Map<String, String>> data = new ArrayList<Map<String, String>>();
for (int i = 0; i < 20; i++) {
Map<String, String> item = new HashMap<String, String>();
item.put("data", "data" + i);
data.add(item);
}
mListView.addHeaderView(mHeader);
mListView.setAdapter(new SimpleAdapter(this, data,
android.R.layout.simple_list_item_1, new String[] { "data" },
new int[] { android.R.id.text1 }));
//设置OnScrollListener 当ListView滑动时触发其事件
mListView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
onScrolled();
}
});
}
private int last = 0;
//因为当界面进入时,第一次设置停靠View mTopLayout的位置,并不会改变,
//需要在手动触发的时候才能改变所以添加了这个变量
public void onScrolled() {
int[] location = new int[2];
mTopView.getLocationOnScreen(location);//获取控件在屏幕中的位置
int temp =location[1] - mTopView.getHeight() * 3 / 2;
//由于坐标系的缘故location[1]在父控件中的top是系统坐标系的button
//所以需要减去一个控件的高度,而实际测试法相 layout似乎设置的控件中心的位置,
//所以又减去了一个控件高度的一半
int t = temp > 0 ? temp: 0;
//当与停靠的View一致的View超出屏幕时,停靠的View停靠在顶部
mTopLayout.layout(0, t, mTopLayout.getWidth(),t + mTopLayout.getHeight());
//Activity进入时会调用三次onScroll 第一次是 0 第二三次都是同一个值,此时界面还没有滑动所以不需要显示停靠的View
if (mTopLayout.getVisibility() != View.VISIBLE && last > 0&& last != t) {
mTopLayout.setVisibility(View.VISIBLE);//显示停靠的View
}
if (last == 0) {
last = t;
}
}
}
以下是xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parent_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="match_parent"
android:layout_height="45dip"
android:scaleType="centerCrop"
android:src="@drawable/navigation_bar" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ListView
android:id="@+id/scrollView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</ListView>
<include
android:id="@+id/top_buy_layout"
layout="@layout/buy_layout" />
</FrameLayout>
</LinearLayout>
最后这是我的代码:http://download.csdn.net/detail/qq_naruto/8007643 (已换成免费积分的了)