admin 管理员组

文章数量: 887016

Android Studio 仿首页美团切换城市(ListView)+数据库帮助类SQLiteOpenHelper+LetterView(字母排序)

一、测试
1.首页美团切换城市:

2.实现:

二、项目:

三、理解
1、高德地图定位的使用 1.添加依赖库 2.启动显示定位城市名的代码,可以在高德地图官网开放平台获取,点击链接:Android 高德地图(带有定位和点击显示经度纬度)
(1)依赖库:

(1).1 在AndroidManifest.xml中添加:
(1).1.1 添加定位权限

    <!-- 请求网络 --><uses-permission android:name="android.permission.INTERNET"/><!-- 需要运行时注册的权限 --><!-- 用于进行网络定位 --><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/><!-- 用于访问GPS定位 --><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

(1).1.2 在application上添加,1.输入高德地图的key 2.定位需要的服务

        <!-- 设置key --><meta-dataandroid:name="com.amap.api.v2.apikey"android:value="请输入高德开放平台申请的Android端API KEY"/><!--定位需要的服务 适配Android Q需要加上android:foregroundServiceType="location"--><serviceandroid:name="com.amap.api.location.APSService"android:foregroundServiceType="location" />

(2)启动显示定位城市名:

    private void initLocation() {//初始化clientlocationClient = new AMapLocationClient(mContext);locationOption = getDefaultOption();//设置定位参数locationClient.setLocationOption(locationOption);// 设置定位监听locationClient.setLocationListener(locationListener);}/*** 默认的定位参数** @author hongming.wang* @since 2.8.0*/private AMapLocationClientOption getDefaultOption() {AMapLocationClientOption mOption = new AMapLocationClientOption();mOption.setLocationMode(AMapLocationMode.Hight_Accuracy);//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式mOption.setGpsFirst(false);//可选,设置是否gps优先,只在高精度模式下有效。默认关闭mOption.setHttpTimeOut(30000);//可选,设置网络请求超时时间。默认为30秒。在仅设备模式下无效mOption.setInterval(2000);//可选,设置定位间隔。默认为2秒mOption.setNeedAddress(true);//可选,设置是否返回逆地理地址信息。默认是truemOption.setOnceLocation(false);//可选,设置是否单次定位。默认是falsemOption.setOnceLocationLatest(false);//可选,设置是否等待wifi刷新,默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用AMapLocationClientOption.setLocationProtocol(AMapLocationProtocol.HTTP);//可选, 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTPmOption.setSensorEnable(false);//可选,设置是否使用传感器。默认是falsemOption.setWifiScan(true); //可选,设置是否开启wifi扫描。默认为true,如果设置为false会同时停止主动刷新,停止以后完全依赖于系统刷新,定位位置可能存在误差mOption.setLocationCacheEnable(true); //可选,设置是否使用缓存定位,默认为truemOption.setGeoLanguage(AMapLocationClientOption.GeoLanguage.DEFAULT);//可选,设置逆地理信息的语言,默认值为默认语言(根据所在地区选择语言)return mOption;}/*** 定位监听*/AMapLocationListener locationListener = new AMapLocationListener() {@Overridepublic void onLocationChanged(AMapLocation location) {if (null != location) {//errorCode等于0代表定位成功,其他的为定位失败,具体的可以参照官网定位错误码说明if (location.getErrorCode() == 0) {currentCity = location.getCity().substring(0, location.getCity().length() - 1);tvLocate.setText("当前定位城市");tvCurrentLocateCity.setVisibility(View.VISIBLE);tvCurrentLocateCity.setText(currentCity);pbLocate.setVisibility(View.GONE);} else {//定位失败tvLocate.setText("未定位到城市,请选择");tvCurrentLocateCity.setVisibility(View.VISIBLE);tvCurrentLocateCity.setText("重新选择");pbLocate.setVisibility(View.GONE);return;}} else {Toast.makeText(mContext, "定位失败", Toast.LENGTH_SHORT).show();}}};/*** 开始定位** @author hongming.wang* @since 2.8.0*/private void startLocation() {//根据控件的选择,重新设置定位参数
//        resetOption();// 设置定位参数locationClient.setLocationOption(locationOption);// 启动定位locationClient.startLocation();}

2、主活动适配器Adapter,CityListAdapter类有六个视图:

1.搜索结果列表展示,搜索城市名的拼音也可以
2.显示当前定位城市名,我使用的是高德地图的
3.最近访问城市
4.热门城市
5.全部城市,仅展示“全部城市这四个字”
6.数据库中所有的城市的名字展示

(1)搜索结果列表展示适配器Adapter,SearchResultAdapter.java

public class SearchResultAdapter extends BaseAdapter {private List<City> mSearchList;private Context mContext;private LayoutInflater mInflater;public SearchResultAdapter(Context context,List<City> searchList){this.mSearchList=searchList;this.mContext=context;mInflater=LayoutInflater.from(mContext);}@Overridepublic int getCount() {return mSearchList.size();}@Overridepublic Object getItem(int position) {return mSearchList.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {ViewHolder viewHolder=null;if(convertView==null){viewHolder=new ViewHolder();convertView=mInflater.inflate(R.layout.item_search_list,null);viewHolder.tvCityName=(TextView) convertView.findViewById(R.id.tv_city_name);convertView.setTag(viewHolder);}else{viewHolder =(ViewHolder) convertView.getTag();}viewHolder.tvCityName.setText(mSearchList.get(position).getName());viewHolder.tvCityName.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(mContext,mSearchList.get(position).getName(),Toast.LENGTH_SHORT).show();}});return convertView;}class ViewHolder{LinearLayout ll_item;TextView tvCityName;}
}

item_search_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=""android:id="@+id/ll_item"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:id="@+id/tv_city_name"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="12dip"android:textColor="#FF767676"android:textSize="16dp" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:layout_marginLeft="10dp"android:layout_marginRight="25dp"android:background="#E2E2E2" />
</LinearLayout>

(2)显示当前定位城市名

if (viewType == 0) {//view类型为0,也就是:当前定位城市的布局convertView = View.inflate(mContext, R.layout.item_location_city, null);tvLocate = (TextView) convertView.findViewById(R.id.tv_locate);tvCurrentLocateCity = (TextView) convertView.findViewById(R.id.tv_current_locate_city);pbLocate = (ProgressBar) convertView.findViewById(R.id.pb_loacte);if (!isNeedRefresh) {tvLocate.setText("当前定位城市");tvCurrentLocateCity.setVisibility(View.VISIBLE);tvCurrentLocateCity.setText(currentCity);pbLocate.setVisibility(View.GONE);} else {startLocation();}tvCurrentLocateCity.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {pbLocate.setVisibility(View.VISIBLE);tvLocate.setText("正在定位");tvCurrentLocateCity.setVisibility(View.GONE);startLocation();}});

item_location_city.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android=""android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/tv_locate"android:layout_width="wrap_content"android:layout_height="40dp"android:layout_marginLeft="10dp"android:gravity="center_vertical"android:text="正在定位"android:textColor="#000000"android:textSize="16sp" /><TextViewandroid:id="@+id/tv_current_locate_city"android:layout_width="80dp"android:layout_height="40dp"android:layout_alignParentRight="true"android:layout_marginRight="40dp"android:background="#FF32B9AA"android:clickable="true"android:focusable="false"android:gravity="center"android:text="北京"android:textColor="#ffffff"android:textSize="16sp"android:visibility="gone" /><ProgressBarandroid:id="@+id/pb_loacte"style="?android:attr/progressBarStyleSmall"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_marginTop="13dp"android:layout_marginRight="64dip"android:layout_marginBottom="5dp" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:layout_marginLeft="10dp"android:layout_marginTop="50dp"android:layout_marginRight="25dp"android:background="#E2E2E2" />
</RelativeLayout>

(3)最近访问城市

          else if (viewType == 1) {//最近访问城市convertView = View.inflate(mContext, R.layout.item_recent_visit_city, null);TextView tvRecentVisitCity = (TextView) convertView.findViewById(R.id.tv_recent_visit_city);tvRecentVisitCity.setText("最近访问城市");MyGridView gvRecentVisitCity = (MyGridView) convertView.findViewById(R.id.gv_recent_visit_city);gvRecentVisitCity.setAdapter(new RecentVisitCityAdapter(mContext, mRecentCityList));

(3).1 自定义的MyGridView类:

public class MyGridView extends GridView {public MyGridView(Context context, AttributeSet attrs) {super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int measureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);super.onMeasure(widthMeasureSpec, measureSpec);}
}

(3).2 item_recent_visit_city.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=""android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/tv_recent_visit_city"android:layout_width="wrap_content"android:layout_height="40dp"android:layout_marginLeft="10dp"android:gravity="center_vertical"android:text="热门城市"android:textColor="#000000"android:textSize="16sp" /><com.example.citylist.view.MyGridViewandroid:id="@+id/gv_recent_visit_city"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="20dp"android:layout_marginTop="15dp"android:layout_marginRight="30dp"android:horizontalSpacing="20dp"android:listSelector="@android:color/transparent"android:numColumns="3"android:verticalSpacing="10dp" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:layout_marginLeft="10dp"android:layout_marginTop="15dp"android:layout_marginRight="25dp"android:background="#E2E2E2" />
</LinearLayout>

(3).3 最后访问城市适配器Adapter,RecentVisitCityAdapter.java

public class RecentVisitCityAdapter extends BaseAdapter {private List<String> mRecentVisitCityList;private LayoutInflater mInflater;private Context mContext;public RecentVisitCityAdapter(Context context, List<String> recentVisitCityList) {this.mRecentVisitCityList=recentVisitCityList;this.mContext=context;mInflater=LayoutInflater.from(mContext);}@Overridepublic int getCount() {return mRecentVisitCityList.size();}@Overridepublic Object getItem(int position) {return mRecentVisitCityList.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {ViewHolder viewHolder=null;if(convertView==null){viewHolder=new ViewHolder();convertView=mInflater.inflate(R.layout.item_city,null);viewHolder.tvCityName=(TextView) convertView.findViewById(R.id.tv_city_name);convertView.setTag(viewHolder);}else{viewHolder=(ViewHolder) convertView.getTag();}viewHolder.tvCityName.setText(mRecentVisitCityList.get(position));viewHolder.tvCityName.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(mContext,mRecentVisitCityList.get(position)+"",0).show();}});return convertView;}class ViewHolder{TextView tvCityName;}
}

(3).3.1 item_city.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=""android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/item_selector"android:gravity="center"android:orientation="vertical"><TextViewandroid:id="@+id/tv_city_name"android:layout_width="75dp"android:layout_height="35dp"android:gravity="center"android:text="北京"android:textColor="#000000"android:textSize="16sp" />
</LinearLayout>

item_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android=""><item android:state_pressed="false"><shape><solid android:color="#FFFFFF" /><corners android:radius="2dp" /></shape></item><item android:state_pressed="true"><shape><solid android:color="#90f0f0f0" /><corners android:radius="2dp" /></shape></item>
</selector>

(4)热门城市

          else if (viewType == 2) {//热门城市convertView = View.inflate(mContext, R.layout.item_recent_visit_city, null);TextView tvRecentVisitCity = (TextView) convertView.findViewById(R.id.tv_recent_visit_city);tvRecentVisitCity.setText("热门城市");MyGridView gvRecentVisitCity = (MyGridView) convertView.findViewById(R.id.gv_recent_visit_city);gvRecentVisitCity.setAdapter(new HotCityAdapter(mContext, mHotCityList));

(4).1 item_recent_visit_city.xml(上面有)
(4).2 自定义MyGridView代码(上面也有)
(4).3 热门城市适配器Adapter,HotCityAdapter.java

public class HotCityAdapter extends BaseAdapter {private List<City> mHotCityList;private LayoutInflater mInflater;private Context mContext;public HotCityAdapter(Context context, List<City> hotCityList) {this.mHotCityList = hotCityList;this.mContext = context;mInflater = LayoutInflater.from(mContext);}@Overridepublic int getCount() {return mHotCityList.size();}@Overridepublic Object getItem(int position) {return mHotCityList.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;if (convertView == null) {viewHolder = new ViewHolder();convertView = mInflater.inflate(R.layout.item_city, null);viewHolder.tvCityName = (TextView) convertView.findViewById(R.id.tv_city_name);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}viewHolder.tvCityName.setText(mHotCityList.get(position).getName());viewHolder.tvCityName.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(mContext,mHotCityList.get(position).getName() + "", 0).show();}});return convertView;}class ViewHolder {TextView tvCityName;}
}

(4).4 item_city.xml(上面有)
(5)全部城市,仅展示“全部城市这四个字”

          else if (viewType == 3) {convertView = View.inflate(mContext, R.layout.item_all_city_textview, null);

item_all_city_textview.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android=""android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center_vertical"><TextViewandroid:id="@+id/tv_text"android:layout_width="wrap_content"android:layout_height="40dp"android:layout_marginLeft="10dip"android:gravity="center_vertical"android:text="全部城市"android:textColor="#000000"android:textSize="16sp" />
</RelativeLayout>

(6)数据库中所有的城市的名字展示

          else {//数据库中所有的城市的名字展示if (convertView == null) {viewHolder = new ViewHolder();convertView = View.inflate(mContext, R.layout.item_city_list, null);viewHolder.tvAlpha = (TextView) convertView.findViewById(R.id.tv_alpha);viewHolder.tvCityName = (TextView) convertView.findViewById(R.id.tv_city_name);viewHolder.llMain = (LinearLayout) convertView.findViewById(R.id.ll_main);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}if (position >= 1) {viewHolder.tvCityName.setText(mAllCityList.get(position).getName());viewHolder.llMain.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(mContext, mAllCityList.get(position).getName(), Toast.LENGTH_SHORT).show();}});String currentStr = getAlpha(mAllCityList.get(position).getPinyin());String previewStr = (position - 1) >= 0 ? getAlpha(mAllCityList.get(position - 1).getPinyin()) : " ";//如果当前的条目的城市名字的拼音的首字母和其前一条条目的城市的名字的拼音的首字母不相同,则将布局中的展示字母的TextView展示出来if (!previewStr.equals(currentStr)) {viewHolder.tvAlpha.setVisibility(View.VISIBLE);viewHolder.tvAlpha.setText(currentStr);} else {viewHolder.tvAlpha.setVisibility(View.GONE);}}}

item_city_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=""android:id="@+id/ll_main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/tv_alpha"android:layout_width="match_parent"android:layout_height="26dp"android:background="#E5E5E5"android:padding="5dp"android:textColor="#787878"android:visibility="gone" /><TextViewandroid:id="@+id/tv_city_name"android:layout_width="match_parent"android:layout_height="40dp"android:background="#F9F9F9"android:padding="6dp"android:textColor="#000000"android:textSize="16dp"android:text="北京"/><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:layout_marginLeft="10dp"android:layout_marginRight="25dp"android:background="#E2E2E2" />
</LinearLayout>

3、自定义MyLetterView的视图代码:

public class MyLetterView extends View {private Paint mPaint;private boolean isShowBg = false;// 用于区分是否显示view的背景private OnSlidingListener mOnSlidingListener;// 滑动此View的监听器private int choose = -1;// 用于标记当前所选中的位置private TextView mTvDialog;//用于接受从activity中传过来的,中间用于展示字母的textView//需要展示的数据private String[] letter = {"定位", "最近", "热门", "全部", "A", "B", "C", "D","E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q","R", "S", "T", "W", "X", "Y", "Z"};public MyLetterView(Context context) {super(context);}public MyLetterView(Context context, AttributeSet attrs) {super(context, attrs);initPaint();}public MyLetterView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}private void initPaint() {mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setTextSize(30);mPaint.setColor(Color.parseColor("#000000"));}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//当此View被按下时所显示的背景颜色if (isShowBg) {canvas.drawColor(Color.parseColor("#40000000"));}//计算每个字符所占的高度float singleHeight = getHeight() / letter.length;int width = getWidth();for (int i = 0; i < letter.length; i++) {String text = letter[i];float xPosition = width / 2 - mPaint.measureText(text) / 2;float yPosition = singleHeight * i + singleHeight;//通过不断的改变yPosition将数组中的数据一个一个绘制到自定义的View中canvas.drawText(text, xPosition, yPosition, mPaint);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {int action = event.getAction();int position = (int) (event.getY() / getHeight() * letter.length);int oldChoose = choose;switch (action) {case MotionEvent.ACTION_DOWN:isShowBg = true;if (oldChoose != position && mOnSlidingListener != null) {if (position > 0 && position < letter.length) {//将滑动到的字母传递到activity中mOnSlidingListener.sliding(letter[position]);choose = position;if (mTvDialog != null) {mTvDialog.setVisibility(View.VISIBLE);mTvDialog.setText(letter[position]);}}invalidate();}break;case MotionEvent.ACTION_MOVE:isShowBg = true;if (oldChoose != position && mOnSlidingListener != null) {if (position >= 0 && position < letter.length) {mOnSlidingListener.sliding(letter[position]);choose = position;if (mTvDialog != null) {mTvDialog.setVisibility(View.VISIBLE);mTvDialog.setText(letter[position]);}}invalidate();}break;case MotionEvent.ACTION_UP:isShowBg = false;choose = -1;if (mTvDialog != null) {mTvDialog.setVisibility(View.GONE);}invalidate();break;}return true;}//MyLetterView的一个滑动的监听public void setOnSlidingListener(OnSlidingListener mOnSlidingListener) {this.mOnSlidingListener = mOnSlidingListener;}public interface OnSlidingListener {public void sliding(String str);}public void setTextView(TextView tvDialog) {mTvDialog = tvDialog;}
}

4、在assets目录放在meituan_cities.db,点击链接直接下载:下载meituan_cities.db

meituan_cities.db打开看看:

5、数据库帮助类SQLiteOpenHelper有两种:

1.最近访问城市
2.所有的城市

(1)最近访问城市CitySqliteOpenHelper.java

public class CitySqliteOpenHelper extends SQLiteOpenHelper {public static final String DB_ALL_CITY_NAME = "city";public static final int version = 1;public CitySqliteOpenHelper(Context context) {super(context, DB_ALL_CITY_NAME, null, version);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("CREATE TABLE IF NOT EXISTS recentcity (" +"id integer primary key autoincrement," +" name varchar(40)," +" date INTEGER)");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
}

(2)所有的城市AllCitySqliteOpenHelper.java

public class AllCitySqliteOpenHelper extends SQLiteOpenHelper {// 数据库文件目标存放路径为系统默认位置,com.droid 是你的包名private static String DB_PATH = "/data/data/com.example.citylist/databases/";private static String DB_NAME = "meituan_cities.db";private Context mContext;private static String ASSETS_NAME = "meituan_cities.db";private static final int DB_VERSION = 3;public AllCitySqliteOpenHelper(Context context, String name, CursorFactory factory,int version) {// 必须通过super调用父类当中的构造函数super(context, name, null, version);this.mContext = context;}public AllCitySqliteOpenHelper(Context context, String name, int version) {this(context, name, null, version);}public AllCitySqliteOpenHelper(Context context, String name) {this(context, name, DB_VERSION);}public AllCitySqliteOpenHelper(Context context) {this(context, DB_PATH + DB_NAME);}public void createDataBase() throws IOException {boolean dbExist = checkDataBase();if (dbExist) {// 数据库已存在,do nothing.} else {// 创建数据库try {File dir = new File(DB_PATH);if (!dir.exists()) {dir.mkdirs();}File dbf = new File(DB_PATH + DB_NAME);if (dbf.exists()) {dbf.delete();}//通过openOrCreateDatabase方法将assets目录下的数据库,创建到系统默认的地方SQLiteDatabase.openOrCreateDatabase(dbf, null);// 复制asseets中的db文件到DB_PATH下copyDataBase();} catch (IOException e) {throw new Error("数据库创建失败");}}}// 检查数据库是否有效private boolean checkDataBase() {SQLiteDatabase checkDB = null;String myPath = DB_PATH + DB_NAME;try {checkDB = SQLiteDatabase.openDatabase(myPath, null,SQLiteDatabase.OPEN_READONLY);} catch (SQLiteException e) {// database does't exist yet.}if (checkDB != null) {checkDB.close();}return checkDB != null ? true : false;}//将assets目录下的城市的数据复制到所创建的数据库下private void copyDataBase() throws IOException {// Open your local db as the input streamInputStream myInput = mContext.getAssets().open(ASSETS_NAME);// Path to the just created empty dbString outFileName = DB_PATH + DB_NAME;// Open the empty db as the output streamOutputStream myOutput = new FileOutputStream(outFileName);// transfer bytes from the inputfile to the outputfilebyte[] buffer = new byte[1024];int length;while ((length = myInput.read(buffer)) > 0) {myOutput.write(buffer, 0, length);}// Close the streamsmyOutput.flush();myOutput.close();myInput.close();}@Overridepublic void onCreate(SQLiteDatabase db) {}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
}

6、实体类City.java

public class City {private String name;//城市的名字private String pinyin;//城市名字的拼音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;}
}

7、将字符串的中文转化为拼音,其他字符不变,需要添加拼音jar文件,直接下载,点击链接:下载pinyin4j-2.5.0.jar
PingYinUtil.java

public class PingYinUtil {/*** 将字符串中的中文转化为拼音,其他字符不变*/public static String getPingYin(String inputString) {HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();format.setCaseType(HanyuPinyinCaseType.LOWERCASE);format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);format.setVCharType(HanyuPinyinVCharType.WITH_V);Pattern p = Patternpile("^[\u4E00-\u9FA5A-Za-z_]+$");Matcher matcher = p.matcher(inputString.substring(0, 1));if (matcher.find()) {char[] input = inputString.trim().toCharArray();String output = "";try {for (int i = 0; i < input.length; i++) {if (java.lang.Character.toString(input[i]).matches("[\\u4E00-\\u9FA5]+")) {String[] temp = PinyinHelper.toHanyuPinyinStringArray(input[i], format);output += temp[0];} elseoutput += java.lang.Character.toString(input[i]);}} catch (BadHanyuPinyinOutputFormatCombination e) {e.printStackTrace();}return output;} else {return "";}}public static String converterToFirstSpell(String chines) {String pinyinName = "";char[] nameChar = chines.toCharArray();HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();defaultFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);for (int i = 0; i < nameChar.length; i++) {if (nameChar[i] > 128) {try {pinyinName += PinyinHelper.toHanyuPinyinStringArray(nameChar[i], defaultFormat)[0].charAt(0);} catch (BadHanyuPinyinOutputFormatCombination e) {e.printStackTrace();}} else {pinyinName += nameChar[i];}}return pinyinName;}
}

四、使用
1.初始化所有城市的适配器Adapter,完整的CityListAdapter.java

public class CityListAdapter extends BaseAdapter {private Context mContext;private List<City> mAllCityList;private List<City> mHotCityList;private List<String> mRecentCityList;public HashMap<String, Integer> alphaIndexer;// 存放存在的汉语拼音首字母和与之对应的列表位置private String[] sections;// 存放存在的汉语拼音首字母private String currentCity;//当前城市private boolean isNeedRefresh;//当前定位的城市是否需要刷新private TextView tvCurrentLocateCity;private ProgressBar pbLocate;private TextView tvLocate;private final int VIEW_TYPE = 5;//view的类型个数private AMapLocationClient locationClient = null;private AMapLocationClientOption locationOption = null;public CityListAdapter(Context context, List<City> allCityList, List<City> hotCityList, List<String> recentCityList) {this.mContext = context;this.mAllCityList = allCityList;this.mHotCityList = hotCityList;this.mRecentCityList = recentCityList;alphaIndexer = new HashMap<String, Integer>();sections = new String[allCityList.size()];//这里的主要目的是将listview中要显示字母的条目保存下来,方便在滑动时获得位置,alphaIndexer在Acitivity有调用for (int i = 0; i < mAllCityList.size(); i++) {// 当前汉语拼音首字母String currentStr = getAlpha(mAllCityList.get(i).getPinyin());// 上一个汉语拼音首字母,如果不存在为" "String previewStr = (i - 1) >= 0 ? getAlpha(mAllCityList.get(i - 1).getPinyin()) : " ";if (!previewStr.equals(currentStr)) {String name = getAlpha(mAllCityList.get(i).getPinyin());alphaIndexer.put(name, i);sections[i] = name;}}isNeedRefresh = true;initLocation();}@Overridepublic int getViewTypeCount() {return VIEW_TYPE;}@Overridepublic int getItemViewType(int position) {return position < 4 ? position : 4;}@Overridepublic int getCount() {return mAllCityList.size();}@Overridepublic Object getItem(int position) {return mAllCityList.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;int viewType = getItemViewType(position);if (viewType == 0) {//view类型为0,也就是:当前定位城市的布局convertView = View.inflate(mContext, R.layout.item_location_city, null);tvLocate = (TextView) convertView.findViewById(R.id.tv_locate);tvCurrentLocateCity = (TextView) convertView.findViewById(R.id.tv_current_locate_city);pbLocate = (ProgressBar) convertView.findViewById(R.id.pb_loacte);if (!isNeedRefresh) {tvLocate.setText("当前定位城市");tvCurrentLocateCity.setVisibility(View.VISIBLE);tvCurrentLocateCity.setText(currentCity);pbLocate.setVisibility(View.GONE);} else {startLocation();}tvCurrentLocateCity.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {pbLocate.setVisibility(View.VISIBLE);tvLocate.setText("正在定位");tvCurrentLocateCity.setVisibility(View.GONE);startLocation();}});} else if (viewType == 1) {//最近访问城市convertView = View.inflate(mContext, R.layout.item_recent_visit_city, null);TextView tvRecentVisitCity = (TextView) convertView.findViewById(R.id.tv_recent_visit_city);tvRecentVisitCity.setText("最近访问城市");MyGridView gvRecentVisitCity = (MyGridView) convertView.findViewById(R.id.gv_recent_visit_city);gvRecentVisitCity.setAdapter(new RecentVisitCityAdapter(mContext, mRecentCityList));} else if (viewType == 2) {//热门城市convertView = View.inflate(mContext, R.layout.item_recent_visit_city, null);TextView tvRecentVisitCity = (TextView) convertView.findViewById(R.id.tv_recent_visit_city);tvRecentVisitCity.setText("热门城市");MyGridView gvRecentVisitCity = (MyGridView) convertView.findViewById(R.id.gv_recent_visit_city);gvRecentVisitCity.setAdapter(new HotCityAdapter(mContext, mHotCityList));} else if (viewType == 3) {//全部城市,仅展示“全部城市这四个字”convertView = View.inflate(mContext, R.layout.item_all_city_textview, null);} else {//数据库中所有的城市的名字展示if (convertView == null) {viewHolder = new ViewHolder();convertView = View.inflate(mContext, R.layout.item_city_list, null);viewHolder.tvAlpha = (TextView) convertView.findViewById(R.id.tv_alpha);viewHolder.tvCityName = (TextView) convertView.findViewById(R.id.tv_city_name);viewHolder.llMain = (LinearLayout) convertView.findViewById(R.id.ll_main);convertView.setTag(viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag();}if (position >= 1) {viewHolder.tvCityName.setText(mAllCityList.get(position).getName());viewHolder.llMain.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(mContext, mAllCityList.get(position).getName(), Toast.LENGTH_SHORT).show();}});String currentStr = getAlpha(mAllCityList.get(position).getPinyin());String previewStr = (position - 1) >= 0 ? getAlpha(mAllCityList.get(position - 1).getPinyin()) : " ";//如果当前的条目的城市名字的拼音的首字母和其前一条条目的城市的名字的拼音的首字母不相同,则将布局中的展示字母的TextView展示出来if (!previewStr.equals(currentStr)) {viewHolder.tvAlpha.setVisibility(View.VISIBLE);viewHolder.tvAlpha.setText(currentStr);} else {viewHolder.tvAlpha.setVisibility(View.GONE);}}}return convertView;}// 获得汉语拼音首字母private String getAlpha(String str) {if (str == null) {return "#";}if (str.trim().length() == 0) {return "#";}char c = str.trim().substring(0, 1).charAt(0);// 正则表达式,判断首字母是否是英文字母Pattern pattern = Patternpile("^[A-Za-z]+$");if (pattern.matcher(c + "").matches()) {return (c + "").toUpperCase();} else if (str.equals("0")) {return "定位";} else if (str.equals("1")) {return "最近";} else if (str.equals("2")) {return "热门";} else if (str.equals("3")) {return "全部";} else {return "#";}}class ViewHolder {TextView tvAlpha;TextView tvCityName;LinearLayout llMain;}private void initLocation() {//初始化clientlocationClient = new AMapLocationClient(mContext);locationOption = getDefaultOption();//设置定位参数locationClient.setLocationOption(locationOption);// 设置定位监听locationClient.setLocationListener(locationListener);}/*** 默认的定位参数** @author hongming.wang* @since 2.8.0*/private AMapLocationClientOption getDefaultOption() {AMapLocationClientOption mOption = new AMapLocationClientOption();mOption.setLocationMode(AMapLocationMode.Hight_Accuracy);//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式mOption.setGpsFirst(false);//可选,设置是否gps优先,只在高精度模式下有效。默认关闭mOption.setHttpTimeOut(30000);//可选,设置网络请求超时时间。默认为30秒。在仅设备模式下无效mOption.setInterval(2000);//可选,设置定位间隔。默认为2秒mOption.setNeedAddress(true);//可选,设置是否返回逆地理地址信息。默认是truemOption.setOnceLocation(false);//可选,设置是否单次定位。默认是falsemOption.setOnceLocationLatest(false);//可选,设置是否等待wifi刷新,默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用AMapLocationClientOption.setLocationProtocol(AMapLocationProtocol.HTTP);//可选, 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTPmOption.setSensorEnable(false);//可选,设置是否使用传感器。默认是falsemOption.setWifiScan(true); //可选,设置是否开启wifi扫描。默认为true,如果设置为false会同时停止主动刷新,停止以后完全依赖于系统刷新,定位位置可能存在误差mOption.setLocationCacheEnable(true); //可选,设置是否使用缓存定位,默认为truemOption.setGeoLanguage(AMapLocationClientOption.GeoLanguage.DEFAULT);//可选,设置逆地理信息的语言,默认值为默认语言(根据所在地区选择语言)return mOption;}/*** 定位监听*/AMapLocationListener locationListener = new AMapLocationListener() {@Overridepublic void onLocationChanged(AMapLocation location) {if (null != location) {//errorCode等于0代表定位成功,其他的为定位失败,具体的可以参照官网定位错误码说明if (location.getErrorCode() == 0) {currentCity = location.getCity().substring(0, location.getCity().length() - 1);tvLocate.setText("当前定位城市");tvCurrentLocateCity.setVisibility(View.VISIBLE);tvCurrentLocateCity.setText(currentCity);pbLocate.setVisibility(View.GONE);} else {//定位失败tvLocate.setText("未定位到城市,请选择");tvCurrentLocateCity.setVisibility(View.VISIBLE);tvCurrentLocateCity.setText("重新选择");pbLocate.setVisibility(View.GONE);return;}} else {Toast.makeText(mContext, "定位失败", Toast.LENGTH_SHORT).show();}}};/*** 开始定位** @author hongming.wang* @since 2.8.0*/private void startLocation() {//根据控件的选择,重新设置定位参数
//        resetOption();// 设置定位参数locationClient.setLocationOption(locationOption);// 启动定位locationClient.startLocation();}
}

2.主活动布局activity_main.xml

<RelativeLayout xmlns:android=""android:layout_width="match_parent"android:layout_height="match_parent"android:background="#f0f0f0"><EditTextandroid:id="@+id/et_search"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="输入城市名或拼音" /><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_below="@id/et_search"><ListViewandroid:id="@+id/lv_city"android:layout_width="match_parent"android:layout_height="match_parent"android:divider="#00000000"android:dividerHeight="0dp"android:listSelector="@android:color/transparent" /><TextViewandroid:id="@+id/tv_dialog"android:layout_width="80dp"android:layout_height="80dp"android:layout_gravity="center"android:background="@android:color/darker_gray"android:gravity="center"android:textColor="#ffffffff"android:textSize="30dp"android:visibility="gone" /><ListViewandroid:id="@+id/lv_result"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="7dp"android:divider="#00000000"android:dividerHeight="0dp"android:listSelector="@android:color/transparent"android:scrollbars="none"android:visibility="gone" /><TextViewandroid:id="@+id/tv_noresult"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="抱歉,暂时没有找到相关城市"android:visibility="gone" /><LinearLayoutandroid:layout_width="30dp"android:layout_height="match_parent"android:layout_gravity="right"android:layout_marginTop="7dp"android:layout_marginRight="2dp"><com.example.citylist.view.MyLetterViewandroid:id="@+id/my_letterview"android:layout_width="25dp"android:layout_height="300dp"android:layout_gravity="center" /></LinearLayout></FrameLayout>
</RelativeLayout>

3.主活动MainActivity.java

public class MainActivity extends Activity {protected static final String TAG = "MainActivity";private MyLetterView myLetterView;//自定义的Viewprivate TextView tvDialog;//主界面显示字母的TextViewprivate ListView lvCity;//进行城市列表展示private EditText etSearch;private ListView lvResult;//搜索结果列表展示private TextView tvNoResult;//搜索无结果时文字展示private List<City> allCityList;//所有的城市private List<City> hotCityList;//热门城市列表private List<City> searchCityList;//搜索城市列表private List<String> recentCityList;//最近访问城市列表public CitySqliteOpenHelper cityOpenHelper;//对保存了最近访问城市的数据库操作的帮助类public SQLiteDatabase cityDb;//保存最近访问城市的数据库public CityListAdapter cityListAdapter;public SearchResultAdapter searchResultAdapter;private boolean isScroll = false;private boolean mReady = false;private Handler handler;private OverlayThread overlayThread; //显示首字母对话框@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);initView();initData();setListener();//初始化所有城市列表initAllCityData();initRecentVisitCityData();//初始化最近访问的城市数据initHotCityData();//初始化热门城市setAdapter();//设置适配器mReady = true;}private void setAdapter() {cityListAdapter = new CityListAdapter(this, allCityList, hotCityList, recentCityList);searchResultAdapter = new SearchResultAdapter(this, searchCityList);lvCity.setAdapter(cityListAdapter);lvResult.setAdapter(searchResultAdapter);}private void initView() {myLetterView = (MyLetterView) findViewById(R.id.my_letterview);tvDialog = (TextView) findViewById(R.id.tv_dialog);myLetterView.setTextView(tvDialog);lvCity = (ListView) findViewById(R.id.lv_city);etSearch = (EditText) findViewById(R.id.et_search);lvResult = (ListView) findViewById(R.id.lv_result);tvNoResult = (TextView) findViewById(R.id.tv_noresult);}private void setListener() {//自定义myLetterView的一个监听myLetterView.setOnSlidingListener(new OnSlidingListener() {@Overridepublic void sliding(String s) {isScroll = false;if (cityListAdapter.alphaIndexer.get(s) != null) {//根据MyLetterView滑动到的数据获得ListView应该展示的位置int position = cityListAdapter.alphaIndexer.get(s);//将listView展示到相应的位置lvCity.setSelection(position);}}});etSearch.addTextChangedListener(new TextWatcher() {@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {if (s.toString() == null || "".equals(s.toString())) {myLetterView.setVisibility(View.VISIBLE);lvCity.setVisibility(View.VISIBLE);lvResult.setVisibility(View.GONE);tvNoResult.setVisibility(View.GONE);} else {searchCityList.clear();myLetterView.setVisibility(View.GONE);lvCity.setVisibility(View.GONE);getResultCityList(s.toString());if (searchCityList.size() <= 0) {lvResult.setVisibility(View.GONE);tvNoResult.setVisibility(View.VISIBLE);} else {lvResult.setVisibility(View.VISIBLE);tvNoResult.setVisibility(View.GONE);searchResultAdapter.notifyDataSetChanged();}}}@Overridepublic void beforeTextChanged(CharSequence s, int start, int count,int after) {}@Overridepublic void afterTextChanged(Editable s) {}});lvCity.setOnScrollListener(new OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if (scrollState == SCROLL_STATE_TOUCH_SCROLL|| scrollState == SCROLL_STATE_FLING) {isScroll = true;}}@SuppressLint("DefaultLocale")@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {if (!isScroll) {return;}if (mReady) {String text;String name = allCityList.get(firstVisibleItem).getName();String pinyin = allCityList.get(firstVisibleItem).getPinyin();if (firstVisibleItem < 4) {text = name;} else {text = PingYinUtil.converterToFirstSpell(pinyin).substring(0, 1).toUpperCase();}tvDialog.setText(text);tvDialog.setVisibility(View.VISIBLE);handler.removeCallbacks(overlayThread);
//					Toast.makeText(MainActivity.this,"测试",0).show();
//					 延迟一秒后执行,让中间显示的TextView为不可见handler.postDelayed(overlayThread, 1000);}}});}private void initData() {cityOpenHelper = new CitySqliteOpenHelper(MainActivity.this);cityDb = cityOpenHelper.getWritableDatabase();allCityList = new ArrayList<City>();hotCityList = new ArrayList<City>();searchCityList = new ArrayList<City>();recentCityList = new ArrayList<String>();handler = new Handler();overlayThread = new OverlayThread();}private void initAllCityData() {City city = new City("定位", "0"); // 当前定位城市allCityList.add(city);city = new City("最近", "1");allCityList.add(city);city = new City("热门", "2");allCityList.add(city);city = new City("全部", "3");allCityList.add(city);allCityList.addAll(getCityList());}@SuppressWarnings("unchecked")private ArrayList<City> getCityList() {SQLiteDatabase db;Cursor cursor = null;//获取assets目录下的数据库中的所有城市的openHelperAllCitySqliteOpenHelper op = new AllCitySqliteOpenHelper(MainActivity.this);ArrayList<City> cityList = new ArrayList<City>();try {op.createDataBase();db = op.getWritableDatabase();cursor = db.rawQuery("select * from city", null);while (cursor.moveToNext()) {String cityName = cursor.getString(cursor.getColumnIndex("name"));String cityPinyin = cursor.getString(cursor.getColumnIndex("pinyin"));City city = new City(cityName, cityPinyin);cityList.add(city);}} catch (IOException e) {e.printStackTrace();} finally {cursor.close();}Collections.sort(cityList, comparator);return cityList;}private void initRecentVisitCityData() {InsertCity("北京");InsertCity("上海");InsertCity("广州");SQLiteDatabase recentVisitDb = cityOpenHelper.getWritableDatabase();Cursor cursor = recentVisitDb.rawQuery("select * from recentcity order by date desc limit 0, 3", null);while (cursor.moveToNext()) {String recentVisitCityName = cursor.getString(cursor.getColumnIndex("name"));recentCityList.add(recentVisitCityName);}cursor.close();recentVisitDb.close();}private void initHotCityData() {City city = new City("北京", "2");hotCityList.add(city);city = new City("上海", "2");hotCityList.add(city);city = new City("广州", "2");hotCityList.add(city);city = new City("南京", "2");hotCityList.add(city);city = new City("合肥", "2");hotCityList.add(city);city = new City("安徽", "2");hotCityList.add(city);city = new City("砀山", "2");hotCityList.add(city);city = new City("日本", "2");hotCityList.add(city);}/*** 自定义的排序规则,按照A-Z进行排序*/@SuppressWarnings("rawtypes")Comparator comparator = new Comparator<City>() {@Overridepublic int compare(City lhs, City rhs) {String a = lhs.getPinyin().substring(0, 1);String b = rhs.getPinyin().substring(0, 1);int flag = apareTo(b);if (flag == 0) {return apareTo(b);} else {return flag;}}};@SuppressWarnings("unchecked")private void getResultCityList(String keyword) {AllCitySqliteOpenHelper dbHelper = new AllCitySqliteOpenHelper(this);try {dbHelper.createDataBase();SQLiteDatabase db = dbHelper.getWritableDatabase();Cursor cursor = db.rawQuery("select * from city where name like \"%" + keyword+ "%\" or pinyin like \"%" + keyword + "%\"", null);City city;while (cursor.moveToNext()) {String cityName = cursor.getString(cursor.getColumnIndex("name"));String cityPinyin = cursor.getString(cursor.getColumnIndex("pinyin"));city = new City(cityName, cityPinyin);searchCityList.add(city);}cursor.close();db.close();} catch (IOException e) {e.printStackTrace();}//将得到的集合按照自定义的comparator的规则进行排序Collections.sort(searchCityList, comparator);}// 设置显示字母的TextView为不可见private class OverlayThread implements Runnable {@Overridepublic void run() {tvDialog.setVisibility(View.INVISIBLE);}}//插入数据到最近访问的城市public void InsertCity(String name) {SQLiteDatabase db = cityOpenHelper.getReadableDatabase();Cursor cursor = db.rawQuery("select * from recentcity where name = '" + name + "'", null);if (cursor.getCount() > 0) { //db.delete("recentcity", "name = ?", new String[]{name});}db.execSQL("insert into recentcity(name, date) values('" + name + "', " + System.currentTimeMillis() + ")");db.close();}
}

本文标签: Android Studio 仿首页美团切换城市(ListView)数据库帮助类SQLiteOpenHelperLetterView(字母排序)