Android ListView实现分组字母索引排序

       因为项目需要字母索引排序的效果,GIT上和博客上有很多,但是发现很多DEMO的效果和自己想要的有出入,所以稍微整理了一下。
       这篇就是在整理过程中自己写的测试Demo。
       先上截图(字符串是随机生成的,部分视觉效果也进行了处理……修复了Bug,但是搜索的效率太低,搜索实现的另一种方法,在下一篇博客中)。



 说明:顶部是搜索部分,右边是自定义的字母索引MyLetterSortView,中间的是展示数据的ListView。

       先看字母索引MyLetterSortView:

<pre name="code" class="java">public class MyLetterSortView extends View {
	// 触摸事件
	private OnTouchingLetterChangedListener onTouchingLetterChangedListener;
	// 26个字母
	public static String[] b = { "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;// 选中
	private Paint paint = new Paint();

	private TextView mTextDialog;

	public void setTextView(TextView mTextDialog) {
		this.mTextDialog = mTextDialog;
	}

	public MyLetterSortView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public MyLetterSortView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public MyLetterSortView(Context context) {
		super(context);
	}

	/**
	 * 重写这个方法
	 */
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		// 获取焦点改变背景颜色.
		int height = getHeight();// 获取对应高度
		int width = getWidth(); // 获取对应宽度
		int singleHeight = height / b.length;// 获取每一个字母的高度

		for (int i = 0; i < b.length; i++) {
			paint.setColor(Color.parseColor("#9da0a4"));
			paint.setTypeface(Typeface.DEFAULT_BOLD);
			paint.setAntiAlias(true);
	      //	paint.setTextSize(PixelUtil.sp2px(12));
	    	paint.setTextSize(25);
	
			// 选中的状态
			if (i == choose) {
				paint.setColor(Color.parseColor("#3399ff"));
				paint.setFakeBoldText(true);
			}
			// x坐标等于中间-字符串宽度的一半.
			float xPos = width / 2 - paint.measureText(b[i]) / 2;
			float yPos = singleHeight * i + singleHeight;
			canvas.drawText(b[i], xPos, yPos, paint);
			paint.reset();// 重置画笔
		}

	}

	@SuppressWarnings("deprecation")
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		final int action = event.getAction();
		final float y = event.getY();// 点击y坐标
		final int oldChoose = choose;
		final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
		final int c = (int) (y / getHeight() * b.length);// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数.

		switch (action) {
		case MotionEvent.ACTION_UP:
			setBackgroundDrawable(new ColorDrawable(0x00000000));
			choose = -1;//
			invalidate();
			if (mTextDialog != null) {
				mTextDialog.setVisibility(View.INVISIBLE);
			}
			break;

		default:
			//设置右侧字母列表[A,B,C,D,E....]的背景颜色
			setBackgroundResource(R.drawable.letter_sort_background);
			if (oldChoose != c) {
				if (c >= 0 && c < b.length) {
					if (listener != null) {
						listener.onTouchingLetterChanged(b[c]);
					}
					if (mTextDialog != null) {
						mTextDialog.setText(b[c]);
						mTextDialog.setVisibility(View.VISIBLE);
					}
					
					choose = c;
					invalidate();
				}
			}

			break;
		}
		return true;
	}

	/**
	 * 向外公开的方法
	 * 
	 * @param onTouchingLetterChangedListener
	 */
	public void setOnTouchingLetterChangedListener(
			OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
		this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
	}

	
	public interface OnTouchingLetterChangedListener {
		public void onTouchingLetterChanged(String s);
	}

}


 
 

       再看看布局activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.example.testbase.ClearEditText
        android:id="@+id/et_msg_search"
        android:layout_width="match_parent"
        android:layout_height="48.0dip"   
        android:background="@drawable/base_edit_input"
        android:drawableLeft="@drawable/icon_msg_search"
        android:drawablePadding="10dp"
        android:hint="@string/message_search_hint"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:singleLine="true" />

    <RelativeLayout
        android:id="@+id/layout_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/base_bg"
        android:orientation="vertical" >

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="top"
            android:background="@drawable/content_bg"
            android:orientation="horizontal" >

            <ListView
                android:id="@+id/list"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:cacheColorHint="#00000000"
                android:scrollbars="none" />

            <TextView
                android:id="@+id/tv_mid_letter"
                android:layout_width="80.0dip"
                android:layout_height="80.0dip"
                android:layout_centerInParent="true"
                android:layout_gravity="center"
                android:background="@drawable/letter_mid_view_background"
                android:gravity="center"
                android:padding="5dip"
                android:textColor="@color/base_actionbar_bg"
                android:textSize="35.0dip"
                android:visibility="invisible" />
        </RelativeLayout>

        <com.example.testbase.MyLetterSortView
            android:id="@+id/right_letter"
            android:layout_width="25dip"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:background="@color/transparent" />
    </RelativeLayout>

</LinearLayout>

       为了测试带有汉字,数字,字母的字符串排序,就写了一个随机字符串工具StringRandomUtils:

public class StringRandomUtils {

	private Random widthRandom = new Random();  
    private int length;  
    private static char[] charsNumber = ("0123456789").toCharArray(); 
    private static char[] charsLetter = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
    private static char[] charsRandom = ("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
    private static Random random = new Random();
    
    
    //参数为生成的字符串的长度,根据给定的char集合生成字符串数字  
    public static String randomNumber(int length){      
          
        char[] data = new char[length];  
          
        for(int i = 0;i < length;i++){  
            int index = random.nextInt(charsNumber.length);  
            data[i] = charsNumber[index];  
        }  
        String s = new String(data);  
        return s;  
    }  
    
    //参数为生成的字符串的长度,根据给定的char集合生成字符串  字母
    public static String randomLetter(int length){      
          
        char[] data = new char[length];  
          
        for(int i = 0;i < length;i++){  
            int index = random.nextInt(charsLetter.length);  
            data[i] = charsLetter[index];  
        }  
        String s = new String(data);  
        return s;  
    }  
    
    //参数为生成的字符串的长度,根据给定的char集合生成字符串  
    public static String getStringRandom(int length){      
          
        char[] data = new char[length];  
          
        for(int i = 0;i < length;i++){  
            int index = random.nextInt(charsRandom.length);  
            data[i] = charsRandom[index];  
        }  
        String s = new String(data);  
        return s;  
    }  
    
    //简体中文
    public static String getRandomJianHan(int len)
    {
        String ret="";
          for(int i=0;i<len;i++){
              String str = null;
              int hightPos, lowPos; // 定义高低位
              Random random = new Random();
              hightPos = (176 + Math.abs(random.nextInt(39))); //获取高位值
              lowPos = (161 + Math.abs(random.nextInt(93))); //获取低位值
              byte[] b = new byte[2];
              b[0] = (new Integer(hightPos).byteValue());
              b[1] = (new Integer(lowPos).byteValue());
              try
              {
                  str = new String(b, "GBk"); //转成中文
              }
              catch (UnsupportedEncodingException ex)
              {
                  ex.printStackTrace();
              }
               ret+=str;
          }
      return ret;
    }

	
}

       可以随机生成简体汉字的字符串,纯阿拉伯数字的字符串,纯字母的字符串,数字和字母组合的字符串,方便模拟测试数据。

       实体ItemBean定义三个字段,用于排序的sortLetters,username和头像avatar。

扫描二维码关注公众号,回复: 3268672 查看本文章

public class ItemBean {

	
	/**
	 * 显示数据拼音的首字母
	 */
	private String sortLetters;
	
	
	String username;
	
	String avatar;
	
	
	public String getSortLetters() {
		return sortLetters;
	}
	public void setSortLetters(String sortLetters) {
		this.sortLetters = sortLetters;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getAvatar() {
		return avatar;
	}
	public void setAvatar(String avatar) {
		this.avatar = avatar;
	}
	
	
	
}

       至于ItemBeanAdapter,需要实现SectionIndexer接口,重写getSections()和getSectionForPosition()方法即可,这里代码就不贴了。

       再来看看LetterSortActivity:

public class LetterSortActivity extends Activity implements OnClickListener {

	public static final String TAG = LetterSortActivity.class.getSimpleName();

	private Context context = LetterSortActivity.this;

	private ClearEditText mClearEditText;
	private TextView tv_mid_letter;
	private ListView listView;
	private MyLetterSortView right_letter;

	private ItemBeanAdapter mAdapter;
	private List<ItemBean> mlist = new ArrayList<ItemBean>();
	private InputMethodManager inputMethodManager;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
		setLinstener();
		initData();
		fillData();
	}

	protected void initData() {

		getData();
		mAdapter = new ItemBeanAdapter(this, mlist);
		listView.setAdapter(mAdapter);

	}

	protected void initView() {

		inputMethodManager = (InputMethodManager) this
				.getSystemService(Context.INPUT_METHOD_SERVICE);
		listView = (ListView) findViewById(R.id.list);
		mClearEditText = (ClearEditText) findViewById(R.id.et_msg_search);
		// 这里设置中间字母
		right_letter = (MyLetterSortView) findViewById(R.id.right_letter);
		tv_mid_letter = (TextView) findViewById(R.id.tv_mid_letter);
		right_letter.setTextView(tv_mid_letter);

	}

	protected void setLinstener() {

		// tv_reget_pwd.setOnClickListener(this);

		listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				T.showShort(getApplicationContext(), ((ItemBean) mAdapter
						.getItem(position)).getUsername());

			}
		});

		listView.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				// 隐藏软键盘
				if (getWindow().getAttributes().softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) {
					if (getCurrentFocus() != null)
						inputMethodManager.hideSoftInputFromWindow(
								getCurrentFocus().getWindowToken(),
								InputMethodManager.HIDE_NOT_ALWAYS);
				}
				return false;
			}
		});

		// 设置右侧触摸监听
		right_letter
				.setOnTouchingLetterChangedListener(new OnTouchingLetterChangedListener() {

					@Override
					public void onTouchingLetterChanged(String s) {
						// 该字母首次出现的位置
						int position = mAdapter.getPositionForSection(s
								.charAt(0));
						if (position != -1) {
							listView.setSelection(position );
						}

					}
				});

		// 根据输入框输入值的改变来过滤搜索
		mClearEditText.addTextChangedListener(new TextWatcher() {

			@Override
			public void onTextChanged(CharSequence s, int start, int before,
					int count) {
				// 当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表
				filterData(s.toString());
			}

			@Override
			public void beforeTextChanged(CharSequence s, int start, int count,
					int after) {

			}

			@Override
			public void afterTextChanged(Editable s) {
			}
		});
	}

	protected void fillData() {
		// TODO Auto-generated method stub

	}

	private void getData() {
		mlist.clear();

		// 随机生成40个简体汉字字符串
		for (int i = 0; i < 40; ++i) {
			ItemBean uBean = new ItemBean();

			String str1 = StringRandomUtils.getRandomJianHan(5);
			String ch1 = ((CharacterParser.getInstance().getSelling(str1))
					.toUpperCase()).substring(0, 1);
			uBean.setSortLetters(ch1);
			uBean.setUsername(str1);
			mlist.add(uBean);

		}

		// 随机生成40个字母数字组合字符串
		for (int i = 0; i < 40; ++i) {
			ItemBean uBean = new ItemBean();
			String str1 = StringRandomUtils.getStringRandom(5);
			String ch1 = ((CharacterParser.getInstance().getSelling(str1))
					.toUpperCase()).substring(0, 1);
			uBean.setSortLetters(ch1);
			uBean.setUsername(str1);
			mlist.add(uBean);

		}

		// 对list进行排序
		Collections.sort(mlist, new PinyinComparator() {
		});

	}

	/**
	 * 根据输入框中的值来过滤数据并更新ListView
	 * 
	 * @param filterStr
	 */
	private void filterData(String filterStr) {
		List<ItemBean> filterDateList = new ArrayList<ItemBean>();
		if (TextUtils.isEmpty(filterStr)) {
			mAdapter.updateListView(mlist);
		} else {
			filterDateList.clear();
			for (ItemBean sortModel : mlist) {
				String name = sortModel.getUsername();
				if (name != null) {
					if (name.indexOf(filterStr.toString()) != -1
							|| CharacterParser.getInstance().getSelling(name)
									.startsWith(filterStr.toString())) {
						filterDateList.add(sortModel);
					}
				}
			}
			Collections.sort(filterDateList, new PinyinComparator() {
			});

			mAdapter.updateListView(filterDateList);
		}

	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		//
		default:
			break;
		}

	}

}

       首先 通过getData()初始化测试数据,一共80条,再通过Collections.sort(mlist, new PinyinComparator())排序,然后把数据源放在Adapter中即可。
       至于搜索,仍然是先筛选出条件数据,再进行排序,不过这样效率是在不敢恭维,数据量少的话,或许感觉不出来,但是一遇到几千条数据要等好几秒,在下一篇博客中将使用Filterable的getFilter()实现数据过滤,效率要高不少。
       好了,上面的代码很好理解,就不多说了。

Demo下载地址:http://download.csdn.net/detail/yalinfendou/8615325

猜你喜欢

转载自blog.csdn.net/yalinfendou/article/details/45166119