您的位置:首页 > 其它

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

2015-09-25 15:26 891 查看
在我们的项目开发中,用到列表控件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;

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: