首先给大家上效果图
涉及的技术包括以下三点
绘制右侧的条目
点击某个字母定位到当前listview的position Android SearchView实现
1、涉及到的类:SectionIndexer
方法介绍
-
object[] getSections();
获取一个数组,存的是索引的列表,其是将每一个数组单独作为一个数组元素存储的。
-
int getPositionForSection(int sectionIndex);
根据右侧的索引字母获得position(ListView的每个条目位置)。
-
int getSectionForPosition(int postion);
2、实现步骤和方法
IndexableListViewActivity类
package com.android.xiong.gridlayoutTest;
import java.util.ArrayList;
import java.util.Collections;
import com.android.xiong.gridlayoutTest.customeview.IndexableListView;
import com.android.xiong.gridlayoutTest.util.StringMatcher;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.ArrayAdapter;
import android.widget.SearchView;
import android.widget.SectionIndexer;
import android.widget.Toast;
import android.widget.SearchView.OnQueryTextListener;
public class IndexableListViewActivity extends Activity {
/**
* 1初始化item 根据selection获取position
*/
private SearchView sv;
private ArrayList<String> mItem;
private IndexableListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_index);
sv = (SearchView) findViewById(R.id.sv);
mListView = (IndexableListView) findViewById(R.id.listview);
mListView.setTextFilterEnabled(true);
// 设置文本过滤
sv.setIconifiedByDefault(false);
// 设置默认是否为缩小图标
sv.setSubmitButtonEnabled(true);
// 显示搜索按钮
sv.setQueryHint("查找");
sv.setOnQueryTextListener(new OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
Toast.makeText(IndexableListViewActivity.this,
"你选择的是:" + query.toString(), Toast.LENGTH_SHORT).show();
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
// 如果query不为零的字符串
if (TextUtils.isEmpty(newText)) {
// 清除listview的过滤
mListView.clearTextFilter();
} else {
// 使用listview输入文本进行过滤
mListView.setFilterText(newText);
}
return true;
}
});
//添加item条目
mItem = new ArrayList<String>();
mItem.add("1234567");
mItem.add("A ASF1234567");
mItem.add("B DASD1234567");
mItem.add("C ipui1234567");
mItem.add("D qwwe1234567");
mItem.add("E ryhr1234567");
mItem.add("F asfdf1234567");
mItem.add("G hk,jmj1234567");
mItem.add("H aasff1234567");
mItem.add("L fhn1234567");
mItem.add("J asdfg1234567");
mItem.add("K asf1234567");
mItem.add("L sfaa1234567");
mItem.add("M uoou1234567");
mItem.add("N hkhk1234567");
mItem.add("O sfg1234567");
mItem.add("P ree1234567");
mItem.add("Q ere123erer4567");
mItem.add("R 123ere4567");
mItem.add("S 123er4efer567");
mItem.add("T 1234567");
mItem.add("U 123b4567");
mItem.add("V 1234567");
mItem.add("W 1ASS234567");
mItem.add("X 1234567");
mItem.add("Y 1234567");
mItem.add("Z 1234567");
// 排序
Collections.sort(mItem);
ContentAdapter adapter = new ContentAdapter(this,
android.R.layout.simple_list_item_1, mItem);
mListView.setAdapter(adapter);
//设置滚动事件
mListView.setFastScrollEnabled(true);
// mListView.set
}
private class ContentAdapter extends ArrayAdapter<String> implements
SectionIndexer {
private String mSections = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public ContentAdapter(Context context, int resource,
ArrayList<String> mItem) {
super(context, resource, mItem);
// TODO Auto-generated constructor stub
}
@Override
public Object[] getSections() {
String[] sections = new String[mSections.length()];
// 将每个Section作为单元数组放在sections中
for (int i = 0; i < sections.length; i++) {
sections[i] = String.valueOf(mSections.charAt(i));
}
return sections;
}
@Override
public int getPositionForSection(int sectionIndex) {
// Section一直往前查询,直到第一个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++) {
// value item的首字母
if (StringMatcher.match(String.valueOf(getItem(j)),
String.valueOf(k))) {
// 返回当前索引
return j;
}
}
} else {
if (StringMatcher.match(String.valueOf(getItem(j)),
String.valueOf(mSections.charAt(i)))) {
}
}
}
}
return 0;
}
@Override
public int getSectionForPosition(int position) {
// TODO Auto-generated method stub
return 0;
}
}
}
import java.util.ArrayList;
import java.util.Collections;
import com.android.xiong.gridlayoutTest.customeview.IndexableListView;
import com.android.xiong.gridlayoutTest.util.StringMatcher;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.ArrayAdapter;
import android.widget.SearchView;
import android.widget.SectionIndexer;
import android.widget.Toast;
import android.widget.SearchView.OnQueryTextListener;
public class IndexableListViewActivity extends Activity {
/**
* 1初始化item 根据selection获取position
*/
private SearchView sv;
private ArrayList<String> mItem;
private IndexableListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_index);
sv = (SearchView) findViewById(R.id.sv);
mListView = (IndexableListView) findViewById(R.id.listview);
mListView.setTextFilterEnabled(true);
// 设置文本过滤
sv.setIconifiedByDefault(false);
// 设置默认是否为缩小图标
sv.setSubmitButtonEnabled(true);
// 显示搜索按钮
sv.setQueryHint("查找");
sv.setOnQueryTextListener(new OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
Toast.makeText(IndexableListViewActivity.this,
"你选择的是:" + query.toString(), Toast.LENGTH_SHORT).show();
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
// 如果query不为零的字符串
if (TextUtils.isEmpty(newText)) {
// 清除listview的过滤
mListView.clearTextFilter();
} else {
// 使用listview输入文本进行过滤
mListView.setFilterText(newText);
}
return true;
}
});
//添加item条目
mItem = new ArrayList<String>();
mItem.add("1234567");
mItem.add("A ASF1234567");
mItem.add("B DASD1234567");
mItem.add("C ipui1234567");
mItem.add("D qwwe1234567");
mItem.add("E ryhr1234567");
mItem.add("F asfdf1234567");
mItem.add("G hk,jmj1234567");
mItem.add("H aasff1234567");
mItem.add("L fhn1234567");
mItem.add("J asdfg1234567");
mItem.add("K asf1234567");
mItem.add("L sfaa1234567");
mItem.add("M uoou1234567");
mItem.add("N hkhk1234567");
mItem.add("O sfg1234567");
mItem.add("P ree1234567");
mItem.add("Q ere123erer4567");
mItem.add("R 123ere4567");
mItem.add("S 123er4efer567");
mItem.add("T 1234567");
mItem.add("U 123b4567");
mItem.add("V 1234567");
mItem.add("W 1ASS234567");
mItem.add("X 1234567");
mItem.add("Y 1234567");
mItem.add("Z 1234567");
// 排序
Collections.sort(mItem);
ContentAdapter adapter = new ContentAdapter(this,
android.R.layout.simple_list_item_1, mItem);
mListView.setAdapter(adapter);
//设置滚动事件
mListView.setFastScrollEnabled(true);
// mListView.set
}
private class ContentAdapter extends ArrayAdapter<String> implements
SectionIndexer {
private String mSections = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public ContentAdapter(Context context, int resource,
ArrayList<String> mItem) {
super(context, resource, mItem);
// TODO Auto-generated constructor stub
}
@Override
public Object[] getSections() {
String[] sections = new String[mSections.length()];
// 将每个Section作为单元数组放在sections中
for (int i = 0; i < sections.length; i++) {
sections[i] = String.valueOf(mSections.charAt(i));
}
return sections;
}
@Override
public int getPositionForSection(int sectionIndex) {
// Section一直往前查询,直到第一个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++) {
// value item的首字母
if (StringMatcher.match(String.valueOf(getItem(j)),
String.valueOf(k))) {
// 返回当前索引
return j;
}
}
} else {
if (StringMatcher.match(String.valueOf(getItem(j)),
String.valueOf(mSections.charAt(i)))) {
}
}
}
}
return 0;
}
@Override
public int getSectionForPosition(int position) {
// TODO Auto-generated method stub
return 0;
}
}
}
3、创建StringMatcher类
package com.android.xiong.gridlayoutTest.util;
public class StringMatcher {
/**
* @param value item条目中的文字
* @param keyword 索引列表的字符
* @return 这两个是否进行匹配
*/
public static boolean match(String value, String keyword) {
if (value == null || keyword == null) {
return false;
}
if (keyword.length() > value.length()) {
return false;
}
int i = 0; //value的指针
int j = 0; //keyword的指针
do {
if (keyword.charAt(j) == value.charAt(i)) {
i++;
j++;
} else if (j > 0) {
//
break;
} else {
i++;
}
} while (i < value.length() && j < keyword.length());
return (j == keyword.length()?true:false);
}
}
public class StringMatcher {
/**
* @param value item条目中的文字
* @param keyword 索引列表的字符
* @return 这两个是否进行匹配
*/
public static boolean match(String value, String keyword) {
if (value == null || keyword == null) {
return false;
}
if (keyword.length() > value.length()) {
return false;
}
int i = 0; //value的指针
int j = 0; //keyword的指针
do {
if (keyword.charAt(j) == value.charAt(i)) {
i++;
j++;
} else if (j > 0) {
//
break;
} else {
i++;
}
} while (i < value.length() && j < keyword.length());
return (j == keyword.length()?true:false);
}
}
4、创建IndexScroller类
package com.android.xiong.gridlayoutTest.util;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.widget.Adapter;
import android.widget.ListView;
import android.widget.SectionIndexer;
public class IndexScroller {
private float mIndexbarWidth; //索引条的宽度(索引条是直到显示完为止,所以不需要高度)
private float mIndexbarMargin; //索引条距离右侧边缘的距离
private float mPreviewPadding; //在中心显示的预览文本到四周的距离
private float mDensity; //320/160 = 2 ,这代表这个2,指的当前屏幕密度/160
private float mScaledDensity; //指的当前屏幕密度/160(设置字体的尺寸)
private float mAlphaRate; //透明度(用于显示和隐藏索引条)0~1
private int mState = STATE_HIDDEN;//索引条的状态
private int mListViewWidth;
private int mListViewHeight;
private int mCurrentSection = -1;//
private boolean mIsIndexing = false;
private ListView mListView = null;
private SectionIndexer mIndexer = null;
private String[] mSections = null;//索引条的文本
private RectF mIndexbarRect; //表示整个索引条的区域
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;
public IndexScroller(Context context, ListView listview) {
//获取屏幕密度的比值
mDensity = context.getResources().getDisplayMetrics().density;
mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
mListView = listview;
setAdapter(mListView.getAdapter());
//根据屏幕密度计算索引条的高度(单位:像素)
mIndexbarWidth = 20 * mDensity;
mIndexbarMargin = 10 * mDensity;
mPreviewPadding = 5 * mDensity;
}
public void setAdapter(Adapter adapter) {
if (adapter instanceof SectionIndexer) {
mIndexer = (SectionIndexer) adapter;
mSections = (String[]) mIndexer.getSections();
}
}
public void draw(Canvas canvas) {
// 1、绘制索引条、包括索引条的背景和文本
// 2、绘制预览文本和背景
// 如果索引条是隐藏状态,则不进行绘制
if (mState == STATE_HIDDEN) {
return;
}
// 设置索引条背景的绘制属性
Paint indexbarPaint = new Paint();
indexbarPaint.setColor(Color.BLACK);
indexbarPaint.setAlpha((int) (64 * mAlphaRate));
//绘制索引条(4个角都是圆角的矩形区域)
canvas.drawRoundRect(mIndexbarRect, 5 * mDensity, 5 * mDensity, indexbarPaint);
//绘制Section
if (mSections != null && mSections.length > 0) {
//绘制预览文本和背景
if (mCurrentSection >= 0) {
//预览的背景
Paint previewPaint = new Paint();
previewPaint.setColor(Color.BLACK);
previewPaint.setAlpha(96);
//预览的文本
Paint previewTextPaint = new Paint();
previewTextPaint.setColor(Color.WHITE);
previewTextPaint.setTextSize(50 * mScaledDensity);
//测量文本的宽度
float previewTextWidth = previewTextPaint.measureText(mSections[mCurrentSection]);
//预览文本高度=文本上边距+文本下边距+文字高度
//TODO:注1
float previewSize = 2 * mPreviewPadding + previewTextPaint.descent() - previewTextPaint.ascent();
//预览文本背景区域 (预览背景左侧到listview左侧的距离)
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();
indexbarPaint.setColor(Color.WHITE);
indexbarPaint.setAlpha((int) (255 * mAlphaRate));
indexbarPaint.setTextSize(12 * mScaledDensity);
float sectionHeight = (mIndexbarRect.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], mIndexbarRect.left + paddingLeft, mIndexbarRect.top + mIndexbarMargin + sectionHeight * i + paddingTop - indexPaint.ascent(), indexPaint);
}
}
public void onSizeChanged(int w, int h, int oldw, int oldh) {
mListViewWidth = w;
mListViewHeight = h;
mIndexbarRect = new RectF(w - mIndexbarMargin - mIndexbarWidth, mIndexbarMargin, w - mIndexbarMargin, h - mIndexbarMargin);
}
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 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:
mAlphaRate = 1;
fade(3000);
break;
}
}
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;
//通过触摸点获取当前的section的索引
mCurrentSection = getSectionByPoint(ev.getY());
//将listview定位到指定的item
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 boolean contains(float x, float y) {
// Determine if the point is in index bar region, which includes the right margin of the bar
return (x >= mIndexbarRect.left && y >= mIndexbarRect.top && y <= mIndexbarRect.top + mIndexbarRect.height());
}
private int getSectionByPoint(float y) {
if (mSections == null || mSections.length == 0)
return 0;
if (y < mIndexbarRect.top + mIndexbarMargin)
return 0;
if (y >= mIndexbarRect.top + mIndexbarRect.height() - mIndexbarMargin)
return mSections.length - 1;
return (int) ((y - mIndexbarRect.top - mIndexbarMargin) / ((mIndexbarRect.height() - 2 * mIndexbarMargin) / mSections.length));
}
//索引条目的显示
public void show() {
if (mState == STATE_HIDDEN)
setState(STATE_SHOWING);
else if (mState == STATE_HIDING)
setState(STATE_HIDING);
}
//索引条目的隐藏
public void hide() {
if (mState == STATE_SHOWN)
setState(STATE_HIDING);
}
}
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.view.MotionEvent;
import android.widget.Adapter;
import android.widget.ListView;
import android.widget.SectionIndexer;
public class IndexScroller {
private float mIndexbarWidth; //索引条的宽度(索引条是直到显示完为止,所以不需要高度)
private float mIndexbarMargin; //索引条距离右侧边缘的距离
private float mPreviewPadding; //在中心显示的预览文本到四周的距离
private float mDensity; //320/160 = 2 ,这代表这个2,指的当前屏幕密度/160
private float mScaledDensity; //指的当前屏幕密度/160(设置字体的尺寸)
private float mAlphaRate; //透明度(用于显示和隐藏索引条)0~1
private int mState = STATE_HIDDEN;//索引条的状态
private int mListViewWidth;
private int mListViewHeight;
private int mCurrentSection = -1;//
private boolean mIsIndexing = false;
private ListView mListView = null;
private SectionIndexer mIndexer = null;
private String[] mSections = null;//索引条的文本
private RectF mIndexbarRect; //表示整个索引条的区域
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;
public IndexScroller(Context context, ListView listview) {
//获取屏幕密度的比值
mDensity = context.getResources().getDisplayMetrics().density;
mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
mListView = listview;
setAdapter(mListView.getAdapter());
//根据屏幕密度计算索引条的高度(单位:像素)
mIndexbarWidth = 20 * mDensity;
mIndexbarMargin = 10 * mDensity;
mPreviewPadding = 5 * mDensity;
}
public void setAdapter(Adapter adapter) {
if (adapter instanceof SectionIndexer) {
mIndexer = (SectionIndexer) adapter;
mSections = (String[]) mIndexer.getSections();
}
}
public void draw(Canvas canvas) {
// 1、绘制索引条、包括索引条的背景和文本
// 2、绘制预览文本和背景
// 如果索引条是隐藏状态,则不进行绘制
if (mState == STATE_HIDDEN) {
return;
}
// 设置索引条背景的绘制属性
Paint indexbarPaint = new Paint();
indexbarPaint.setColor(Color.BLACK);
indexbarPaint.setAlpha((int) (64 * mAlphaRate));
//绘制索引条(4个角都是圆角的矩形区域)
canvas.drawRoundRect(mIndexbarRect, 5 * mDensity, 5 * mDensity, indexbarPaint);
//绘制Section
if (mSections != null && mSections.length > 0) {
//绘制预览文本和背景
if (mCurrentSection >= 0) {
//预览的背景
Paint previewPaint = new Paint();
previewPaint.setColor(Color.BLACK);
previewPaint.setAlpha(96);
//预览的文本
Paint previewTextPaint = new Paint();
previewTextPaint.setColor(Color.WHITE);
previewTextPaint.setTextSize(50 * mScaledDensity);
//测量文本的宽度
float previewTextWidth = previewTextPaint.measureText(mSections[mCurrentSection]);
//预览文本高度=文本上边距+文本下边距+文字高度
//TODO:注1
float previewSize = 2 * mPreviewPadding + previewTextPaint.descent() - previewTextPaint.ascent();
//预览文本背景区域 (预览背景左侧到listview左侧的距离)
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();
indexbarPaint.setColor(Color.WHITE);
indexbarPaint.setAlpha((int) (255 * mAlphaRate));
indexbarPaint.setTextSize(12 * mScaledDensity);
float sectionHeight = (mIndexbarRect.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], mIndexbarRect.left + paddingLeft, mIndexbarRect.top + mIndexbarMargin + sectionHeight * i + paddingTop - indexPaint.ascent(), indexPaint);
}
}
public void onSizeChanged(int w, int h, int oldw, int oldh) {
mListViewWidth = w;
mListViewHeight = h;
mIndexbarRect = new RectF(w - mIndexbarMargin - mIndexbarWidth, mIndexbarMargin, w - mIndexbarMargin, h - mIndexbarMargin);
}
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 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:
mAlphaRate = 1;
fade(3000);
break;
}
}
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;
//通过触摸点获取当前的section的索引
mCurrentSection = getSectionByPoint(ev.getY());
//将listview定位到指定的item
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 boolean contains(float x, float y) {
// Determine if the point is in index bar region, which includes the right margin of the bar
return (x >= mIndexbarRect.left && y >= mIndexbarRect.top && y <= mIndexbarRect.top + mIndexbarRect.height());
}
private int getSectionByPoint(float y) {
if (mSections == null || mSections.length == 0)
return 0;
if (y < mIndexbarRect.top + mIndexbarMargin)
return 0;
if (y >= mIndexbarRect.top + mIndexbarRect.height() - mIndexbarMargin)
return mSections.length - 1;
return (int) ((y - mIndexbarRect.top - mIndexbarMargin) / ((mIndexbarRect.height() - 2 * mIndexbarMargin) / mSections.length));
}
//索引条目的显示
public void show() {
if (mState == STATE_HIDDEN)
setState(STATE_SHOWING);
else if (mState == STATE_HIDING)
setState(STATE_HIDING);
}
//索引条目的隐藏
public void hide() {
if (mState == STATE_SHOWN)
setState(STATE_HIDING);
}
}
5、最后一步IndexableList集成ListView
package com.android.xiong.gridlayoutTest.customeview;
import com.android.xiong.gridlayoutTest.util.IndexScroller;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.ListAdapter;
import android.widget.ListView;
public class IndexableListView extends ListView {
private boolean mIsFastScrollEnable = false;
//负责绘制索引条
private IndexScroller mScroller = null;
private GestureDetector mGestureDetector = null;//手势检测器
public IndexableListView(Context context) {
super(context);
}
public IndexableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean isFastScrollEnabled() {
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); //绘制listview原本的item
if (mScroller != null) {
//绘制右侧的索引条
mScroller.draw(canvas);
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
//如果mScroller自己来处理触摸事件,该方法返回true。
//如果用户手指点到了右侧索引条,就由它自己来处理
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) {
super.setAdapter(adapter);
if (mScroller != null) {
mScroller.setAdapter(adapter);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mScroller != null) {
//当屏幕方向发生改变,索引条也要相应改变
mScroller.onSizeChanged(w, h, oldw, oldh);
}
}
}
import com.android.xiong.gridlayoutTest.util.IndexScroller;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.ListAdapter;
import android.widget.ListView;
public class IndexableListView extends ListView {
private boolean mIsFastScrollEnable = false;
//负责绘制索引条
private IndexScroller mScroller = null;
private GestureDetector mGestureDetector = null;//手势检测器
public IndexableListView(Context context) {
super(context);
}
public IndexableListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean isFastScrollEnabled() {
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); //绘制listview原本的item
if (mScroller != null) {
//绘制右侧的索引条
mScroller.draw(canvas);
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
//如果mScroller自己来处理触摸事件,该方法返回true。
//如果用户手指点到了右侧索引条,就由它自己来处理
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) {
super.setAdapter(adapter);
if (mScroller != null) {
mScroller.setAdapter(adapter);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mScroller != null) {
//当屏幕方向发生改变,索引条也要相应改变
mScroller.onSizeChanged(w, h, oldw, oldh);
}
}
}
注解:
画布上绘制一个文本,有一条基准线:baseline,往上是负,往下是正。
()就是baseline到descender line的距离(正值)。
ascent()就是baseline到ascender line的距离(负值)。
所以一个文本的高度就是:paint.descent()-paint.ascent()。
画布上绘制一个文本,有一条基准线:baseline,往上是负,往下是正。
()就是baseline到descender line的距离(正值)。
ascent()就是baseline到ascender line的距离(负值)。
所以一个文本的高度就是:paint.descent()-paint.ascent()。