转载请注明出处: 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);
}
}
在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);
}
}
}
}
标题的移动
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;
}
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
效果图: