一步一步做ListView滚动固定头部并且头部会变化哦

来源:互联网 发布:网络哲学百科 编辑:程序博客网 时间:2024/05/28 16:23

手机QQ好友列表中,组名在滚动的时候会固定在头部,等到这个组全部滚完后,组名也会随着向上滚动。在ListView也可以实现这么的效果。



如上所示:listview中分类显示,每一个类别有一个catalog指示,相当与QQ中的分组,我把这个catalog指示叫做header吧,当这个固定的头部滚出了屏幕外,它的item还在屏幕内,所以需要标识。这时在最上面固定一个header来标识本组,当这个组的item全部滚完后,下一个catalog出来了,这个组的固定header就要被下一个header推出去。这么看不明。。。那就展开QQ的所有分组,感受一下吧~

下面一步一步实现它。

首先,设计数据结构,本来数据只有item,但是分类之后,就要在每一个类别前面加一个catalog,这个catalog看成一个item,但是item类中最好有一个标识,如Tag指示是否为catalog,即header。

首先看整体的布局文件:

activity_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <LinearLayout        android:id="@+id/listview_wrap"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical" >        <ListView            android:id="@+id/listview"            android:layout_width="match_parent"            android:layout_height="match_parent" >        </ListView>    </LinearLayout>      <include android:id="@+id/list_header"        layout="@layout/listview_header"/></FrameLayout>


listview_header.xml    --- 这是header的布局

<?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="wrap_content"    android:background="@android:color/holo_green_light"    android:orientation="vertical" >    <TextView        android:id="@+id/float_textview"        android:layout_width="match_parent"        android:layout_height="30dp"        android:layout_centerVertical="true"        android:layout_marginLeft="10dp"        android:text="header"        android:textColor="#FFFFFF"        android:textSize="20sp" /></RelativeLayout>

list_item.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="70dp"    android:orientation="vertical"     >    <TextView        android:id="@+id/listitem_textview"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="listItem"         android:textSize="20sp"        android:layout_centerVertical="true"        android:textColor="#4d4d4d"        android:layout_marginLeft="10dp"/></LinearLayout>

好了,布局完了就开始写代码了。没时间写了,贴上整个代码大家慢慢看吧~

package org.robam.floatlistviewtest;import java.util.ArrayList;import java.util.List;import org.json.JSONArray;import org.json.JSONException;import org.json.JSONObject;import android.os.Bundle;import android.app.Activity;import android.content.Context;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.BaseAdapter;import android.widget.FrameLayout;import android.widget.ListView;import android.widget.TextView;public class MainActivity extends Activity implements OnScrollListener {//测试的数据private static final String testString = "["+ "{\"title\":\"图片报告:水价快速上涨势头不变\",\"catalog\":\"1\"},"+ "{\"title\":\"中国新规要求铁矿石进口商加入CBMX交易平台\",\"catalog\":\"1\"},"+ "{\"title\":\"早9点简报:重要经济新闻与市场概况一览\",\"catalog\":\"2\"},"+ "{\"title\":\"凤凰古城资本瓜分秘密:背后是低调亿万富豪\",\"catalog\":\"3\"},"+ "{\"title\":\"黄金大“劫”案\",\"catalog\":\"3\"},"+ "{\"title\":\"周小川:经济减速正常 需牺牲增长完成结构调整目标\",\"catalog\":\"4\"},"+ "{\"title\":\"周小川楼继伟同时表态:日本超宽松政策难自救\",\"catalog\":\"4\"},"+ "{\"title\":\"周小川楼继伟同时表态:日本超宽松政策难自救\",\"catalog\":\"4\"},"+ "{\"title\":\"20%个税成房价上升推手 北京有买家承担28万税\",\"catalog\":\"5\"},"+ "{\"title\":\"燃气公司每立方米亏1元 气价倒挂逼多地上调价格\",\"catalog\":\"6\"},"+ "{\"title\":\"4月22日国内主要财经媒体头版要闻精选\",\"catalog\":\"6\"},"+ "{\"title\":\"发改委紧急协调调运煤电油气 全力保障抗震救灾\",\"catalog\":\"7\"},"+ "{\"title\":\"·北京一季度财政收入增15.6% 房屋销售增长为主因\",\"catalog\":\"7\"},"+ "{\"title\":\"国土部:去年全国土地出让价款为2.69万亿元\",\"catalog\":\"7\"},"+ "{\"title\":\"不动产统一登记制度有望年内破冰 推进中频受阻\",\"catalog\":\"8\"},"+ "{\"title\":\"政府卖地钱一季度增一半 国五条遇土地财政怪圈\",\"catalog\":\"8\"},"+ "{\"title\":\"十大经济学家解析房价:降价误读 稳价才是真目标\",\"catalog\":\"8\"},"+ "{\"title\":\"美联储主席称美经济尚不理想 将继续刺激政策\",\"catalog\":\"8\"},"+ "{\"title\":\"美联储埃文斯:联储不应急于缩减QE\",\"catalog\":\"8\"},"+ "{\"title\":\"消息称默多克邓文迪离婚协议已接近完成\",\"catalog\":\"9\"},"+ "{\"title\":\"昨夜今晨国际市场重要财经新闻一览\",\"catalog\":\"9\"},"+ "{\"title\":\"·巴菲特称股市处于合理区域\",\"catalog\":\"10\"},"+ "{\"title\":\"美国财长称国会应该考虑债限规则改革\",\"catalog\":\"10\"},"+ "{\"title\":\"消息称英国监管当局正审查黄金基准价\",\"catalog\":\"10\"},"+ "{\"title\":\"印度推出女性银行:男人只能存钱 女人可以贷款\",\"catalog\":\"11\"},"+ "{\"title\":\"波音:787客机问题需6个月时间才能解决\",\"catalog\":\"12\"},"+ "{\"title\":\"纽约油价19日小幅反弹\",\"catalog\":\"12\"},"+ "{\"title\":\"诺基亚股东大会批准向微软出售手机业务\",\"catalog\":\"13\"},"+ "{\"title\":\"连创新高后承压回调 美股周二小幅收低\",\"catalog\":\"14\"},"+ "{\"title\":\"报告称明年全球企业仅有14%拟聘新员工\",\"catalog\":\"15\"},"+ "{\"title\":\"摩根大通正式达成130亿美元和解协议\",\"catalog\":\"15\"},"+ "{\"title\":\"本政府考虑降低必需消费品消费税率\",\"catalog\":\"15\"},"+ "{\"title\":\"油价周二涨0.3% 收于每桶93.34美元\",\"catalog\":\"15\"}" + "]";//存储item的listprivate List<ItemBeen> theList;private ListView listview;// 设置一个static,记录是否要刷新public static boolean isRefresh = false;// 浮动层,就是头部private View floatLayout = null;//浮动层是否总是显示private boolean isFloatLayoutShow = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);listview = (ListView) findViewById(R.id.listview);//获得浮动层floatLayout = findViewById(R.id.list_header);// 使用前面的测试数据,初始化list列表try {JSONArray jsonArray = new JSONArray(testString);theList = new ArrayList<ItemBeen>();int size = jsonArray.length();for (int i = 0; i < size; i++) {ItemBeen item = new ItemBeen();JSONObject jsonObject = jsonArray.getJSONObject(i);item.title = jsonObject.getString("title");item.catalog = jsonObject.getString("catalog");theList.add(item);}} catch (JSONException e) {e.printStackTrace();}// 在list中写入Tag,来标识是头部。每一个Tag也看成是list中的一个Item,用isTag=true来区分.在不同catalog前面写上一个headerList<ItemBeen> tempList = new ArrayList<ItemBeen>();String catalog = "";for (ItemBeen each : theList) {if (!each.catalog.equals(catalog)) {// 当这个Item的catalog不等于前面的,就在这个的前面加入一个TagItemBeen header = new ItemBeen();header.title = "catalog:" + each.catalog;header.catalog = each.catalog;header.isTag = true;tempList.add(header);catalog = each.catalog;}tempList.add(each);}theList = tempList;Log.d("hehe", theList.size() + "");// 以上写入标签结束// 设置Adapterlistview.setAdapter(new MyAdapter(this, theList));// 设置listview滚动监听事件listview.setOnScrollListener(this);}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}/** * 这个onScroll方法在滚动的时候会接收到很多事件,效率嘛。。。自己考虑了 * */@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {//排除了初始化的时候visibleitemcount还为o的时候,那就不处理了。如果去掉这个判断,一开始初始化就会抛出事if (visibleItemCount == 0) {return;}//注意,这个view的值的是可见部分而已,而不是整个listview。//比如:如果滚到了第十个,即可见的第一个是listview的第十个item,//那么,getchildat(0)实际上就是listview的第十项。//刚开始我试图getchildat(firstvisibleItem),还以为这是得到第一个可见的item的view,但不是//不要吧view中的位置和listview的位置搞乱了。//获取第一个可见的viewView firstView = view.getChildAt(0);//第二个可见的view。如果item只有一条。。。好像会抛异常。。。这就留给你们做吧~View secoundView = view.getChildAt(1);//当取到的view都不为空的时候才继续哦~if (firstView != null && secoundView != null && floatLayout != null) {if (firstVisibleItem == 0 && firstView.getTop() == 0) {//当第一个是tag并且top在开头,float隐藏Log.d("hehe", "float hide");floatLayout.setVisibility(View.INVISIBLE);isFloatLayoutShow = false;} else {floatLayout.setVisibility(View.VISIBLE);Log.d("hehe", "floatLayout setVisibility VISIBLE ");}if (theList.get(firstVisibleItem + 1).isTag&& firstView.getBottom() < secoundView.getHeight()) {// 如果第二个是tag,并且第一个的底部里屏幕顶距离小于tag的高度,这时float应该pushLog.d("hehe", "float push");//设置float层的margin来改变位置。看起来就像被下一个推上去的。ViewGroup.MarginLayoutParams margin = new ViewGroup.MarginLayoutParams(floatLayout.getLayoutParams());margin.setMargins(margin.leftMargin, firstView.getBottom()- secoundView.getHeight(), margin.rightMargin,firstView.getBottom());FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(margin);floatLayout.setLayoutParams(layoutParams);// 改变Tag的文字,当前Tag的文字for (int i = firstVisibleItem; i >= 0; i--) {if (theList.get(i).isTag) {TextView textView = (TextView) floatLayout.findViewById(R.id.float_textview);textView.setText(theList.get(i).title);break;}}isFloatLayoutShow = false;} else {Log.d("hehe", "float should show");//为什么要这个判断,就是为了避免反复的设置floatlayout的位置。什么情况只要设置一次?//不被推着走的时候,floatlayout总是会显示的,所以只要做一次设置会原来的位置就可以了//因为滚动的时候会产生很多onScroll,如果不需要的时候也设置floatLayout的位置,效率很低if (!isFloatLayoutShow) {ViewGroup.MarginLayoutParams margin = new ViewGroup.MarginLayoutParams(floatLayout.getLayoutParams());Log.d("hehe", floatLayout.getHeight() + "");margin.setMargins(margin.leftMargin, 0, margin.rightMargin,floatLayout.getHeight());FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(margin);floatLayout.setLayoutParams(layoutParams);// 改变Tag的文字,当前Tag的文字for (int i = firstVisibleItem; i >= 0; i--) {if (theList.get(i).isTag) {TextView textView = (TextView) floatLayout.findViewById(R.id.float_textview);textView.setText(theList.get(i).title);isFloatLayoutShow = true;break;}}Log.d("hehe", "float show ok");}}}}public class ItemBeen {public String title;public String catalog;public boolean isTag = false;}public class MyAdapter extends BaseAdapter {private List<ItemBeen> list;private LayoutInflater inflater;private Context mContext;public MyAdapter(Context context, List<ItemBeen> alist) {mContext = context;list = alist;inflater = getLayoutInflater();}@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return list.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic boolean isEnabled(int position) {if (list.get(position).isTag) {return false;}return true;}@Overridepublic int getItemViewType(int position) {// 如果是Tag,则返回1,不是的就返回0return list.get(position).isTag ? 1 : 0;}@Overridepublic int getViewTypeCount() {return 2;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {if (getItemViewType(position) == 1) {// 是Tag,则是header.HeaderViewHolder headerHolder;if (convertView == null|| convertView.findViewById(R.id.float_textview) == null) {// 如果converView是空或者converView原来不是header的view,都要从新new viewconvertView = inflater.inflate(R.layout.listview_header,null);headerHolder = new HeaderViewHolder();headerHolder.title = (TextView) convertView.findViewById(R.id.float_textview);convertView.setTag(headerHolder);} else {headerHolder = (HeaderViewHolder) convertView.getTag();}headerHolder.title.setText(list.get(position).title);} else {// 是一般的ItemItemViewHolder itemHolder;if (convertView == null|| convertView.findViewById(R.id.listitem_textview) == null) {// 如果converView是空或者converView原来不是header的view,都要从新new viewconvertView = inflater.inflate(R.layout.list_item, null);itemHolder = new ItemViewHolder();itemHolder.title = (TextView) convertView.findViewById(R.id.listitem_textview);convertView.setTag(itemHolder);} else {itemHolder = (ItemViewHolder) convertView.getTag();}itemHolder.title.setText(list.get(position).title);}return convertView;}}class HeaderViewHolder {TextView title;}class ItemViewHolder {TextView title;}}