转载请注明出处: http://blog.csdn.net/forwardyzk/article/details/42710837
我们平时看到当滑动ListView时,标题的内容会不断的更改,并且标题会有一个推动的效果,下面与大家共享一个示例。
思路:
1.自定义ListView,给ListView绘画一个子标题(childView),将其位置设置为(0,0,width,height)
2.给ListView添加滑动监听事件。
当向下滑动时,当前第一个完全显示的item的标题内容和标题内容进行比较
如果一样,则不更改标题内容和位置,
如果不一样,则把标题的内容更改为当前显示的第一个item的标题内容,并且更改标题位置
当向上滑动时,当前第一个显示item的标题内容和标题内容进行比较
如果一样,则不更改标题内容和位置,
如果不一样,则把标题标题的内容更改为当前显示的第一个item的标题内容,并且更改标题位置
自定义一个PinnedHeaderListView集成ListView
public class PinnedHeaderListView extends ListView { /** * 更改标题的接口 * */ public interface PinnedHeaderConfig { void configurePinnedHeader(View header, int position); } private PinnedHeaderConfig mAdapter; private View mHeaderView; private boolean mHeaderViewVisible; private int mHeaderViewWidth; private int mHeaderViewHeight; public PinnedHeaderListView(Context context) { super(context); } public PinnedHeaderListView(Context context, AttributeSet attrs) { super(context, attrs); } public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setPinnedHeaderView(View view) { mHeaderView = view; if (mHeaderView != null) { setFadingEdgeLength(0); } showLog("setPinnedHeaderView"); } @Override public void setAdapter(ListAdapter adapter) { super.setAdapter(adapter); mAdapter = (PinnedHeaderConfig) adapter; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 测量长和宽 if (mHeaderView != null) { showLog("onMeasure"); measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec); mHeaderViewWidth = mHeaderView.getMeasuredWidth(); mHeaderViewHeight = mHeaderView.getMeasuredHeight(); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (mHeaderView != null) { showLog("onLayout"); mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight); moveTitle(getFirstVisiblePosition()); } } /** * 处理标题的移动 * * @param position */ public void moveTitle(int position) { if (mHeaderView == null) { return; } View firstView = getChildAt(0); int bottom = firstView.getBottom(); int headerHeight = mHeaderView.getHeight(); int y; if (bottom < headerHeight) { y = (bottom - headerHeight); } else { y = 0; } // 设置标题显示的内容 mAdapter.configurePinnedHeader(mHeaderView, position); if (mHeaderView.getTop() != y) { mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight + y); } mHeaderViewVisible = true; } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mHeaderViewVisible) { showLog("dispatchDraw"); drawChild(canvas, mHeaderView, getDrawingTime()); } } public void showLog(String message) { Log.d("MESSAGE", message); } }
setPinnedHeaderView(View view):给ListView添加一个标题。
在onMeasure()中先测量一个此标题的长和宽,
measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec);表示先访问Listview其中的一个标题,容纳后获取其长和宽。
在onLayout()中制定显示的位置,mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);其中长和宽是在onMeasure()中获取的。
然后在dispatchDraw(Canvas canvas)把其标题View绘画出来,此方法主要是绘画子组件,使用drawChild()方法进行绘制。
/** * Item信息Bean类 * */ public class ItemInfo { private String title;// 标题 private String content;// 内容 public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
在ListView的滑动监听事件中,控制标题的移动和内容的变换
@Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (view instanceof PinnedHeaderListView) { showLog("adapter-onScroll"); // 处理的是向下滑动 if (!data .get(firstVisibleItem + 1 < data.size() ? firstVisibleItem + 1 : data.size() - 1).getTitle().equals(titleContent)) { ((PinnedHeaderListView) view).moveTitle(firstVisibleItem); } else { // 处理向上滑动 if (!data.get(firstVisibleItem).getTitle().equals(titleContent)) { ((PinnedHeaderListView) view).moveTitle(firstVisibleItem); } } } }
我们要处理的是向上滑动和向下滑动两种情况,不管是向上滑动还是向下滑动,适当的时候都需要更改标题的内容和位置,在这里判断的依据,当前显示的第一个item的标题和其下一个item的标题的内容不一样的时候,需要进行标题更改标题的内容,当前显示的第一个标题的item完全不显示时,其标题内容发生了变化(更改为此时第一个Item的标题内容)。
标题的移动
public void moveTitle(int position) { if (mHeaderView == null) { return; } View firstView = getChildAt(0); int bottom = firstView.getBottom(); int headerHeight = mHeaderView.getHeight(); int y; if (bottom < headerHeight) { y = (bottom - headerHeight); } else { y = 0; } // 设置标题显示的内容 mAdapter.configurePinnedHeader(mHeaderView, position); if (mHeaderView.getTop() != y) { mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight + y); } mHeaderViewVisible = true; }
Y方向位置发生的变化值(ListView第一个Item的底部和标题View的高度差值)
mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight + y);
mAdapter.configurePinnedHeader(mHeaderView, position);设置标题的内容
public void configurePinnedHeader(View header, int position) { showLog("adapter-configurePinnedHeader"); titleContent = data.get(position).getTitle(); ((TextView) header.findViewById(R.id.header_text)).setText(String .valueOf(titleContent)); }
使用步骤:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/listView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <com.example.view.PinnedHeaderListView android:id="@+id/section_list_view" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@null" /> </LinearLayout>
MainActivity.java
public void initView() { List<ItemInfo> list = new ArrayList<ItemInfo>(); ItemInfo info; for (int i = 0; i < 50; i++) { info = new ItemInfo(); info.setTitle(String.valueOf(i / 5) + "组"); info.setContent("这是第 " + i + " Item"); list.add(info); } adapter = new ListViewAdapter(getLayoutInflater()); adapter.setData(list); listView = (PinnedHeaderListView) findViewById(R.id.section_list_view); listView.setAdapter(adapter); listView.setOnScrollListener(adapter); listView.setPinnedHeaderView(getLayoutInflater().inflate( R.layout.child_titleview_section, listView, false)); }
setPinnedHeaderView():设置标题View,和在适配器中更改标题中的TextView的id就是其View中的控件
setData()设置加载的数据,ItemInfo
setOnScrollListener()设置滚动监听,这里适配器实现额OnScrollListener接口,所以可以直接写适配器对象
源码下载:
http://download.csdn.net/detail/forwardyzk/8361563
效果图: