Android 仿美团大众字母索引实现

android字母索引流程:

  界面步骤:
  1、初始化右侧字母的高度以及大小
  2、回执索引列表,并为列表当中的字母设置监听事件

业务逻辑步骤:
   1、先从服务器读取我的好友列表
   2、将好友的名字转换为拼音,并取得首字母,按照字母大小进行排序。装载联系人非字母为首的数组。保存到selector HashMap当中。键为联系人的字母的开头,值为联系人在这个集合里面的下标
   3、取得返回数据装载到适配器里面去 

会用到的第三方库:pinyin4j.jar :http://download.csdn.net/detail/q908555281/9296197
 

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
	<!-- 这个用来显示联系人的列表 -->
    <ListView
        android:id="@+id/listview_news_friends"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/comment_background"
        android:cacheColorHint="#00000000"
        android:divider="@null"
        android:fadingEdge="none"
        android:scrollbars="none" >
    </ListView>
	<!-- 这个用来显示右侧字母栏的布局 -->
    <LinearLayout
        android:id="@+id/lilayout_news_friends"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="right"
        android:background="#d7d7d7"
        android:gravity="center"
        android:orientation="vertical" >
    </LinearLayout>

    <!-- 这个用来显示点击字母后,屏幕中间出现的字母提示 -->
    <TextView
        android:id="@+id/tv_news_friends_letter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:background="@color/white"
        android:gravity="center"
        android:paddingBottom="@dimen/px_20"
        android:paddingLeft="@dimen/px_30"
        android:paddingRight="@dimen/px_30"
        android:paddingTop="@dimen/px_20"
        android:text="A"
        android:textColor="@color/common_text_input"
        android:textSize="@dimen/text_size_25"
        android:visibility="gone" />

</FrameLayout>


主类:


import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.TextView;

import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.slife.gopapa.R;
import com.slife.gopapa.activity.news.ContactsActivity;
import com.slife.gopapa.adapter.FriendsAdapter;
import com.slife.gopapa.application.MyApplication;
import com.slife.gopapa.common.APPConstants;
import com.slife.gopapa.dao.JArrayErrorDao;
import com.slife.gopapa.dao.impl.JArrayErrorDaoImpl;
import com.slife.gopapa.database.DBConstants;
import com.slife.gopapa.http.MyHttpClient;
import com.slife.gopapa.model.ContactsPerson;
import com.slife.gopapa.util.ChineseTransferUtils;

/**
* @ClassName: FriendsFragment 
* @Description: 我的好友界面
*  界面步骤:
*  1、初始化右侧字母的高度以及大小
*  2、回执索引列表,并为列表当中的字母设置监听事件
* 
* 	业务逻辑步骤:
* 	1、先从服务器读取我的好友列表
*   2、将好友的名字转换为拼音,并取得首字母,按照字母大小进行排序。装载联系人非字母为首的数组。保存到selector HashMap当中。键为联系人的字母的开头,值为联系人在这个集合里面的下标
*   
*   3、取得返回数据装载到适配器里面去 
* @author 菲尔普斯
* @date 2015-3-26 下午5:12:37 
*
 */
public class FriendsFragment extends Fragment {
	@ViewInject(R.id.listview_news_friends)
	private ListView listView; // 联系人列表
	@ViewInject(R.id.tv_news_friends_letter)
	private TextView txCenter; // 显示在中间的字母
	@ViewInject(R.id.lilayout_news_friends)
	private LinearLayout layoutIndex;
	private HashMap<String, Integer> selector = new HashMap<String, Integer>();// 存放含有索引字母的位置
	private static final String[] indexStr = { "☆", "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 List<ContactsPerson> newPersons = new ArrayList<ContactsPerson>();// 对联系人进行转换成拼音
	private int height;// 字体高度
	private boolean flag = false;// 选中标签

	private FriendsAdapter adapter;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.fragment_friends, container,
				false);
		ViewUtils.inject(this, view);
		View headView = inflater.inflate(R.layout.view_friends_headview, null);
		listView.addHeaderView(headView);
		initOverload();
		initListView();
		return view;
	}

	private void initListView() {
		adapter = new FriendsAdapter(getActivity(), newPersons);
		listView.setAdapter(adapter);
		new GetFriendListTask().execute();
		listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				if(position==0){
					startActivity(new Intent(getActivity(),ContactsActivity.class));
				}
			}
		});

	}

	/**
	 * @Title: initOverload
	 * @Description: 初始化右侧字母栏高度
	 * @param
	 * @return void
	 * @throws
	 */
	private void initOverload() {
		// 这是获取屏幕宽高的监听器
		ViewTreeObserver observer = layoutIndex.getViewTreeObserver();
		observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
			public boolean onPreDraw() {
				if (!flag) {
					// 获取每个字母的高度
					height = layoutIndex.getMeasuredHeight() / indexStr.length;
					getIndexView();
					flag = true;
				}
				return true;
			}
		});
	}

	/**
	 * @Title: getIndexView
	 * @Description: 绘制索引列表(字母列表的监听事件)
	 * @param
	 * @return void
	 * @throws
	 */
	public void getIndexView() {
		LinearLayout.LayoutParams params = new LayoutParams(
				LayoutParams.WRAP_CONTENT, height);
		for (int i = 0; i < indexStr.length; i++) {
			final TextView tv = new TextView(this.getActivity());// 右侧字母索引
			tv.setLayoutParams(params);
			tv.setText(indexStr[i]);// 右侧字母
			tv.setPadding(10, 0, 10, 0);
			tv.setTextSize(12);
			tv.setTextColor(Color.parseColor("#3E75A9"));
			layoutIndex.addView(tv);// 设置右侧索引布局的触摸监听器
			layoutIndex.setOnTouchListener(new OnTouchListener() {
				@SuppressLint("ClickableViewAccessibility")
				@Override
				public boolean onTouch(View v, MotionEvent event) {
					float y = event.getY();
					int index = (int) (y / height);
					if (index > -1 && index < indexStr.length) {// 防止越界
						String key = indexStr[index];
						if (selector.containsKey(key)) {
							int pos = selector.get(key);
							// 将ListView的item移动到点击相应字母
							if (listView.getHeaderViewsCount() > 0) {// 防止ListView有标题栏,本例中没有。
								listView.setSelectionFromTop(
										pos + listView.getHeaderViewsCount(), 0);
							} else {
								listView.setSelectionFromTop(pos, 0);// 滑动到第一项
							}
							txCenter.setVisibility(View.VISIBLE);
							txCenter.setText(indexStr[index]);
						} else {
							txCenter.setVisibility(View.VISIBLE);
							txCenter.setText(indexStr[index]);
						}
					}
					switch (event.getAction()) {
					case MotionEvent.ACTION_DOWN:
						layoutIndex.setBackgroundColor(Color
								.parseColor("#606060"));
						txCenter.setVisibility(View.VISIBLE);
						break;
					case MotionEvent.ACTION_MOVE:
						break;
					case MotionEvent.ACTION_UP:
						layoutIndex.setBackgroundColor(Color
								.parseColor("#00ffffff"));
						txCenter.setVisibility(View.GONE);
						break;
					}
					return true;
				}
			});
		}
	}
	/**
	* @ClassName: GetFriendListTask 
	* @Description: 取得我的好友列表
	* @author 菲尔普斯
	* @date 2015-3-26 下午5:13:45 
	*
	 */
	class GetFriendListTask extends AsyncTask<Void, Void, String[]> {

		@Override
		protected String[] doInBackground(Void... params) {

			return MyHttpClient.postDataToService(getActivity(),
					APPConstants.URL_HOSTNAME
							+ APPConstants.URL_FRIENDS_LISTSAPP2,
					MyApplication.tokenApp2, new String[] { "user_account" },
					new String[] { MyApplication.preferences.getString(DBConstants.USER_ACCOUNT, "") }, null,
					null);
		}

		@Override
		protected void onPostExecute(String[] result) {
			super.onPostExecute(result);
			JArrayErrorDaoImpl.resolveJson(getActivity(), result,
					new JArrayErrorDao() {

						@Override
						public void disposeJsonArray(JSONArray array) {
							if (array != null && array.length() > 0) {
								for (int i = 0; i < array.length(); i++) {
									try {
										JSONObject obj = array.getJSONObject(i);
										ContactsPerson info = new ContactsPerson();
										info.setUser_account(obj
												.optString("friend_user_account"));
										info.setExtend_user_account(obj
												.optString("friend_extend_user_account"));
										info.setUser_nickname(obj
												.optString("user_nickname"));
										info.setUser_logo(obj
												.getString("user_logo_200"));
										info.setName(obj
												.optString("user_nickname"));
										newPersons.add(info);
									} catch (JSONException e) {
										e.printStackTrace();
									}
								}
								layoutIndex.setBackgroundColor(Color.parseColor("#00ffffff"));// 设置背景颜色
								txCenter.setVisibility(View.GONE); 
								initPinyin();
							}
						}

						@Override
						public void disponseJsonArrayFinish() {
							// TODO Auto-generated method stub
							
						}
					});
		}
	}
	
	/**
	* @Title: getPinyin
	* @Description: 获取每个人的拼音的首字母,并setPinyinName为这个首字母。然后将集合进行排序。再将集合里面的每个首字母和Position的位置存放到selector对象当中
	* @param 
	* @return void
	* @throws
	 */
	@SuppressLint("DefaultLocale")
	private void initPinyin(){
		for(int i=0;i<newPersons.size();i++){
			String name=newPersons.get(i).getUser_nickname();
			if(name!=null&&!"".equals(name)){
				String pinyin=ChineseTransferUtils.getPingYin(name);
				newPersons.get(i).setPinyinName((pinyin.substring(0, 1)).toUpperCase());
			}
		}
		Collections.sort(newPersons); //对联系人进行排序从A-Z(根据pinyinName)
		for(int j=0;j<indexStr.length;j++){
			for(int i=0;i<newPersons.size();i++){
				if(newPersons.get(i).getPinyinName().equals(indexStr[j])){
					if(!selector.containsKey(indexStr[j])){
						selector.put(indexStr[j], i);	
					}
				}else{
					if(!selector.containsKey("#")){
						selector.put("#", i);	
					}
				}
			}
		}
		adapter.notifyDataSetChanged();
	}
}


字母处理工具类:


import java.util.Arrays;
import java.util.List;
import java.util.TreeSet;

import com.slife.gopapa.model.ContactsPerson;

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;

/****
 * @ClassName: ChineseTransferUtils
 * @Description: 汉字与拼音以及首字母转换的助手类(pingyin4j的处理)
 * @author 菲尔普斯
 * @date 2015-2-2 上午9:59:21
 * 
 */
public class ChineseTransferUtils {
	/**
	 * @Title: getPingYin
	 * @Description: 得到 汉字的全部拼音
	 * @param @param src 要转换的对象
	 * @param @return
	 * @return String
	 * @throws
	 */
	public static String getPingYin(String src) {
		char[] stringCharArray = null;
		stringCharArray = src.toCharArray();
		String[] stringArray = new String[stringCharArray.length];
		HanyuPinyinOutputFormat outputFormat = new HanyuPinyinOutputFormat();
		// 设置输出格式
		outputFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
		outputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
		outputFormat.setVCharType(HanyuPinyinVCharType.WITH_V);
		String preString = "";
		int t0 = stringCharArray.length;
		try {
			for (int i = 0; i < t0; i++) {
				// 判断是否为汉字字符
				if (java.lang.Character.toString(stringCharArray[i]).matches(
						"[\\u4E00-\\u9FA5]+")) {
					stringArray = PinyinHelper.toHanyuPinyinStringArray(
							stringCharArray[i], outputFormat);
					preString += stringArray[0];
				} else {
					preString += java.lang.Character
							.toString(stringCharArray[i]);
				}
			}
			return preString;
		} catch (BadHanyuPinyinOutputFormatCombination e1) {
			e1.printStackTrace();
		}
		return preString;
	}

	/**
	 * @Title: getHeadChar
	 * @Description: 得到首字母
	 * @param @param str
	 * @param @return
	 * @return String
	 * @throws
	 */
	public static String getHeadChar(String str) {

		String convert = "";
		char word = str.charAt(0);
		String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(word);
		if (pinyinArray != null) {
			convert += pinyinArray[0].charAt(0);
		} else {
			convert += word;
		}
		return convert.toUpperCase();
	}

	/**
	 * @Title: getPinYinHeadChar
	 * @Description: 得到中文首字母缩写
	 * @param @param str
	 * @param @return
	 * @return String
	 * @throws
	 */
	public static String getPinYinHeadChar(String str) {

		String convert = "";
		for (int j = 0; j < str.length(); j++) {
			char word = str.charAt(j);
			String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(word);
			if (pinyinArray != null) {
				convert += pinyinArray[0].charAt(0);
			} else {
				convert += word;
			}
		}
		return convert.toUpperCase();
	}
	
	/**
	 * @Title: sortIndex
	 * @Description: 字母排序。获取排序后的新数据
	 * @param @param persons
	 * @param @return
	 * @return String[]
	 * @throws
	 */
	public static String[] sortIndex(List<ContactsPerson> contacts) {
		TreeSet<String> set = new TreeSet<String>();
		String a = null;
		// 获取初始化数据源中的首字母,添加到set中
		for (ContactsPerson person : contacts) {
			String pre = ChineseTransferUtils.getPinYinHeadChar(person.getName()).substring(0, 1);
			if (65 <= pre.charAt(0) && pre.charAt(0) <= 90 || 97 <= pre.charAt(0) && pre.charAt(0) <= 122) {
				set.add(pre);
			} else {
				set.add("!");
			}
		}
		// 新数组的长度为原数据加上set的大小(set内部已经按字母大小排序,将set里的数据装载到数组中以便往后调用系统拷贝方法)
		String[] names = new String[contacts.size() + set.size()];
		int i = 0;
		for (String string : set) {
			names[i] = string;
			i++;
		}
		String[] pinYinNames = new String[contacts.size()];
		for (int j = 0; j < contacts.size(); j++) {
			contacts.get(j).setPinyinName(a = ChineseTransferUtils.getPingYin(contacts.get(j).getName().toString()));
			pinYinNames[j] = a + "&^-@";
		}
		// 将原数据拷贝到新数据中(将pinYinNames数组中的元素从0下标开始复制元素到names数组)
		// src:源数组; srcPos:源数组要复制的起始位置; dest:目的数组; destPos:目的数组放置的起始位置;
		// length:复制的长度。 注意:src and dest都必须是同类型或者可以进行转换类型的数组.
		System.arraycopy(pinYinNames, 0, names, set.size(), pinYinNames.length);
		// 复制后自动按照首字母排序
		Arrays.sort(names, String.CASE_INSENSITIVE_ORDER);// 这个方法得到的数组不是以A为开头的,所以下面的方法就是重新排布用户名的顺序
		/**************** 重新排布用户名的顺序开始 ************************/
		String[] newNames = new String[names.length + 1];// 装载以A为首的数组
		String[] postNames = new String[names.length];// 装载非字母为首的数组
		int k = 0;
		int postCount = 0;
		int temp = 0;
		for (String pre : names) {
			if (65 <= pre.charAt(0) && pre.charAt(0) <= 90 || 97 <= pre.charAt(0) && pre.charAt(0) <= 122) {
				// 字母开头的数组
				newNames[k] = names[temp];
				k++;
				temp++;
			} else {
				// 非字母开头的数组
				postNames[postCount] = names[postCount];
				temp = ++postCount;
			}
		}
		// 将非字母开头的数组加到字母开头数组的后面
		for (int j = 0; j < postCount; j++, k++) {
			newNames[k] = postNames[j];
		}
		temp = 0;
		// 防止数组中有null
		for (String pew : newNames) {
			if (pew != null)
				postNames[temp++] = pew;
		}
		/**************** 重新排布用户名的顺序结束 ************************/
		return postNames;
	}
}

最后的实体类:因为实体类需要进行排序,所以重写compareTo方法


import java.io.Serializable;

/**
 * @ClassName: ContactsPerson
 * @Description: 我的好友实体类和通讯录实体类公用 通讯录联系人的实体类 ,以及好友请求列表的实体类
 * 
 *               status 联系人状态 -2 : 不是手机号 -1 : 是手机但是没有注册啪啪,可邀请 0 :
 *               是手机且已经注册啪啪,不可添加为好友 1 : 是手机且已经注册啪啪,可添加为好友 2 : 是手机且已经注册啪啪,已经是好友
 * @author 菲尔普斯
 * @date 2015-1-4 下午2:15:48
 * 
 */
public class ContactsPerson implements Serializable,Comparable<ContactsPerson> {
	/**
	 * @Fields serialVersionUID : TODO(用一句话描述这个变量表示什么)
	 */
	private static final long serialVersionUID = -8839670785761661380L;
	public String name;// 中文名
	public String pinyinName;// 拼音的名字
	public String phone; // 电话号码
	public String status;// 是不是好友(状态,默认-1,代表是还未处理)
	public String user_account; // 啪啪号
	public String extend_user_account; // 聊天账号
	public String user_nickname; // 用户昵称
	public String user_logo; // 用户头像

	public ContactsPerson() {
		super();
	}

	public ContactsPerson(String name) {
		super();
		this.name = name;
	}

	public ContactsPerson(String name, String pinyinName) {
		super();
		this.name = name;
		this.pinyinName = pinyinName;
	}

	public ContactsPerson(String name, String pinyinName, String phone) {
		super();
		this.name = name;
		this.pinyinName = pinyinName;
		this.phone = phone;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPinyinName() {
		return pinyinName;
	}

	public void setPinyinName(String pinyinName) {
		this.pinyinName = pinyinName;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public String getUser_account() {
		return user_account;
	}

	public void setUser_account(String user_account) {
		this.user_account = user_account;
	}

	public String getExtend_user_account() {
		return extend_user_account;
	}

	public void setExtend_user_account(String extend_user_account) {
		this.extend_user_account = extend_user_account;
	}

	public String getUser_nickname() {
		return user_nickname;
	}

	public void setUser_nickname(String user_nickname) {
		this.user_nickname = user_nickname;
	}

	public String getUser_logo() {
		return user_logo;
	}

	public void setUser_logo(String user_logo) {
		this.user_logo = user_logo;
	}

	@Override
	public int compareTo(ContactsPerson another) {
		char a=this.getPinyinName().charAt(0);
		char b=another.getPinyinName().charAt(0);
		if(Integer.valueOf(a)>Integer.valueOf(b)){
			return 1;
		}else{
			return -1;
		}
	}

}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值