/**
* 侧拉索引:音乐APP,即时通讯,电商选择城市,短信验证选择城市都有这个类型自定义控件
* 实现步骤:
* 1.绘制A-Z的字母列表(自绘式自定义控件)
* 2.响应触摸事件
* 3.提供监听回调
* 4.获取汉字拼音首字母,首字母 (pinying4j通过汉字得到他的拼音,只能一个字符一个字符的转换拼音)
* 5.根据拼音排序
* 6.根据首字母分组
* 7.把监听回调和listview结合起来
* 掌握解决问题的思路,把复杂的东西简单化,吧复杂的东西尽可能分成小的模块,把我模块关键点,一步一个脚印
*/
public class MainActivity extends AppCompatActivity {
QuickindexBar qb;
private ListView lv;
private ArrayList<haohan> persons;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//listviewid
lv = (ListView) findViewById(R.id.lv);
//右边的字母栏
qb= (QuickindexBar) findViewById(R.id.qq);
persons = new ArrayList<>();
fillAndsortData(persons);
//controller层设置适配器
lv.setAdapter(new haohanAdapter(persons,this));
//根据用户按住的字符自动跳转到对应的listview条目上
qb.setOnLetterUpdateListener(new QuickindexBar.OnLetterUpdateListener() {
@Override
public void onLetterUpdate(String letter) {
for (int i = 0; i <persons.size() ; i++) {
String l=persons.get(i).getPinyin().charAt(0)+"";
if (TextUtils.equals(letter,l)){
//找到第一个首字母是letter条目
lv.setSelection(i);
break;
}
}
toastUtil.showToast(MainActivity.this,letter);
}
});
}
/**
* 填充数据,并排序
* @param persons
*/
private void fillAndsortData(ArrayList<haohan> persons) {
//填充数据
for (int i = 0; i < Cheeses.NAMES.length; i++) {
String name=Cheeses.NAMES[i];
persons.add(new haohan(name));
}
//排序
Collections.sort(persons);
}
}
/////////////////////////////////////////////////////////////////////////////////////
/**
* Created by 小亚 on 2017/8/14.
* function:快速索引栏实现思路
* 1.继承view,覆写构造方法,初始化画笔
* 2.在OnDrawer方法里绘制字符
* 3.在OnMeasure方法里测量高度
* 4.在OntouchEvent事件知道用户具体按住了那个字母
* 5.定义抽象方法,实现监听回调
*/
public class QuickindexBar extends View {
private Paint paint;
//A.要绘制的内容
private static final String[] LETTERS = new String[]{
"A", "B", "C", "D", "E", "F",
"G", "H", "I", "J", "K", "L",
"M", "N", "O", "P", "Q", "R",
"S", "T", "U", "V", "W", "X",
"Y", "Z"
};
private float cellHeight;
private int cellwidth;
private int mHeight;
private float y;
private float x;
private float Y;
private int currentindex;
public QuickindexBar(Context context) {
this(context, null);
}
public QuickindexBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public QuickindexBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
intiPaint();
}
private void intiPaint() {
//创建一个抗锯齿的画笔
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
//画笔文本加粗
paint.setTypeface(Typeface.DEFAULT_BOLD);
//颜色
paint.setColor(Color.WHITE);
//字体粗细
paint.setTextSize(15);
}
@Override
protected void onDraw(Canvas canvas) {
//遍历了26个字母,进行坐标计算,进行绘制
for (int i = 0; i < LETTERS.length; i++) {
//从数组,根据i去除字母
String letter = LETTERS[i];
//计算X轴坐标
x = cellwidth * 0.5f - paint.measureText(letter) * 0.5f;
//计算Y轴坐标
y = cellHeight * 0.5f + paint.measureText(letter) * 0.5f + i * cellHeight;
canvas.drawText(letter, x, y, paint);
}
}
//A.完成侧拉索引的测量,得到单元格的高度
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mHeight = getMeasuredHeight();
cellwidth = getMeasuredWidth();
//获取单元格的高度,自由定义控件总高度,除以所有字母所占用的高度
cellHeight = mHeight * 1.0f / LETTERS.length;
}
//记录用户上一次按下的位置,以便于进行判断这一次按住的位置是否还是上一次安卓的位置,如果是不做任何处理
private int lastIndex = 1;
//B.从谢触摸事件,返回值为True,方起效果
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
//计算用户按到那个字母的范围,主要是Y轴;
Y = event.getY();
currentindex = (int) (Y / cellHeight);
//为了防止一个字母按住,不停的重复调用,将进行判断,判断是否还是按着上一个字母,是的话就不做任何处理,提高程序的性能
if (currentindex != lastIndex) {
//为了防止角标越界
if (currentindex >= 0 && currentindex < LETTERS.length) {
String str = LETTERS[currentindex];
//设置回调监听
if (mOnLetterUpdateListener != null) {
mOnLetterUpdateListener.onLetterUpdate(str);
}
lastIndex = currentindex;
}
}
//Toast.makeText(getContext(),LETTERS[currentindex],Toast.LENGTH_SHORT).show();
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
//定义接口
public interface OnLetterUpdateListener {
void onLetterUpdate(String letter);
}
//定义接口对象
private OnLetterUpdateListener mOnLetterUpdateListener;
//暴露方法,让外界传过来一个实现接口的类对象
public void setOnLetterUpdateListener(OnLetterUpdateListener onLetterUpdateListener) {
mOnLetterUpdateListener = onLetterUpdateListener;
}
}
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
/**
* Created by 小亚 on 2017/8/14.
*utils工具类实现吐司
*/
public class toastUtil {
private static Toast toast;
public static void showToast(Context context,String msg){
if (toast==null){
toast=Toast.makeText(context,"",Toast.LENGTH_SHORT);
}
toast.setText(msg);
toast.show();
}
}
////////////////////////////////////////////////////////////
/**
* Created by 小亚 on 2017/8/15.
* function:工具类方法,一般定义为静态,不要创建类对象一样就可以调用
*导拼音jar包然后创建工具类
*/
public class PinyingUtils {
//创建对象,以便调用
private static HanyuPinyinOutputFormat hanyuPinyinOutputFormat;
//吧得到的字符串改为字符数组
public static String getPinyin(String string){
hanyuPinyinOutputFormat = new HanyuPinyinOutputFormat();
//不要音标
hanyuPinyinOutputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
//设置转换出大写字母
hanyuPinyinOutputFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);
char[] chars=string.toCharArray();
//创建一个装字符的容器,
StringBuffer sb=new StringBuffer();
for (int i = 0; i < chars.length; i++) {
char c=chars[i];
//如果是空格,跳出当前循环
if (Character.isWhitespace(c)){
continue;
}
//是不是汉字,如果不是直接拼写
if (c>-128&&c<127){
sb.append(c);
}//是汉字那么我们获取拼音
else{
try {
String s=PinyinHelper.toHanyuPinyinStringArray(c, hanyuPinyinOutputFormat)[0];
//获取某个字符对应的拼音,可以获取到多音字 , 单->shan ,dan ,朴-> pu ,piao
String s1 = PinyinHelper.toHanyuPinyinStringArray(c, hanyuPinyinOutputFormat)[0];
sb.append(s);
} catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) {
badHanyuPinyinOutputFormatCombination.printStackTrace();
}
}
}
return sb.toString();
}
}
//////////////////////////////////////////////////////////////
/**
* Created by 小亚 on 2017/8/15.
* 拼音工具类
*/
public class haohan implements Comparable<haohan>{
private String name;
private String pinyin;
public haohan(String name) {
this.name = name;
//使用工具类根据汉字拿到拼音
this.pinyin = PinyingUtils.getPinyin(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPinyin() {
return pinyin;
}
public void setPinyin(String pinyin) {
this.pinyin = pinyin;
}
@Override
public int compareTo(@NonNull haohan haohan) {
return this.pinyin.compareTo(haohan.pinyin);
}
}
//////////////////////////////////////////////////////////////////
/**
* Created by 小亚 on 2017/8/15.
* 完成效果使用的是,判断当前首字母和上一个条目的是否一致,不一致就显示全部界面,一致就隐藏
*一个界面
*/
class haohanAdapter extends BaseAdapter {
private ArrayList<haohan> persons;
private Context context;
public haohanAdapter(ArrayList<haohan> persons, Context context) {
this.persons=persons;
this.context=context;
}
@Override
public int getCount() {
return persons.size();
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
View view;
if (convertView==null){
view=View.inflate(context, R.layout.activity_item,null);
}else{
view=convertView;
}
TextView tv_index=(TextView)view.findViewById(R.id.tv_index);
TextView tv_name=(TextView)view.findViewById(R.id.tv_name);
haohan haohan=persons.get(position);
//当前首字母
String currentstr=haohan.getPinyin().charAt(0)+"";
String indexstr = null;
//如果是第一个名字,直接显示
if (position==0){
indexstr=currentstr;
}else{
//拿到当前首字母,上面一个条目的首字母,判断当前首字母和上一条首字母是否一致,不一致显示完整item
String laststr=persons.get(position-1).getPinyin().charAt(0)+"";
//当前首字母和上面一个判断两个参数是否一致,不一致就执行赋值逻辑
if (!TextUtils.equals(laststr,currentstr)) {
//不一致时赋值indexstr
indexstr = currentstr;
}
}
tv_index.setVisibility(indexstr !=null ?View.VISIBLE:View.GONE);
tv_index.setText(currentstr);
tv_name.setText(haohan.getName());
return view;
}
}
//////////////////////////////////////////////////////////////////////////////////
/**
* Created by 小亚 on 2017/8/15.
*/
public class Cheeses {
public static final String[] NAMES = new String[]{"宋江", "卢俊义", "吴用",
"公孙胜", "关胜", "林冲", "秦明", "呼延灼", "花荣", "柴进", "李应", "朱仝", "鲁智深",
"武松", "董平", "张清", "杨志", "徐宁", "索超", "戴宗", "刘唐", "李逵", "史进", "穆弘",
"雷横", "李俊", "阮小二", "张横", "阮小五", " 张顺", "阮小七", "杨雄", "石秀", "解珍",
" 解宝", "燕青", "朱武", "黄信", "孙立", "宣赞", "郝思文", "韩滔", "彭玘", "单廷珪",
"魏定国", "萧让", "裴宣", "欧鹏", "邓飞", " 燕顺", "杨林", "凌振", "蒋敬", "吕方",
"郭 盛", "安道全", "皇甫端", "王英", "扈三娘", "鲍旭", "樊瑞", "孔明", "孔亮", "项充",
"李衮", "金大坚", "马麟", "童威", "童猛", "孟康", "侯健", "陈达", "杨春", "郑天寿",
"陶宗旺", "宋清", "乐和", "龚旺", "丁得孙", "穆春", "曹正", "宋万", "杜迁", "薛永", "施恩",
"周通", "李忠", "杜兴", "汤隆", "邹渊", "邹润", "朱富", "朱贵", "蔡福", "蔡庆", "李立",
"李云", "焦挺", "石勇", "孙新", "顾大嫂", "张青", "孙二娘", " 王定六", "郁保四", "白胜",
"时迁", "段景柱", "易宸锋","郭嘉","吕布","袁绍","孙策","甘宁","吕蒙"};
}
本文介绍了一种自定义侧拉索引控件的实现方法,包括绘制字母列表、响应触摸事件、提供监听回调等功能,并展示了如何将其与ListView结合使用以实现快速定位。

被折叠的 条评论
为什么被折叠?



