Android 仿美团网,探索ListView的A-Z字母排序功能实现选择城市

本文主要是介绍Android 仿美团网,探索ListView的A-Z字母排序功能实现选择城市,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

记得在我刚开始接触到美团网的时候就对美团网这个城市定位、选择城市功能很感兴趣,觉得它做得很棒。有如下几个点:
一:实现ListView的A-Z字母排序功能
二:根据输入框的输入值改变来过滤搜索结果,如果输入框里面的值为空,更新为原来的列表,否则为过滤数据列表
三:汉字转成拼音的功能,很多时候实现联系人或者城市列表等实现A-Z的排序功能,我们可以直接从数据库中获取他的汉字拼音,而对于一般的数据,我们怎么实现A-Z的排序,这里我使用了PinYin4j.jar将汉字转换为拼音.
按照惯例先来看一下最终效果图:
这里写图片描述
接下来分析下整个功能模块的布局结构:
(1)首先一个带删除按钮的EditText,我们在输入框中输入我们查找的城市可以自动过滤出最终的结果,当输入框中没有数据自动替换到原来的数据列表;
(2)中间是当前定位的城市和热门的城市,其中热门城市使用到了GridView;
(3)下面是一个ListView用来显示数据列表,右侧是一个字母索引表,当我们点击不同的字母,ListView会定位到该字母地方
现在我们来看下项目结构图
这里写图片描述
按照项目中类的顺序来一一介绍
1.PinYin4j.jar用于将汉字转换为拼音,你还可以使用其他方式将汉子转换为拼音,我之前有介绍过,这里就不详讲啦,探索PinYin4j.jar将汉字转换为拼音的基本用法
2.CitySortModel一个实体类,一个显示的城市和相对应的拼音首字母

package com.adan.selectcitydome.view;public class CitySortModel {private String name;//显示的数据private String sortLetters;//显示数据拼音的首字母public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSortLetters() {return sortLetters;}public void setSortLetters(String sortLetters) {this.sortLetters = sortLetters;}
}

3.EditTextWithDel类是自定义的一个带清除功能的输入框控件,也可以用Android原生的EditText,这个类上一篇博客有介绍,这里就不贴上代码了Android 带清除功能的输入框控件EditTextWithDel
4.MyGridView类就是自定义GridView,主要是解决了在热门城市中嵌套Grideview的显示不完全的问题

package com.adan.selectcitydome.view;import android.content.Context;
import android.util.AttributeSet;
import android.widget.GridView;/*** 自定义GridView,解决嵌套Grideview的显示不完全的问题*/
public class MyGridView extends GridView {public MyGridView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public MyGridView(Context context, AttributeSet attrs) {super(context, attrs);}public MyGridView(Context context) {super(context);}/*** 其中onMeasure函数决定了组件显示的高度与宽度;* makeMeasureSpec函数中第一个函数决定布局空间的大小,第二个参数是布局模式* MeasureSpec.AT_MOST的意思就是子控件需要多大的控件就扩展到多大的空间*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);super.onMeasure(widthMeasureSpec, expandSpec);}}

5.PinyinComparator类用来对ListView中的数据根据A-Z进行排序,前面两个if判断主要是将不是以汉字开头的数据放在后面

package com.adan.selectcitydome.view;import java.util.Comparator;/*** 用来对ListView中的数据根据A-Z进行排序,前面两个if判断主要是将不是以汉字开头的数据放在后面*/
public class PinyinComparator implements Comparator<CitySortModel> {public int compare(CitySortModel o1, CitySortModel o2) {//这里主要是用来对ListView里面的数据根据ABCDEFG...来排序if (o1.getSortLetters().equals("@")|| o2.getSortLetters().equals("#")) {return -1;} else if (o1.getSortLetters().equals("#")|| o2.getSortLetters().equals("@")) {return 1;} else {return o1.getSortLetters().compareTo(o2.getSortLetters());}}
}

6.PinyinUtils类,就是第一点所讲的PinYin4j.jar用于将汉字转换为拼音啦,这里就不粘贴代码啦
7.SideBar类就是ListView右侧的字母索引View,我们需要使用setTextView(TextView mTextDialog)来设置用来显示当前按下的字母的TextView,以及使用setOnTouchingLetterChangedListener方法来设置回调接口,在回调方法onTouchingLetterChanged(String s)中来处理不同的操作

package com.adan.selectcitydome.view;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;import com.adan.selectcitydome.R;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** ListView右侧的字母索引View*/
public class SideBar extends View {public static String[] INDEX_STRING = {"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 OnTouchingLetterChangedListener onTouchingLetterChangedListener;private List<String> letterList;private int choose = -1;private Paint paint = new Paint();private TextView mTextDialog;public SideBar(Context context) {this(context, null);}public SideBar(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SideBar(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}private void init() {setBackgroundColor(Color.parseColor("#F0F0F0"));letterList = Arrays.asList(INDEX_STRING);}protected void onDraw(Canvas canvas) {super.onDraw(canvas);int height = getHeight();// 获取对应高度int width = getWidth();// 获取对应宽度int singleHeight = height / letterList.size();// 获取每一个字母的高度for (int i = 0; i < letterList.size(); i++) {paint.setColor(Color.parseColor("#606060"));paint.setTypeface(Typeface.DEFAULT_BOLD);paint.setAntiAlias(true);paint.setTextSize(20);// 选中的状态if (i == choose) {paint.setColor(Color.parseColor("#4F41FD"));paint.setFakeBoldText(true);}// x坐标等于中间-字符串宽度的一半.float xPos = width / 2 - paint.measureText(letterList.get(i)) / 2;float yPos = singleHeight * i + singleHeight / 2;canvas.drawText(letterList.get(i), xPos, yPos, paint);paint.reset();// 重置画笔}}@Overridepublic 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() * letterList.size());// 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数.switch (action) {case MotionEvent.ACTION_UP:setBackgroundColor(Color.parseColor("#F0F0F0"));choose = -1;invalidate();if (mTextDialog != null) {mTextDialog.setVisibility(View.GONE);}break;default:setBackgroundResource(R.drawable.sidebar_background);if (oldChoose != c) {if (c >= 0 && c < letterList.size()) {if (listener != null) {listener.onTouchingLetterChanged(letterList.get(c));}if (mTextDialog != null) {mTextDialog.setText(letterList.get(c));mTextDialog.setVisibility(View.VISIBLE);}choose = c;invalidate();}}break;}return true;}public void setIndexText(ArrayList<String> indexStrings) {this.letterList = indexStrings;invalidate();}/*** 为SideBar设置显示当前按下的字母的TextView** @param mTextDialog*/public void setTextView(TextView mTextDialog) {this.mTextDialog = mTextDialog;}/*** 向外公开的方法** @param onTouchingLetterChangedListener*/public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener onTouchingLetterChangedListener) {this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;}/*** 接口*/public interface OnTouchingLetterChangedListener {void onTouchingLetterChanged(String s);}}

8.CityAdapter就是热门城市中GridView的适配器

package com.adan.selectcitydome;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;import java.util.List;/*** @author: xiaolijuan* @description:* @projectName: SelectCityDome* @date: 2016-03-01* @time: 17:25*/
public class CityAdapter extends ArrayAdapter<String> {/*** 需要渲染的item布局文件*/private int resource;public CityAdapter(Context context, int textViewResourceId, List<String> objects) {super(context, textViewResourceId, objects);resource = textViewResourceId;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {LinearLayout layout = null;if (convertView == null) {layout = (LinearLayout) LayoutInflater.from(getContext()).inflate(resource, null);} else {layout = (LinearLayout) convertView;}Button name = (Button) layout.findViewById(R.id.tv_city);name.setText(getItem(position));return layout;}
}

9.SortAdapter 数据的适配器类,这里我们需要用到的就是SectionIndexer接口,它能够有效地帮助我们对分组进行控制。使用SectionIndexer接口需要实现三个方法:getSectionForPosition(int position),getPositionForSection(int section),getSections(),我们只需要自行实现前面两个方法:
(一)getSectionForPosition(int position)是根据ListView的position来找出当前位置所在的分组
getPositionForSection(int section)就是根据首字母的Char值来获取在该ListView中第一次出现该首字母的位置,也就是当前分组所在的位置

package com.adan.selectcitydome;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.SectionIndexer;
import android.widget.TextView;import com.adan.selectcitydome.view.CitySortModel;import java.util.List;public class SortAdapter extends BaseAdapter implements SectionIndexer {private List<CitySortModel> list = null;private Context mContext;public SortAdapter(Context mContext, List<CitySortModel> list) {this.mContext = mContext;this.list = list;}/*** 当ListView数据发生变化时,调用此方法来更新ListView** @param list*/public void updateListView(List<CitySortModel> list) {this.list = list;notifyDataSetChanged();}public int getCount() {return this.list.size();}public Object getItem(int position) {return list.get(position);}public long getItemId(int position) {return position;}public View getView(final int position, View view, ViewGroup arg2) {ViewHolder viewHolder = null;final CitySortModel mContent = list.get(position);if (view == null) {viewHolder = new ViewHolder();view = LayoutInflater.from(mContext).inflate(R.layout.item_select_city, null);viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_city_name);view.setTag(viewHolder);viewHolder.tvLetter = (TextView) view.findViewById(R.id.tv_catagory);} else {viewHolder = (ViewHolder) view.getTag();}int section = getSectionForPosition(position);if (position == getPositionForSection(section)) {viewHolder.tvLetter.setVisibility(View.VISIBLE);viewHolder.tvLetter.setText(mContent.getSortLetters());} else {viewHolder.tvLetter.setVisibility(View.GONE);}viewHolder.tvTitle.setText(this.list.get(position).getName());return view;}final static class ViewHolder {TextView tvLetter;TextView tvTitle;}public int getSectionForPosition(int position) {return list.get(position).getSortLetters().charAt(0);}public int getPositionForSection(int section) {for (int i = 0; i < getCount(); i++) {String sortStr = list.get(i).getSortLetters();char firstChar = sortStr.toUpperCase().charAt(0);if (firstChar == section) {return i;}}return -1;}@Overridepublic Object[] getSections() {return null;}
}

10.MainActivity 对EditTextWithDel设置addTextChangedListener监听,当输入框内容发生变化根据里面的值过滤ListView,里面的值为空显示原来的列表和给ListView添加表头等

package com.adan.selectcitydome;import android.app.Activity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;import com.adan.selectcitydome.view.CitySortModel;
import com.adan.selectcitydome.view.EditTextWithDel;
import com.adan.selectcitydome.view.PinyinComparator;
import com.adan.selectcitydome.view.PinyinUtils;
import com.adan.selectcitydome.view.SideBar;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;public class MainActivity extends Activity {private ListView sortListView;private SideBar sideBar;private TextView dialog, mTvTitle;private SortAdapter adapter;private EditTextWithDel mEtCityName;private List<CitySortModel> SourceDateList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initViews();}private void initViews() {mEtCityName = (EditTextWithDel) findViewById(R.id.et_search);sideBar = (SideBar) findViewById(R.id.sidrbar);dialog = (TextView) findViewById(R.id.dialog);mTvTitle = (TextView) findViewById(R.id.tv_title);sortListView = (ListView) findViewById(R.id.country_lvcountry);initDatas();initEvents();setAdapter();}private void setAdapter() {SourceDateList = filledData(getResources().getStringArray(R.array.provinces));Collections.sort(SourceDateList, new PinyinComparator());adapter = new SortAdapter(this, SourceDateList);sortListView.addHeaderView(initHeadView());sortListView.setAdapter(adapter);}private void initEvents() {//设置右侧触摸监听sideBar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() {@Overridepublic void onTouchingLetterChanged(String s) {//该字母首次出现的位置int position = adapter.getPositionForSection(s.charAt(0));if (position != -1) {sortListView.setSelection(position + 1);}}});//ListView的点击事件sortListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {mTvTitle.setText(((CitySortModel) adapter.getItem(position - 1)).getName());Toast.makeText(getApplication(), ((CitySortModel) adapter.getItem(position)).getName(), Toast.LENGTH_SHORT).show();}});//根据输入框输入值的改变来过滤搜索mEtCityName.addTextChangedListener(new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {//当输入框里面的值为空,更新为原来的列表,否则为过滤数据列表filterData(s.toString());}@Overridepublic void afterTextChanged(Editable s) {}});}private void initDatas() {sideBar.setTextView(dialog);}private View initHeadView() {View headView = getLayoutInflater().inflate(R.layout.headview, null);GridView mGvCity = (GridView) headView.findViewById(R.id.gv_hot_city);String[] datas = getResources().getStringArray(R.array.city);ArrayList<String> cityList = new ArrayList<>();for (int i = 0; i < datas.length; i++) {cityList.add(datas[i]);}CityAdapter adapter = new CityAdapter(getApplicationContext(), R.layout.gridview_item, cityList);mGvCity.setAdapter(adapter);return headView;}/*** 根据输入框中的值来过滤数据并更新ListView** @param filterStr*/private void filterData(String filterStr) {List<CitySortModel> mSortList = new ArrayList<>();if (TextUtils.isEmpty(filterStr)) {mSortList = SourceDateList;} else {mSortList.clear();for (CitySortModel sortModel : SourceDateList) {String name = sortModel.getName();if (name.toUpperCase().indexOf(filterStr.toString().toUpperCase()) != -1 || PinyinUtils.getPingYin(name).toUpperCase().startsWith(filterStr.toString().toUpperCase())) {mSortList.add(sortModel);}}}// 根据a-z进行排序Collections.sort(mSortList, new PinyinComparator());adapter.updateListView(mSortList);}private List<CitySortModel> filledData(String[] date) {List<CitySortModel> mSortList = new ArrayList<>();ArrayList<String> indexString = new ArrayList<>();for (int i = 0; i < date.length; i++) {CitySortModel sortModel = new CitySortModel();sortModel.setName(date[i]);String pinyin = PinyinUtils.getPingYin(date[i]);String sortString = pinyin.substring(0, 1).toUpperCase();if (sortString.matches("[A-Z]")) {sortModel.setSortLetters(sortString.toUpperCase());if (!indexString.contains(sortString)) {indexString.add(sortString);}}mSortList.add(sortModel);}Collections.sort(indexString);sideBar.setIndexText(indexString);return mSortList;}
}

布局文件就不贴出来了,有兴趣的可以下载代码,有什么疑问的,请在下面留言,感谢各位^_^
本项目改造自http://blog.csdn.net/xiaanming/article/details/12684155
源代码请戳这里:Android 仿美团网,探索ListView的A-Z字母排序功能实现选择省份或者城市

这篇关于Android 仿美团网,探索ListView的A-Z字母排序功能实现选择城市的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/154801

相关文章

如何选择适合孤独症兄妹的学校?

在探索适合孤独症儿童教育的道路上,每一位家长都面临着前所未有的挑战与抉择。当这份责任落在拥有孤独症兄妹的家庭肩上时,选择一所能够同时满足两个孩子特殊需求的学校,更显得尤为关键。本文将探讨如何为这样的家庭做出明智的选择,并介绍星贝育园自闭症儿童寄宿制学校作为一个值得考虑的选项。 理解孤独症儿童的独特性 孤独症,这一复杂的神经发育障碍,影响着儿童的社交互动、沟通能力以及行为模式。对于拥有孤独症兄

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

usaco 1.2 Name That Number(数字字母转化)

巧妙的利用code[b[0]-'A'] 将字符ABC...Z转换为数字 需要注意的是重新开一个数组 c [ ] 存储字符串 应人为的在末尾附上 ‘ \ 0 ’ 详见代码: /*ID: who jayLANG: C++TASK: namenum*/#include<stdio.h>#include<string.h>int main(){FILE *fin = fopen (

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影