最近开发项目有一个需要,选择城市,有三级滚筒式的,有些丑,所以参考了他人的联系人列表选择做了一个。先上图看下效果:
https://img-blog.csdn.net/20170112141746730?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhbjEwMDAx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
具体效果,为左边显示城市列表,在每个拼音的开头显示大写字母,右边的控件点击滑动,会在中间显示选中的字母,并有小框框显示,列表滑动到相应的字母处。
必须引入汉字转拼音开源库:https://github.com/stuxuhai/jpinyin
具体代码如下:
右侧字母表自定义控件:
public class SideCityBar extends View { private String[] letterStrings = {"#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "S", "Y", "Z"}; private Paint paint; private Paint mPaint; private Context mContext; private int heght; private int postion; private boolean isShow = false; private Rect rect = new Rect(); public SideCityBar(Context context) { super(context); init(context); } public SideCityBar(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public SideCityBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { mContext = context; mPaint = new Paint(); mPaint.setColor(Color.WHITE); mPaint.setTextSize(30); mPaint.setAntiAlias(true); paint = new Paint(); paint.setColor(Color.WHITE); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(5); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { heght = getHeight() / letterStrings.length; super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); setBackgroundColor(Color.BLACK); for (int i = 0; i < letterStrings.length; i++) { String s1 = letterStrings[i]; mPaint.getTextBounds(s1, 0, s1.length(), rect); int h1 = rect.height(); int w1 = rect.width(); // mPaint.measureText(s1)只需要文字宽度可用此方法。 int h2 = heght / 2 + h1 / 2 + heght * i; int w2 = getWidth() / 2 - w1 / 2; canvas.drawText(s1, w2, h2, mPaint); if (isShow && (i == postion)) { canvas.drawRect((getWidth() - heght) / 2, (0 + i * heght), (getWidth() + heght) / 2, (heght + i * heght), paint); } } } @Override public boolean onTouchEvent(MotionEvent event) { float y1 = event.getY(); if (event.getAction() == MotionEvent.ACTION_UP) { isShow = false; mTextView.setVisibility(GONE); mOnchangeString.setTextShow(false); invalidate(); return true; } else { isShow = true; //计算框框的位置 postion = (int) (y1 / heght); if (postion < letterStrings.length) { mTextView.setVisibility(VISIBLE); mTextView.setText(letterStrings[postion]); mOnchangeString.getChangeString(letterStrings[postion]); mOnchangeString.setTextShow(true); invalidate(); } return true; } } TextView mTextView; public void setTextView(TextView textView) { mTextView = textView; } public void setOnChangeLis(OnchangeString onChangeLis) { mOnchangeString = onChangeLis; } OnchangeString mOnchangeString; public interface OnchangeString { void getChangeString(String s); void setTextShow(boolean b); } }
数据封装类:
public class CityModel { private String name; private String fistletter; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getFistletter() { return fistletter; } public void setFistletter(String fistletter) { this.fistletter = fistletter; } }
接着就是主布局和列表布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" > <ListView android:id="@+id/l1" android:layout_width="match_parent" android:layout_height="match_parent" /> <TextView android:id="@+id/t1" android:layout_width="100dp" android:layout_height="100dp" android:layout_centerInParent="true" android:background="#3CAC48" android:gravity="center" android:textColor="#ffffff" android:textSize="50dp" android:visibility="gone" /> <allother.two.sidecity.SideCityBar android:id="@+id/s1" android:layout_width="40dp" android:layout_height="match_parent" android:layout_alignParentRight="true" android:layout_centerVertical="true" /> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/t1" android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center_vertical" android:paddingLeft="10dp" android:text="A" /> <TextView android:id="@+id/t2" android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center_vertical" android:paddingLeft="10dp" android:text="A" /> </LinearLayout>最后是我们的主要Activity:
public class SideCityActivity extends AppCompatActivity { private String TAG = "SideCityActivity:"; private ListView listView; private TextView txtShowCurrentLetter; private SideCityBar sideBar; private List<CityModel> list = new ArrayList(); private SideBarCityAdapter sideBarCityAdapter; private Map<String, Integer> map = new HashMap<>(); private String[] nameStrings = {"$XXOO", "上海", "北京", "杭州", "广州", "南京", "苏州", "深圳", "成都", "重庆", "天津", "宁波", "扬州", "无锡", "福州", "厦门", "武汉", "西安", "沈阳", "大连", "青岛", "济南", "海口", "石家庄", "唐山", "秦皇岛", "邯郸", "邢台", "保定", "张家口", "承德", "沧州", "廊坊", "衡水", "太原", "大同", "阳泉", "长治", "晋城", "朔州", "晋中", "运城", "忻州", "临汾", "吕梁", "呼和浩特", "包头", "乌海", "赤峰", "通辽", "鄂尔多斯", "呼伦贝尔", "兴安盟", "锡林郭勒", "乌兰察布", "巴彦淖尔", "阿拉善", "鞍山", "抚顺", "本溪", "丹东", "锦州", "营口", "阜新", "辽阳", "盘锦", "铁岭", "朝阳", "葫芦岛", "长春", "吉林", "四平", "辽源", "通化", "白山", "松原", "白城", "延边", "哈尔滨", "齐齐哈尔", "鸡西", "鹤岗", "双鸭山", "大庆", "伊春", "佳木斯", "七台河", "牡丹江", "黑河", "绥化", "大兴安岭", "徐州", "常州", "南通", "连云港", "淮安", "盐城", "镇江", "泰州", "宿迁", "温州", "嘉兴", "湖州", "绍兴", "金华", "衢州", "舟山", "台州", "丽水", "合肥", "芜湖", "蚌埠", "淮南", "马鞍山", "淮北", "铜陵", "安庆", "黄山", "滁州", "阜阳", "宿州", "六安", "亳州", "池州", "宣城", "莆田", "三明", "泉州", "漳州", "南平", "龙岩", "宁德", "南昌", "景德镇", "萍乡", "九江", "新余", "鹰潭", "赣州", "吉安", "宜春", "抚州", "上饶", "淄博", "枣庄", "东营", "烟台", "潍坊", "济宁", "泰安", "威海", "日照", "莱芜", "临沂", "德州", "聊城", "滨州", "菏泽", "郑州", "开封", "洛阳", "平顶山", "安阳", "鹤壁", "新乡", "焦作", "濮阳", "许昌", "漯河", "三门峡", "南阳", "商丘", "信阳", "周口", "驻马店", "黄石", "十堰", "宜昌", "襄阳", "鄂州", "荆门", "孝感", "荆州", "黄冈", "咸宁", "随州", "恩施州", "仙桃", "潜江", "天门", "株洲", "湘潭", "衡阳", "邵阳", "岳阳", "常德", "张家界", "益阳", "郴州", "永州", "怀化", "娄底", "湘西", "韶关", "珠海", "汕头", "佛山", "江门", "湛江", "茂名", "肇庆", "惠州", "梅州", "汕尾", "河源", "阳江", "清远", "东莞", "中山", "潮州", "揭阳", "云浮", "南宁", "柳州", "桂林", "梧州", "北海", "防城港", "钦州", "贵港", "玉林", "百色", "贺州", "河池", "自贡", "攀枝花", "泸州", "德阳", "绵阳", "广元", "遂宁", "内江", "乐山", "南充", "眉山", "宜宾", "广安", "达州", "雅安", "巴中", "资阳", "阿坝", "甘孜州", "凉山", "贵阳", "六盘水", "遵义", "安顺", "铜仁地区", "黔西南", "毕节地区", "黔东南", "黔南", "昆明", "曲靖", "玉溪", "保山", "昭通", "楚雄州", "红河", "文山州", "普洱", "西双版纳", "大理州", "德宏", "丽江", "怒江", "迪庆", "临沧", "拉萨", "昌都地区", "山南", "日喀则地区", "那曲", "阿里", "林芝地区", "铜川", "宝鸡", "咸阳", "渭南", "延安", "汉中", "榆林", "安康", "商洛", "兰州", "嘉峪关", "金昌", "白银", "天水", "武威", "张掖", "平凉", "酒泉", "庆阳", "定西", "陇南", "临夏州", "甘南", "西宁", "海东", "海北", "黄南", "果洛", "玉树", "海西", "银川", "石嘴山", "吴忠", "固原", "乌鲁木齐", "克拉玛依", "吐鲁番地区", "哈密地区", "昌吉州", "博尔塔拉", "巴音郭楞", "阿克苏地区", "克孜勒苏", "喀什地区", "和田地区", "伊犁", "塔城地区", "阿勒泰地区", "石河子", "香港", "澳门", "长沙", "三亚", "中卫", "儋州", "保亭", "昌江", "澄迈县", "崇左", "定安县", "东方", "济源", "来宾", "乐东", "陵水", "琼海", "神农架林区", "图木舒克", "屯昌县", "万宁", "文昌", "海南州"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.act_two_sidecity); txtShowCurrentLetter = (TextView) findViewById(R.id.t1); sideBar = (SideCityBar) findViewById(R.id.s1); listView = (ListView) findViewById(R.id.l1); sideBar.setTextView(txtShowCurrentLetter); getAllData(); sideBarCityAdapter = new SideBarCityAdapter(); listView.setAdapter(sideBarCityAdapter); sideBar.setOnChangeLis(new SideCityBar.OnchangeString() { @Override public void getChangeString(String s) { int a1 = getCurrentPosition(s); if (a1 != -1) { listView.setSelection(a1); } else { } } @Override public void setTextShow(boolean b) { } }); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String city = list.get(position).getName(); } }); } /** * list获取所有数据 */ private void getAllData() { list = new ArrayList(); for (int i = 0; i < nameStrings.length; i++) { CityModel cityModel = new CityModel(); cityModel.setName(nameStrings[i]); //将汉字转换为拼音 String pin = PinyinHelper.getShortPinyin(nameStrings[i]); //将拼音字符串转换为大写拼音 String pinD = String.valueOf(pin.charAt(0)).toUpperCase(); //获取大写拼音字符串的第一个字符 char c1 = pinD.charAt(0); if (c1 < 'A' || c1 > 'Z') { cityModel.setFistletter("#"); } else { cityModel.setFistletter(String.valueOf(c1)); } list.add(cityModel); } //将联系人列表的标题字母排序 Collections.sort(list, new Comparator<CityModel>() { @Override public int compare(CityModel o1, CityModel o2) { return o1.getFistletter().compareTo(o2.getFistletter()); } }); //记录字母所在位置 String s1 = list.get(0).getFistletter(); map.put(s1, 0); for (int i = 0; i < list.size(); i++) { String s2 = list.get(i).getFistletter(); if (!s1.equals(s2)) { s1 = s2; map.put(s2, i); } } } private int getCurrentPosition(String s) { if (map.get(s) != null) { return map.get(s); } else { return -1; } } public class SideBarCityAdapter extends BaseAdapter { @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { viewHolder = new ViewHolder(); convertView = LayoutInflater.from(SideCityActivity.this).inflate(R.layout.act_two_sidecity_item, null); viewHolder.txtFirstLetter = (TextView) convertView.findViewById(R.id.t1); viewHolder.txtName = (TextView) convertView.findViewById(R.id.t2); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } viewHolder.txtName.setText(list.get(position).getName()); String s1 = list.get(position).getFistletter(); if (map.get(s1) != null && map.get(s1).equals(position)) { viewHolder.txtFirstLetter.setText(s1); viewHolder.txtFirstLetter.setVisibility(View.VISIBLE); } else { viewHolder.txtFirstLetter.setVisibility(View.GONE); } return convertView; } public class ViewHolder { TextView txtFirstLetter, txtName; } } }