src 中的 MainActivity:
public class MainActivity extends Activity implements OnItemClickListener {
/**
* 显示城市列表的ListView控件
*/
private ListView lvCityList;
/**
* 显示城市列表的ListView的数据源
*/
private List<City> cityList;
/**
* 显示城市列表的ListView的Adapter
*/
private CityAdapter cityAdapter;
/**
* 显示右侧字母导航的ListView控件
*/
private ListView lvSideBar;
/**
* 显示右侧字母导航的ListView的数据源
*/
private List<String> sortKeys;
/**
* 显示右侧字母导航的ListView的Adapter
*/
private SideBarAdapter sideBarAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化ListView
lvCityList = (ListView) findViewById(R.id.lv_city_list);
// 获取数据源
cityList = getCityListData();
// 对城市列表排序
sortCityListByPinyin(cityList);
// 创建adapter
cityAdapter = new CityAdapter(this, cityList);
// 为ListView配置adapter
lvCityList.setAdapter(cityAdapter);
// 初始化右侧字母导航的ListView
lvSideBar = (ListView) findViewById(R.id.lv_side_bar);
// 获取数据源
sortKeys = getSideBarData();
// 创建adapter
sideBarAdapter = new SideBarAdapter(this, sortKeys);
// 为ListView配置adapter
lvSideBar.setAdapter(sideBarAdapter);
// 为ListView配置Item点击监听器
lvSideBar.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// 根据poisiton获取被点击的字母
String str = sortKeys.get(position);
// 把点击的字母转换成char类型
char ch = str.charAt(0);
// 获取该字母在城市列表中出现的位置
int moveToPosition = cityAdapter.getPositionForSection(ch);
// 城市列表的ListView快速移动到指定位置
if(moveToPosition != -1) {
lvCityList.setSelection(moveToPosition);
}
}
/**
* 根据拼音将城市列表排序
*/
private void sortCityListByPinyin(List<City> data) {
Collections.sort(data, new Comparator<City>() {
@Override
public int compare(City lhs, City rhs) {
return lhs.getPinyin().compareTo(rhs.getPinyin());
}
});
}
/**
* 获取城市列表的数据源
*
* @return 城市列表的数据源
*/
private List<City> getCityListData() {
List<City> data = new ArrayList<City>();
data.add(new City("北京", "beijing"));
data.add(new City("上海", "shanghai"));
data.add(new City("广州", "guangzhou"));
data.add(new City("深圳", "shenzhen"));
data.add(new City("成都", "chengdu"));
data.add(new City("重庆", "chongqing"));
data.add(new City("大连", "dalian"));
data.add(new City("厦门", "xiamen"));
data.add(new City("丽江", "lijiang"));
data.add(new City("三亚", "sanya"));
data.add(new City("青岛", "qingdao"));
data.add(new City("铁岭", "tieling"));
data.add(new City("攀枝花", "panzhihua"));
data.add(new City("杭州", "hangzhou"));
data.add(new City("苏州", "suzhou"));
data.add(new City("南京", "nanjing"));
data.add(new City("武汉", "wuhan"));
data.add(new City("哈尔滨", "haerbin"));
return data;
}
/**
* 获取右侧字母导航的数据源
*
* @return 右侧字母导航的数据源
*/
private List<String> getSideBarData() {
List<String> data = new ArrayList<String>();
// 根据CityAdapter中的getSections()获取所有应该显示的首字母
String[] arr = (String[]) cityAdapter.getSections();
// 对数组进行排序
Arrays.sort(arr);
// 将数组转换成List
data = Arrays.asList(arr);
return data;
}
}
src 中的 SideBarAdapter:
public class SideBarAdapter extends BaseAdapter {
private Context context;
private List<String> data;
private LayoutInflater inflater;
public SideBarAdapter(Context context, List<String> data) {
super();
this.context = context;
this.setData(data);
inflater = LayoutInflater.from(this.context);
}
public void setData(List<String> data) {
if (data == null) {
data = new ArrayList<String>();
}
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.side_bar_item, null);
holder.tvSortKey = (TextView) convertView.findViewById(R.id.tv_side_bar_item_sort_key);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tvSortKey.setText(data.get(position));
return convertView;
}
private class ViewHolder {
TextView tvSortKey;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
}
src 中的 CityAdapter:
public class CityAdapter
extends BaseAdapter
implements SectionIndexer {
/**
* 上下文对象
*/
private Context context;
/**
* 数据源
*/
private List<City> data;
/**
* 将XML布局加载为View对象的工具
*/
private LayoutInflater inflater;
/**
* 构造方法
*
* @param context
* 上下文对象
* @param data
* 数据源
*/
public CityAdapter(Context context, List<City> data) {
super();
this.context = context;
this.setData(data);
inflater = LayoutInflater.from(this.context);
}
/**
* 设置数据源
*
* @param data
* 数据源
*/
public void setData(List<City> data) {
if (data == null) {
data = new ArrayList<City>();
}
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
// private String lastSortKey;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 准备ViewHolder
ViewHolder holder;
// 判断convertView参数是否为空
if (convertView == null) {
// 当convertView为空时,需要根据布局进行加载,并创建ViewHolder,完成初始化,将ViewHolder封装到convertView,以至于重用convertView时,可以直接获取ViewHolder
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.city_item, null);
holder.tvCitySortKey = (TextView) convertView
.findViewById(R.id.tv_city_sort_key);
holder.tvCityName = (TextView) convertView
.findViewById(R.id.tv_city_name);
convertView.setTag(holder);
} else {
// 当convertView不为空时,则表示它是被系统自动重用的,则它内部的控件也是重用的
holder = (ViewHolder) convertView.getTag();
}
// 获取当前需要显示的数据
City city = data.get(position);
if(position == getPositionForSection(getSectionForPosition(position))) {
// 当前位置与“根据位置找到的section,然后根据section找到应该出现的位置”相等,则显示section
holder.tvCitySortKey.setVisibility(View.VISIBLE);
} else {
// position与section出现的position不相等,则不显示
holder.tvCitySortKey.setVisibility(View.GONE);
}
// // 获取当前的sort key
// String currentSortKey = city.getPinyin().toUpperCase(Locale.CHINA)
// .substring(0, 1);
// // 对比是否与上一条相同,且必须是position > 0时进行对比(第1条记录不需要对比)
// if(position > 0 && currentSortKey.equals(
// data.get(position - 1)
// .getPinyin().
// toUpperCase(Locale.CHINA)
// .substring(0, 1))) {
// // 相同,隐藏
// holder.tvCitySortKey.setVisibility(View.GONE);
// } else {
// // 不同,显示
// holder.tvCitySortKey.setVisibility(View.VISIBLE);
// }
// 设置控件的显示
holder.tvCitySortKey.setText("" + ((char)getSectionForPosition(position)));
holder.tvCityName.setText(city.getName());
// 返回
return convertView;
}
private class ViewHolder {
TextView tvCitySortKey, tvCityName;
}
@Override
public int getPositionForSection(int section) {
int currentSection;
for (int i = 0; i < data.size(); i++) {
// 找到当次循环到的城市数据的section
currentSection = getSectionForPosition(i);
// 判断当次的section是否与参数section匹配
// 如果匹配,则当前循环的次数就是所需要的position,且跳出循环
if(currentSection == section) {
return i;
}
}
return -1;
}
@Override
public int getSectionForPosition(int position) {
return data.get(position).getPinyin().toUpperCase(Locale.CHINA).charAt(0);
}
// 以下抽象方法可以不具体实现
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
@Override
public Object[] getSections() {
// 基于Set容器内部数据的唯一性,先使用Set保存所有数据对应的首字母
Set<String> set = new HashSet<String>();
for (int i = 0; i < data.size(); i++) {
set.add("" + ((char)getSectionForPosition(i)));
}
Log.d("", "section count -> " + set.size());
// 根据Set的大小创建数组
String[] arr = new String[set.size()];
// 循环整个Set,把每个元素添加到数组
int i = 0;
for (String string : set) {
Log.d("", "set string -> " + string);
arr[i] = string;
Log.d("", "arr[" + i + "]" + arr[i]);
i++;
}
// 返回
return arr;
}
}
src 中的 City:
public class City {
private String name;
private String pinyin;
public City() {
super();
}
public City(String name, String pinyin) {
super();
this.name = name;
this.pinyin = pinyin;
}
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;
}
}
layout 中的 activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<ListView
android:id="@+id/lv_city_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ListView
android:id="@+id/lv_side_bar"
android:layout_width="20dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:divider="@null" >
</ListView>
</RelativeLayout>
layout 中的 city_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/tv_city_sort_key"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#cccccc"
android:paddingBottom="2dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="2dp"
android:text="C"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_city_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_city_sort_key"
android:paddingBottom="5dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp"
android:text="成都"
android:textSize="16sp" />
</RelativeLayout>
layout 中的 side_bar_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/tv_side_bar_item_sort_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="12sp"
android:text="A" />
</RelativeLayout>