一、效果图
二、简介
我在做项目时候添加的功能,网上看了一些别人的做法,觉得一般般,然后就自己花了一下午写了一个感觉还可以的。
列表用的是listView,侧边字母栏是自定义了一个SideBar。
底部有完整的demo(包括json解析为对应的list bean)。
三、思路过程
1. 先确定服务端返回的json格式
2. 把服务器的数据解析为list bean
3. 根据list的bean里面的字符寸的第一个字母的拼音进行重新排序
4. 排序过程在bean添加首字母标识
5. 适配器进行数据填充到listView,根据bean里面的首字母标识进行显示隐藏字母
6. 封装SideBar字母侧边栏,滑动回调字母listView跳到指定的字母
四、详细过程
1. 知道接口数据格式
从后台开发者给的接口可以知道json的格式如下。
{
"desc":"查询成功",
"data":{
"diseaseList":[
{
"xmlName":"痴呆的评估",
"xmlId":"242",
"category":"symptom"
},
{
"xmlName":"非故意性体重减轻评估",
"xmlId":"548",
"category":"symptom"
}
]
},
"action":null,
"code":"",
"type":"SUCCESS"
}
2. 解析为对应的bean的list
我这里是利用上一篇文章的json解析类进行解析: http://blog.csdn.net/niubitianping/article/details/53117978
这里就解析成了List,DiseaseList里面是xmlName、xmlId、category,还有对应的get和set。
3. 重新排序
这是重点,首先用到一个库:pinyin4j来转换把中文转换为拼音。
(ps:还有jpinyin,chineseandpinyin这两个库也是可以转换的。jpinyin好像是pinyin4的优化版 具体我没用。chineseandpinyin我导入了跑起来报错JSONObject找不到,我把json包导入了之后也是一堆问题)。
(1) 导入pinyin4j的库
官网下载地址:http://sourceforge.net/project/showfiles.php?group_id=163377
好多年没更新了,最新是2.5,导入库这么简单就不说了。
(2) 编写比较类
利用java的Collections类进行排序,自己实现排序方法
//对list进行按照拼音排序
Collections.sort(list, new PinYinComparator());
PinYinComparator.java比较类
/**
* 作用:取第一个字的拼音进行比较排序
*
* @author LITP
* @date 2016/11/10
*/
public class PinYinComparator implements Comparator<DiseaseList> {
//已经存在的首字母列表
private SparseArray<Character> letters = new SparseArray<>();
@Override
public int compare(DiseaseList lhs, DiseaseList rhs) {
int c1 = PinYinUtils.getFirstPiniYin(lhs.getXmlName()).charAt(0);
int c2 = PinYinUtils.getFirstPiniYin(rhs.getXmlName()).charAt(0);
//判断是否已经存在了首字母了
if (letters.get(c1) == null) {
//判断是不是字母
if (Character.isLetter((char) c1)) {
letters.put(c1,(char) c1);
//重点,设置首字母标识
lhs.setLetter((char) c1 + "");
} else { //设置#标识
letters.put('#', (char) c1);
lhs.setLetter("#");
}
}
//c1 不为字母
if ((c1 < 65)) {
c1 += 90; // 使其在后面
}
//c2 不为字母
if ((c2 < 65)) {
c2 += 90; // 使其在后面
}
return c1 - c2; //负数则第一个参数在前面,
}
}
4. 编写适配器
列表的适配器,重点是 item的布局是包括的一行的字母的view,根据bean里面是否有字母而隐藏显示
BMJLetterListAdapter.java
/**
* 作用: 字母排序列表的适配器
*
* @author LITP
* @date 2016/11/4
*/
public class BMJLetterListAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private List<?> mDatas;
public BMJLetterListAdapter(Context context, List<?> datas){
mInflater = LayoutInflater.from(context);
mDatas = datas;
}
@Override
public int getCount() {
Log.e("tpnet","总数"+mDatas.size());
return mDatas.size();
}
@Override
public Object getItem(int arg0) {
return mDatas.get(arg0);
}
@Override
public long getItemId(int arg0) {
return arg0;
}
@Override
public View getView(int position, View convertView, ViewGroup arg2) {
MyViewHolder myViewHolder = null;
if(convertView == null){
myViewHolder = new MyViewHolder();
convertView = mInflater.inflate(R.layout.item_bmj_letter_list,arg2,false);
myViewHolder.letterText = (TextView) convertView.findViewById(R.id.bmj_tv_letter);
myViewHolder.contentText = (TextView) convertView.findViewById(R.id.bmj_tv_content);
convertView.setTag(myViewHolder);
}else{
myViewHolder = (MyViewHolder) convertView.getTag();
}
DiseaseList item = (DiseaseList) getItem(position);
if(item != null){
//如果有字母标识就显示 字母那一行
if(!item.getLetter().isEmpty()){
myViewHolder.letterText.setText(item.getLetter());
myViewHolder.letterText.setVisibility(View.VISIBLE);
}else{ //否则隐藏
myViewHolder.letterText.setVisibility(View.GONE);
}
myViewHolder.contentText.setText(item.getXmlName());
}
return convertView;
}
private class MyViewHolder{
private TextView letterText;
private TextView contentText;
}
}
布局文件请看demo,代码太多就不贴了
5. 封装侧边字母栏
上面的排序和数据已经搞好了,接下来是侧边的字母索引。要实现的功能有点击其中的字母 listview跳到指定的位置,滑动时候也要跳到指定的位置。 这个很简单,100多行代码就可以了
SideBar.java
/**
* 作用: 右边的字母View
*
* @author LITP
* @date 2016/11/2
*/
public class SideBar extends View {
// 26个字母
public static String[] letters = {"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 int choose = -1; // 选中的字母的y坐标
//字母画笔
private Paint paint = new Paint();
private TextView mTextDialog; //显示当前字母的文本框
private int singleHeight; //一个字母的空间
Rect rect = new Rect(); //存放文字的高度
public SideBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
paint.setAntiAlias(true); //设置抗锯齿
paint.setTextSize(DensityUtil.sp2px(12f)); //设置字母字体大小为12sp
//获取一个字母实际的宽高到rect
paint.getTextBounds("A", 0, 1, rect);
//获取一个字母的空间
singleHeight = (getHeight() - (getPaddingTop() + getPaddingBottom())) / 27;
}
/**
* 为SideBar设置显示字母的TextView
*
* @param mTextDialog 在activity传递过来的textView
*/
public void setTextView(TextView mTextDialog) {
this.mTextDialog = mTextDialog;
}
/**
* 绘制
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//循环绘制字母
for (int i = 0; i < letters.length; i++) {
//paint.setTypeface(Typeface.DEFAULT_BOLD); //设置默认字体加粗
// 选中的状态
if (i == choose) {
paint.setColor(getResources().getColor(R.color.light_color_blue)); //选中的字母改变颜色
paint.setFakeBoldText(true); //设置字体为粗体
} else {
paint.setColor(getResources().getColor(R.color.darkgray)); //设置字体颜色
paint.setFakeBoldText(false); //设置字体为正常
}
// x坐标等于中间-字符宽度的一半.
float xPos = getWidth() / 2 - paint.measureText(letters[i]) / 2;
//Y轴坐标
float yPos = getPaddingTop() + singleHeight * i + rect.height();
canvas.drawText(letters[i], xPos, yPos, paint); //绘制字母
}
}
/**
* 分发触摸事件
*
* @param event
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float y = event.getY();// 点击y坐标
final int oldChoose = choose; //上一个选中的字母
// 点击y坐标所占总高度的比例 * 数组的长度就等于点击了 数组中的位置.
final int c = (int) (y / (getHeight() - getPadddingTop() - getPaddingBottom()) * letters.length);
switch (action) {
case MotionEvent.ACTION_UP:
//抬起来的时候设置背景为透明
//setBackgroundDrawable(new ColorDrawable(0x00000000));
choose = -1;//
invalidate();
if (mTextDialog != null) {
mTextDialog.setVisibility(View.INVISIBLE);
}
break;
default:
//按下,滑动的时候设置背景为灰色
//setBackgroundDrawable(new ColorDrawable(0x44000000));
//setBackgroundResource(R.drawable.sidebar_background);
if (oldChoose != c) { //判断选中字母是否发生改变
if (c >= 0 && c < letters.length) {
if (listener != null) {
listener.onTouchLetterChanged(letters[c]);
}
if (mTextDialog != null) {
mTextDialog.setText(letters[c]);
mTextDialog.setVisibility(View.VISIBLE);
}
//设置选中字母在数组的位置
choose = c;
invalidate();
}
}
break;
}
return true;
}
// 触摸回调接口
private OnTouchLetterChangedListener listener;
public void setOnTouchLetterChangedListener(
OnTouchLetterChangedListener onTouchLetterChangedListener) {
this.listener = onTouchLetterChangedListener;
}
public interface OnTouchLetterChangedListener {
/**
* 触摸字母回调
*
* @param s 触摸的字符
*/
void onTouchLetterChanged(String s);
}
}
五、使用
- 窗口打开现在加载中的dialog
- 开始请求获取json
- 解析json为对应的list
- 在子线程中进行排序
- 更新到适配器
- 实现字母view滑动接口,字母view滑动点击 listView跳到指定位置
/**
* 点击或者滑动到当前的字母 listView跳到指定位置
*
* @param s 触摸的字符
*/
@Override
public void onTouchLetterChanged(String s) {
for (DiseaseList item : list) {
if (item.getLetter().equals(s)) {
bmjLvLetter.setSelection(list.indexOf(item));
return;
}
}
}
完整demo下载: http://download.csdn.net/detail/niubitianping/9679329