常用的联系人、通讯录,会按照联系人的姓氏从A,B,C,,,X,Y,Z,这样归类排列下去,方便用户快速查找和定位。PinnedSectionListView是一个第三方的开源框架,在github上的链接地址是:
https://github.com/beworker/pinned-section-listview
。Android PinnedSectionListView不仅是一个实现上述功能且有“pinded”的ListView,而且PinnedSectionListView在向上滚动或者向下滚动时候会有一些特殊效果:分组的标签会悬停在ListView的顶部,直到该分组被滑出/滑入整个ListView的可视界面而呈现出弹入弹出效果。
实现上述功能,直接将PinnedSectionListView作为一个普通的ListView即可,但重点是在构造PinnedSectionListView适配器Adapter时,每一个Item需要小心设置View Type。
实现分组标签悬停滑入滑出效果,只需将PinnedSectionListView.java倒入自己的项目即可:
PinnedSectionListView.java代码如下:
/*
* Copyright (C) 2013 Sergej Shafarenka, halfbit.de
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file kt in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hb.views;
import zhangphil.contacts.BuildConfig;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.GradientDrawable.Orientation;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AbsListView;
import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SectionIndexer;
/**
* ListView, which is capable to pin section views at its top while the rest is
* still scrolled.
*/
public class PinnedSectionListView extends ListView {
// -- inner classes
/**
* List adapter to be implemented for being used with PinnedSectionListView
* adapter.
*/
public static interface PinnedSectionListAdapter extends ListAdapter {
/**
* This method shall return 'true' if views of given type has to be
* pinned.
*/
boolean isItemViewTypePinned(int viewType);
}
/** Wrapper class for pinned section view and its position in the list. */
static class PinnedSection {
public View view;
public int position;
public long id;
}
// -- class fields
// fields used for handling touch events
private final Rect mTouchRect = new Rect();
private final PointF mTouchPoint = new PointF();
private int mTouchSlop;
private View mTouchTarget;
private MotionEvent mDownEvent;
// fields used for drawing shadow under a pinned section
private GradientDrawable mShadowDrawable;
private int mSectionsDistanceY;
private int mShadowHeight;
/** Delegating listener, can be null. */
OnScrollListener mDelegateOnScrollListener;
/** Shadow for being recycled, can be null. */
PinnedSection mRecycleSection;
/** shadow instance with a pinned view, can be null. */
PinnedSection mPinnedSection;
/**
* Pinned view Y-translation. We use it to stick pinned view to the next
* section.
*/
int mTranslateY;
/** Scroll listener which does the magic */
private final OnScrollListener mOnScrollListener = new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScrollStateChanged(view,
scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (mDelegateOnScrollListener != null) { // delegate
mDelegateOnScrollListener.onScroll(view, firstVisibleItem,
visibleItemCount, totalItemCount);
}
// get expected adapter or fail fast
ListAdapter adapter = getAdapter();
if (adapter == null || visibleItemCount == 0)
return; // nothing to do
final boolean isFirstVisibleItemSection = isItemViewTypePinned(
adapter, adapter.getItemViewType(firstVisibleItem));
if (isFirstVisibleItemSection) {
View sectionView = getChildAt(0);
if (sectionView.getTop() == getPaddingTop()) { // view stic