Andorid学习笔记 :实现对ListView列表数据添加字母索引效果

    在我们的项目开发中,用到列表控件ListView的机率通过是100%,不过有时候我们在用ListView展示数据的时候,还需要做点美化,比如为了方便查找列表中的数据,我们会给列表数据加个索引功能,就是在屏幕的右边有一个条形的悬浮框显示在Listview上,最典型的例子就是诸如小米手机的联系人列表,右侧有一个#ABC......的索引条,还用很多应用的城市数据列表等,都有这个功能。

小米手机联系人如图:,今天在工作之余,学习了下这个功能的实现,同样是方便以后使用,做了个学习笔记,感谢慕课网老师的无私奉献,个人觉得这个网站的学习视屏越不错。

下面是实现代码:

-------------------主界面----------------------------------------

public class MainActivity extends Activity {
private IndexableListView mListView;
private List<String> mItems;
private ContentAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initDatas();
}
private void initDatas() {
mItems=new ArrayList<String>();//随便构造了一些数据
mItems.add("885855");
mItems.add("A Anheritance (The Inheritance Cycle)");
mItems.add("B BAnheritancefsf");
mItems.add("C cGDAFSFSFsfsf");
mItems.add("D AGDAFSFSFsfsf");
mItems.add("E AGDAFSFSFsfsf");
mItems.add("F AGDAFSFSFsfsf");
mItems.add("G Inheritance (The Inheritance Cycle)");
mItems.add("H Inheritance");
mItems.add("I AGDAFSFSFsfsf");
mItems.add("J Inheritance");
mItems.add("K AGDAFSFSFsfsf");
mItems.add("L Inheritance");
mItems.add("M AGDAFSFSFsfsf");
mItems.add("N AGDAFSFSFsfsf");
mItems.add("O Inheritance (The Inheritance Cycle)");
mItems.add("P AGCycleFSFSFsfsf");
mItems.add("Q CycleDAFSFSFsfsf");
mItems.add("R AGDAFSFSFsfsf");
mItems.add("S AGDCycleSFSFsfsf");
mItems.add("T AGDAFSCyclefsf");
mItems.add("U AGDCycleSFsfsf");
mItems.add("V AGDAFSFSFsfsf");
mItems.add("W AGDAFSFSFsfsf");
mItems.add("X AGCycleFSFSFsfsf");
mItems.add("Y ACycleAFSFSFsfsf");
mItems.add("Z AGDAFSFSFsfsf");
Collections.sort(mItems);//对数据进行排序
mAdapter=new ContentAdapter(this, android.R.layout.simple_list_item_1,mItems);
mListView=(IndexableListView) findViewById(R.id.indexListview);
mListView.setAdapter(mAdapter);
mListView.setFastScrollEnabled(true);
}
}

----------------布局文件,就一个自定义的ListView----------------------

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >


    <com.ldm.letterindex.IndexableListView
        android:id="@+id/indexListview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


</RelativeLayout>

------------自定义的ListView----------------------------------------------

public class IndexableListView extends ListView {
private boolean mIsFastScrollEnable = false;
/* 负责绘制右侧索引* */
private IndexScroller mScroller = null;
private GestureDetector mGestureDetector = null;


public IndexableListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}


public IndexableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}


public IndexableListView(Context context) {
super(context);
}


public boolean isFastScrollEnable() {
return mIsFastScrollEnable;
}


@Override
public void setFastScrollEnabled(boolean enabled) {
mIsFastScrollEnable = enabled;
/* 如果允许FastScroll* */
if (mIsFastScrollEnable) {
if (mScroller == null) {
mScroller = new IndexScroller(getContext(), this);
}
}
else {
if (mScroller != null) {
mScroller.hide();
mScroller = null;
}
}
}


/* 在这个方法中绘制右侧的索引条* */
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mScroller != null) {
mScroller.draw(canvas);
}
}


@Override
public boolean onTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
if (mScroller != null && mScroller.onTouchEvent(ev)) { return true; }
if (mGestureDetector == null) {
mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
mScroller.show();
return super.onFling(e1, e2, velocityX, velocityY);
}
});
}
mGestureDetector.onTouchEvent(ev);
return super.onTouchEvent(ev);


}


@Override
public void setAdapter(ListAdapter adapter) {
// TODO Auto-generated method stub
super.setAdapter(adapter);
if (mScroller != null) {
mScroller.setAdapter(adapter);
}
}


@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO Auto-generated method stub
super.onSizeChanged(w, h, oldw, oldh);
if (mScroller != null) {
mScroller.onSizeChanged(w, h, oldw, oldh);
}
}
}

-----------------实现索引条功能代码------------------------------

public class IndexScroller {
/* 状态是不可见* */
private static final int STATE_HIDDEN = 0;
/* 状态是逐渐可见* */
private static final int STATE_SHOWING = 1;
/* 状态是可见* */
private static final int STATE_SHOWN = 2;
private static final int STATE_HIDING = 3;
/* 索引条宽度* */
private float mIndexbarWidth;
/* 索引条距离右侧的距离* */
private float mIndexbarMargin;
/* 浮动在屏幕中间的文字内间距* */
private float mPreviewPadding;
/* 当前屏幕密度dp* */
private float mDensity;
/* 当前屏幕密度(与文字相关sp)* */
private float mScaledDensity;
/* 设置透明度* */
private float mAlphaRate;
/* 索引条当前状态* */
private int mState = STATE_HIDDEN;
/* ListView宽度* */
private int mListViewWidth;
/* ListView高度* */
private int mListViewHeight;
/* 索引的Section位置* */
private int mCurrentSection = -1;
private boolean mIsIndexing = false;
/* ListView控件* */
private ListView mListView = null;
/* SectionIndexer对象* */
private SectionIndexer mIndexer = null;
/* 索引条文字数组* */
private String[] mSections = null;
/* 索引条RectF* */
private RectF mIndexbarRectF = null;


public IndexScroller(Context context, ListView lv) {
mDensity = context.getResources().getDisplayMetrics().density;
mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
mListView = lv;
setAdapter(mListView.getAdapter());
/* 索引条宽度,基准为20dp* */
mIndexbarWidth = 20 * mDensity;
/* 索引条间距,基准10dp* */
mIndexbarMargin = 10 * mDensity;
/* 文字预览内间距,基准5dp* */
mPreviewPadding = 5 * mDensity;
}


public void draw(Canvas canvas) {
if (mState == STATE_HIDDEN) return;


/* 设置索引条Paint* */
Paint indexbarPaint = new Paint();
indexbarPaint.setColor(Color.BLACK);
indexbarPaint.setAlpha((int) (66 * mAlphaRate));
indexbarPaint.setAntiAlias(true);
/* 画右侧字母索引的圆矩形* */
canvas.drawRoundRect(mIndexbarRectF, 5 * mDensity, 5 * mDensity, indexbarPaint);
/* 对mSections进行判断* */
if (mSections != null && mSections.length > 0) {
/* 当前选择索引条上文字时,绘制位于屏幕中间的悬浮预览框* */
if (mCurrentSection >= 0) {
/* 首先绘制背景框* */
Paint previewPaint = new Paint();
previewPaint.setColor(Color.BLACK);
previewPaint.setAlpha(96);
previewPaint.setAntiAlias(true);
previewPaint.setShadowLayer(3, 0, 0, Color.argb(64, 0, 0, 0));
/* 绘制索引条上字母画笔* */
Paint previewTextPaint = new Paint();
previewTextPaint.setColor(Color.WHITE);
previewTextPaint.setAntiAlias(true);
/* 设置文本大小,基准为50sp* */
previewTextPaint.setTextSize(50 * mScaledDensity);
/* 测量文本宽度* */
float previewTextWidth = previewTextPaint.measureText(mSections[mCurrentSection]);
float previewSize = 2 * mPreviewPadding + previewTextPaint.descent() - previewTextPaint.ascent();
RectF previewRect = new RectF((mListViewWidth - previewSize) / 2, (mListViewHeight - previewSize) / 2, (mListViewWidth - previewSize) / 2 + previewSize, (mListViewHeight - previewSize) / 2 + previewSize);
/* 绘制中间悬浮框* */
canvas.drawRoundRect(previewRect, 5 * mDensity, 5 * mDensity, previewPaint);
/* 悬浮框中字母* */
canvas.drawText(mSections[mCurrentSection], previewRect.left + (previewSize - previewTextWidth) / 2 - 1, previewRect.top + mPreviewPadding - previewTextPaint.ascent() + 1, previewTextPaint);
}


/* 右侧索引条的字母* */
Paint indexPaint = new Paint();
indexPaint.setColor(Color.WHITE);
indexPaint.setAlpha((int) (255 * mAlphaRate));
indexPaint.setAntiAlias(true);
indexPaint.setTextSize(12 * mScaledDensity);


float sectionHeight = (mIndexbarRectF.height() - 2 * mIndexbarMargin) / mSections.length;
float paddingTop = (sectionHeight - (indexPaint.descent() - indexPaint.ascent())) / 2;
for (int i = 0; i < mSections.length; i++) {
float paddingLeft = (mIndexbarWidth - indexPaint.measureText(mSections[i])) / 2;
canvas.drawText(mSections[i], mIndexbarRectF.left + paddingLeft, mIndexbarRectF.top + mIndexbarMargin + sectionHeight * i + paddingTop - indexPaint.ascent(), indexPaint);
}
}
}


public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mState != STATE_HIDDEN && contains(ev.getX(), ev.getY())) {
setState(STATE_SHOWN);
/* 开始索引* */
mIsIndexing = true;
mCurrentSection = getSectionByPoint(ev.getY());
mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));
return true;
}
break;
case MotionEvent.ACTION_MOVE: // 移动
if (mIsIndexing) {
/* 判断当前移动范围* */
if (contains(ev.getX(), ev.getY())) {
/* 设置位置* */
mCurrentSection = getSectionByPoint(ev.getY());
mListView.setSelection(mIndexer.getPositionForSection(mCurrentSection));
}
return true;
}
break;
case MotionEvent.ACTION_UP:
if (mIsIndexing) {
mIsIndexing = false;
mCurrentSection = -1;
}
if (mState == STATE_SHOWN) setState(STATE_HIDING);
break;
}
return false;
}


public void onSizeChanged(int w, int h, int oldw, int oldh) {
mListViewWidth = w;
mListViewHeight = h;
mIndexbarRectF = new RectF(w - mIndexbarMargin - mIndexbarWidth, mIndexbarMargin, w - mIndexbarMargin, h - mIndexbarMargin);
}


/* 设置可见* */
public void show() {
if (mState == STATE_HIDDEN) setState(STATE_SHOWING);
else if (mState == STATE_HIDING) setState(STATE_HIDING);
}


/* 隐藏indexslip* */
public void hide() {
if (mState == STATE_SHOWN) setState(STATE_HIDING);
}


/**
* 设置数据
* @description:
* @date 2015-9-25 下午2:58:28
*/
public void setAdapter(Adapter adapter) {
if (adapter instanceof SectionIndexer) {
mIndexer = (SectionIndexer) adapter;
mSections = (String[]) mIndexer.getSections();
}
}


/**
* 设置索引状态
* @description:
* @date 2015-9-25 下午2:53:17
*/
private void setState(int state) {
if (state < STATE_HIDDEN || state > STATE_HIDING) return;


mState = state;
switch (mState) {
case STATE_HIDDEN:
/* 取消时渐变的效果* */
mHandler.removeMessages(0);
break;
case STATE_SHOWING:
/* 开始显示时渐进效果* */
mAlphaRate = 0;
fade(0);
break;
case STATE_SHOWN:
/* 取消渐退的效果* */
mHandler.removeMessages(0);
break;
case STATE_HIDING:
/* 隐藏3秒钟* */
mAlphaRate = 1;
fade(3000);
break;
}
}


private boolean contains(float x, float y) {
return (x >= mIndexbarRectF.left && y >= mIndexbarRectF.top && y <= mIndexbarRectF.top + mIndexbarRectF.height());
}


private int getSectionByPoint(float y) {
if (mSections == null || mSections.length == 0) return 0;
if (y < mIndexbarRectF.top + mIndexbarMargin) return 0;
if (y >= mIndexbarRectF.top + mIndexbarRectF.height() - mIndexbarMargin) return mSections.length - 1;
return (int) ((y - mIndexbarRectF.top - mIndexbarMargin) / ((mIndexbarRectF.height() - 2 * mIndexbarMargin) / mSections.length));
}


private void fade(long delay) {
mHandler.removeMessages(0);
mHandler.sendEmptyMessageAtTime(0, SystemClock.uptimeMillis() + delay);
}

private Handler mHandler = new Handler() {

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);


switch (mState) {
case STATE_SHOWING:
/* 淡进效果* */
mAlphaRate += (1 - mAlphaRate) * 0.2;
if (mAlphaRate > 0.9) {
mAlphaRate = 1;
setState(STATE_SHOWN);
}


mListView.invalidate();
fade(10);
break;
case STATE_SHOWN:
/* 如果在显示中,但是没有任何操作,也要设置变为不可见* */
setState(STATE_HIDING);
break;
case STATE_HIDING:
/* 淡出效果* */
mAlphaRate -= mAlphaRate * 0.2;
if (mAlphaRate < 0.1) {
mAlphaRate = 0;
setState(STATE_HIDDEN);
}

mListView.invalidate();
fade(10);
break;
}
}

};

---------------------主界面ListView的数据适配----------------------------------

class ContentAdapter extends ArrayAdapter<String> implements SectionIndexer {

public ContentAdapter(Context context, int resource, List<String> objects) {
super(context, resource, objects);
// TODO Auto-generated constructor stub
}


private String mSections = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";


@Override
public int getPositionForSection(int sectionIndex) {
// 从当前的SectionIndex往前查,直到查到每一个有对应item的为止,否则不进行定位
for (int i = sectionIndex; i >= 0; i--) {
for (int j = 0; j < getCount(); j++) {
if (i == 0) {// 查询数字
for (int k = 0; k < 9; k++) {// StringMatcher.match中的value为item中的首字母
if (StringMatcher.match(String.valueOf(getItem(j).charAt(0)), String.valueOf(k))) { return j; }
}


}
else {// 查询字母
if (StringMatcher.match(String.valueOf(getItem(j).charAt(0)), String.valueOf(mSections.charAt(i)))) { return j; }


}
}
}
return 0;
}


@Override
public int getSectionForPosition(int position) {
// TODO Auto-generated method stub
return 0;
}

@Override
public Object[] getSections() {
String[] selections = new String[mSections.length()];
// 将mSections中每一个字符放到selections中
for (int i = 0; i < selections.length; i++) {
selections[i] = String.valueOf(mSections.charAt(i));
}
return selections;
}
}

-----------------------工具类----------------------------

public class StringMatcher {
public static boolean match(String value, String keyword) {// value指item文本,keyword指索引中的字符
if (TextUtils.isEmpty(value) || TextUtils.isEmpty(keyword)) {// 两者都不能为空
return false;
}
if (value.length() < keyword.length()) {// item中的文字长度不能小于索引列表中的字符长度
return false;
}
int i = 0, j = 0;// i是对应value,j是对应keyword
do {
if (value.charAt(i) == keyword.charAt(j)) {
i++;
j++;
}
else if (j > 0) {
break;
}
else {
i++;
}
}
while (i < value.length() && j < keyword.length());
return (j == keyword.length()) ? true : false;
}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值