Andorid學習筆記 :實現對ListView列表資料新增字母索引效果
阿新 • • 發佈:2019-01-31
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;
}
}
};
/* 狀態是不可見* */
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;
}
}
};