转载请注明出处:http://blog.csdn.net/dmk877/article/details/49757731

最近一直关注一些比较有名的app,像美团、58、赶集网、淘宝等等。主要目的就是学习下目前一些常用的技术,模拟一下它们的比较炫的界面来巩固下知识,我发现美团、58同城、赶集网它们的切换城市界面类似,也挺酷炫,另外一个原因由于前面几篇博客写的自定义控件的一些知识相对来说比较难,也有好多看官反应很难读懂,那么好,今天呢,就和大家一起分享一下这个界面的写法。这个界面的实现并不难牵扯到的知识点还挺多的而且还挺酷炫,所以能够读这篇博客你绝对赚到了,哈哈。。。

如有谬误欢迎批评指正,如有疑问欢迎留言。

通过本篇博客你将学到以下知识点

①BaseAdapter的使用包括BaseAdapter中的getViewTypeCount和getItemViewType方法的使用

②百度地图定位的使用

③自定义控件的相关的知识

④数据库相关的操作

⑤pinyin4j的用法

我们废话不多说先看看效果图这也是今天我们要达到的效果,由于csdn只允许上传图片大小不超过2M的图片,所以这里我录制两张图片如下

        

它的主要功能有:①展示定位的城市②展示最近访问的城市③展示热门的城市④展示需要展示的城市⑤用EditText进行筛选城市⑥当滑动右边的字母时左边的ListView会跳到相应的位置等。

在这里要提醒大家注意一点不要在模拟器上运行,最好在真机上运行,模拟器上的运行界面效果不好,看到上面两张图是不是感觉还不错,通过这篇博客的学习相信你也可以,咱们废话不多说进入主题

首先来分析下整个界面如下图

从整体上来说包括三大部分第一部分就是最上方的一个EditText,第二部分就是最右边的自定义View,第三部分是EditText下方的ListView,这里EditText的主要作用就是筛选城市,接着我们一点一点的去实现上面的效果。

1、右侧自定义View的实现

实现这样一个效果就是滑动最右边的自定义View然后界面中间的TextView去展示所滑到的字母,这里就要去自定义一个View了,首先来分析下思路,我是这样想的:

①需要用canvas的drawText方法将:“定位”、"最近"、"热门“、"全部"、"A-Z"这些数据画出来,怎么去按照上述图片的样子去绘画这些数据呢?首先需要获得每个字符的高度,怎么获得?用View的高度除以字符的个数就可以得到每个字符的高度,然后绘制时通过控制Y坐标不断的增加从而使数据沿着竖直方向去绘制,在自定义的View中它的实现代码如下

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);}

第4行就是让所绘制的文字在X方向上显示在View的中间,而float yPosition = singleHeight * i + singleHeight;就是来改变每个文字的Y坐标使其沿着竖直方向去绘制文字

②在滑动时怎样通知Activity当前滑动到哪儿了?

这里是通过一个监听的方式,在Activity中注册了自定义View的监听,然后在View滑动的时候将数据回调给Activity

我们先看看代码然后运行下看看是不是这样

MyLetterView的代码如下

package com.example.citylistpractice;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;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(26);mPaint.setColor(Color.parseColor("#8c8c8c"));}@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;}}

可以发现重写了onTouchEvent方法,然后通过监听down,move,up事件来执行相关的操作,当down时首先会改变整个view的背景色,然后将当前滑到的字母通过回调的方式即调用mOnSlidingListener.sliding(letter[position])(这里的mOnSlidingListener就是在activity中的setOnSlidingListener所注册的监听器)传递到Activity中。

布局文件

<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:background="#f0f0f0" ><TextViewandroid:id="@+id/tv_dialog"android:layout_width="80dp"android:layout_height="80dp"android:layout_centerInParent="true"android:background="@android:color/darker_gray"android:gravity="center"android:textColor="#ffffffff"android:textSize="30dp"android:visibility="gone" /><com.example.citylistpractice.MyLetterViewandroid:id="@+id/my_letterview"android:layout_width="25dp"android:layout_height="match_parent"android:layout_alignParentRight="true"android:layout_marginRight="2dp"android:layout_marginTop="7dp" /></RelativeLayout>

MainActivity中的代码

package com.example.citylistpractice;import com.example.citylistpractice.MyLetterView.OnSlidingListener;import android.os.Bundle;
import android.widget.TextView;
import android.app.Activity;public class MainActivity extends Activity {private MyLetterView myLetterView;private TextView tvDialog;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);myLetterView=(MyLetterView) findViewById(R.id.my_letterview);tvDialog=(TextView) findViewById(R.id.tv_dialog);//将中间展示字母的TextView传递到myLetterView中并在其中控制它的显示与隐藏myLetterView.setTextView(tvDialog);//注册MyLetterView中监听(跟setOnClickListener这种系统默认写好的监听一样只不过这里是我们自己写的)myLetterView.setOnSlidingListener(new OnSlidingListener() {@Overridepublic void sliding(String str) {tvDialog.setText(str);}});}
}

在MAinActivity中可以看到myLetterView注册了在MyLetterView中的监听,通过回调的方式将MyLetterView中滑动到的文字传递给MainAcitivity中,并通过tvDialog显示在屏幕中间,它的效果图如下

可以看到效果还不错,(再次提醒注意这里最好不要用模拟器去运行,因为模拟器上运行的效果与和上面的效果差距很大,用真机效果好)这样自定义的这个View的功能就实现了。

2、ListView数据的展示

ListView数据的展示是这个界面的重点,对于ListView数据的展示我们都知道它是依靠BaseAdapter的,这里也是通过给ListView设置一个适配器从而实现文章刚开始展现的效果,只不过这里的Adapter用了平时再给ListView设置适配器时不常用的两个方法,一个是getViewTypeCount,另外一个是getItemViewType,仔细观察上面的分析图可以发现这个列表共有5种类型的Item①当前定位城市②最近访问城市③热门城市④全部城市(仅仅显示”全部城市“这四个字)⑤也就是这个列表的主角就是显示从数据库中查出的所有的城市。下面来一一分析这个5种Item的实现方法,

第一种item即当前定位城市,这个item就是用百度定位来定位用户当前所在城市,这个布局没什么可说的。

第二种布局即最近访问城市,认真看文章刚开始的那个分析图会发现这个item包含一个GridView用来展示最近访问的城市,这里需要注意美团它的最近访问城市是展示三个,我们这里也是,这里的最近访问城市是通过操作数据库来实现的,这里有两种实现方法①当数据库中已经有三条数据时,当用户访问第四个城市的时候,此时需要将第四个城市插入到最近访问城市的最前面,而将数据库中原来排在第三位的城市删除掉,这样就保证了数据库中始终有三个最近访问的城市。②每次都将新访问的城市插入到数据库,在查询时只查前三条,并按时间先后顺序排序。这里我们采用的是第2个方案,它的实现代码如下

插入城市

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();}

查询城市

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();

如果你对数据库不熟可以参考此博客 SQLiteDatabase数据库操作详解

第三种布局即热门城市这个item和第二种类似,也是包含一个GridView这里的GridView的数据是从服务器中返回过来的,这里需要注意的是这里的GridView和第二种布局中的GridView都是自定义的GridView,因为这里的GridView是以Item的形式展现在ListView中的,所以当数据较多时GridView的数据展示不完,这里进行自定义的目的在于,GridView的数据有多少我们让它自适应数据的个数不需要滑动而将数据展示完。它的定义也非常简单代码如下

package com.example.citylist.view;import android.content.Context;
import android.util.AttributeSet;
import android.widget.GridView;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);}}

如果大家对自定义View不熟可以参考此博客( Android开发之自定义控件(一)---onMeasure详解)

第四种布局很简单就是一个TextView展示“全部城市”这四个字

第五种布局也就是主角的主角,就是按照字母的顺序去展示从数据库中查出来的城市,这里的数据是在assets下的一个db文件通过调用SQLiteDatabase.openOrCreateDatabase(dbf, null)在android中使用SQLiteDatabase的静态方法openOrCreateDatabase(String  path,SQLiteDatabae.CursorFactory  factory)(参数1 :数据库创建的路径,参数2 :一般设置为null就可以了)打开或者创建一个数据库。它会自动去检测是否存在这个数据库,如果存在则打开,不存在则创建一个数据库;创建成功则返回一个SQLiteDatabase对象,否则抛出异常FileNotFoundException,例如创建一个meituan_cities.db的数据库

,SQLiteDatabae db=SQLiteDatabase.openOrCreateDatabase("/data/data/com.lingdududu.db/databases/meituan_cities.db",null); 在这个项目中这里的“/data/data/com.lingdududu.db/databases/meituan_cities.db”就是项目的assets目录下的meituan_cities.db的路径。

如果你仔细看了这个界面你会发现这里的城市被分成了22组,哪22组?从A-Z 26个字母去掉i,o,u,v。这一点可以从我们刚才自定义的View的字母中看到,如果我没说之前你发现了,我只能说你太牛逼了,你可以去警察局破案了,哈哈。每一组的第一个Item是展示这组数据的首字母的。这是怎么做到的呢?这就需要依靠pinyin4j这个jar包了,如果你不会用可以去查查资料,这里我们的代码里也有详细的注释,由于篇幅原因我就不再说了,如果有需要的话我会专门写一篇博客来阐述它的用法。这里说一下它的实现思想,它是通过当前条目的城市的拼音的首字母和它的前一个条目的城市的拼音的首字母进行比较,如果不相同说明这是下一组的数据,当前条目应该展示首字母,否则的话就将展示字母的TextView隐藏起来。

好了将这5种类型的Item都分析完后我们来看看,它的部分代码

将字母按A-Z排序的comparator

        /*** 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 = a.compareTo(b);if (flag == 0) {return a.compareTo(b);} else {return flag;}}};

在MainActivity中将查询出来的数据按照我们自己定义的规则进行排序的代码如下

Collections.sort(cityList, comparator);

创建数据库的代码

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();}SQLiteDatabase.openOrCreateDatabase(dbf, null);// 复制asseets中的db文件到DB_PATH下copyDataBase();} catch (IOException e) {throw new Error("数据库创建失败");}}}

创建好数据库后将assets下的数据复制到创建好的数据库下copyDataBase方法的代码如下

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();}

ListView的Adapter的代码

package com.example.citylist.adapter;import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.example.citylist.R;
import com.example.citylist.bean.City;
import com.example.citylist.view.MyGridView;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 LocationClient myLocationClient;private String currentCity;//当前城市private MyLocationListener myLocationListener;private boolean isNeedRefresh;//当前定位的城市是否需要刷新private TextView tvCurrentLocateCity;private ProgressBar pbLocate;private TextView tvLocate;private final int VIEW_TYPE = 5;//view的类型个数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{myLocationClient.start();}tvCurrentLocateCity.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {pbLocate.setVisibility(View.VISIBLE);tvLocate.setText("正在定位");tvCurrentLocateCity.setVisibility(View.GONE);myLocationClient.start();}});} 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(),0).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 = Pattern.compile("^[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;}public void initLocation() {myLocationClient = new LocationClient(mContext);myLocationListener=new MyLocationListener();myLocationClient.registerLocationListener(myLocationListener);// 设置定位参数LocationClientOption option = new LocationClientOption();option.setCoorType("bd09ll"); // 设置坐标类型option.setScanSpan(10000); // 10分钟扫描1次// 需要地址信息,设置为其他任何值(string类型,且不能为null)时,都表示无地址信息。option.setAddrType("all");// 设置是否返回POI的电话和地址等详细信息。默认值为false,即不返回POI的电话和地址信息。option.setPoiExtraInfo(true);// 设置产品线名称。强烈建议您使用自定义的产品线名称,方便我们以后为您提供更高效准确的定位服务。option.setProdName("通过GPS定位我当前的位置");// 禁用启用缓存定位数据option.disableCache(true);// 设置最多可返回的POI个数,默认值为3。由于POI查询比较耗费流量,设置最多返回的POI个数,以便节省流量。option.setPoiNumber(3);// 设置定位方式的优先级。// 当gps可用,而且获取了定位结果时,不再发起网络请求,直接返回给用户坐标。这个选项适合希望得到准确坐标位置的用户。如果gps不可用,再发起网络请求,进行定位。option.setPriority(LocationClientOption.GpsFirst);myLocationClient.setLocOption(option);myLocationClient.start();}public class MyLocationListener implements BDLocationListener{@Overridepublic void onReceiveLocation(BDLocation arg0) {isNeedRefresh=false;if(arg0.getCity()==null){//定位失败tvLocate.setText("未定位到城市,请选择");tvCurrentLocateCity.setVisibility(View.VISIBLE);tvCurrentLocateCity.setText("重新选择");pbLocate.setVisibility(View.GONE);return;}else{//定位成功currentCity=arg0.getCity().substring(0,arg0.getCity().length()-1);tvLocate.setText("当前定位城市");tvCurrentLocateCity.setVisibility(View.VISIBLE);tvCurrentLocateCity.setText(currentCity);myLocationClient.stop();pbLocate.setVisibility(View.GONE);}}@Overridepublic void onReceivePoi(BDLocation arg0) {}}}

里面用到了百度的定位,里面的注释都很清楚就不多说了,这里说一下这样一个功能的实现,这个功能是当滑动右边的自定义的View时,ListView根据当前滑动的字母进行变动,这是怎么实现的?它的实现的思想是这样的,将上面说的22组数据中,每一组的第一个条目的城市的首字母(也就是在ListView中显示字母的那个条目)以key,value的形式放到Map中,这里的key就是当前展示的字母,而value是当前条目在整个列表集合的位置,注意这里的集合是ListView展示的所有数据的集合,包括我们所说的5种布局的全部数据的集合。而当滑动右边的自定义的View时,假如说滑动到了“S”,这时在MainActivity中有个回调会将当前滑动的字母回调到MainActivity中,在MainAcivity中收到当前滑动到的“S”后,就会从Map中根据这个字母来查询它在ListView中所对应的位置,然后通过ListView.setSelection(position)这个方法使界面显示与当前字母所对应的那个组。它的实现代码如下

//自定义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);}}});

3、EditText实现筛选城市的功能

这个功能的实现其实很简单,在MainAcitivity中其实是有两个ListView的一个就是用来展示所有的数据用的,另外一个就是用来展示搜索结果用的,这里实现筛选的方式很简单就是给EditText添加一个addTextChangedListener,这个监听器的代码如下

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);}}}@Overridepublic void beforeTextChanged(CharSequence s, int start, int count,int after) {}@Overridepublic void afterTextChanged(Editable s) {}});

当输入内容时会把展示数据的ListView进行隐藏,而把展示搜索结果的ListView显示出来,根据EditText输入的内容,从数据库中筛选出来符合条件的数据进行展示,它的筛选的SQL语句如下

Cursor cursor = db.rawQuery("select * from city where name like \"%" + keyword+ "%\" or pinyin like \"%" + keyword + "%\"", null);

然后将筛选出来的数据通过Adapter展示出来,这样就完成这个功能。

到这里关于仿58,美团,赶集网的切换城市的界面就算写完了,里面由于细节特别多,我就捡主要的功能,分析了它的实现思想。

如果你有什么疑问,或者发现文章中的错误,欢迎批评指正,谢谢。如果你觉着这篇文章对你有帮助,就赞一个,顶一下呗,您的支持是我前进的动力。。

源码地址

或者加群,群里有源码。群号:467325240

转载请注明出处:http://blog.csdn.net/dmk877/article/details/49757731

Android仿美团切换城市相关推荐

  1. Android 仿美团选择城市、微信通讯录、饿了么点餐列表的导航悬停分组索引列表

    SuspensionIndexBar 项目地址:mcxtzhang/SuspensionIndexBar 简介:快速实现分组悬停.右侧索引导航联动 列表. 如 美团选择城市界面,微信通讯录界面.饿了么 ...

  2. php 仿美团切换城市,微信小程序仿美团城市选择的实现

    这篇文章主要为大家详细介绍了微信小程序仿美团城市选择,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文实例为大家分享了微信小程序城市选择器,供大家参考,具体内容如下 代码很简单. var cit ...

  3. Android仿美团选择城市

    需求:需要有当前定位城市,热门城市,下面按照城市首拼音排序,滑动的过程中字母A,B,C-会置顶互相切换.右侧有快速切换字母城市的选择 效果图: 思路:因为上部分要划走,RecyclerView滑动过程 ...

  4. [Android精品源码] Android 仿美团网,探索ListView的A-Z字母排序功能实现选择城市

    Material Design中文版Code4APPPHP100UI4APP 开启辅助访问设为首页收藏本站快捷导航切换到宽版切换风格 石刚 | |我的 |签到打卡 |设置 |消息 |提醒(2) |退出 ...

  5. 【Android】快速实现仿美团选择城市界面,微信通讯录界面

    概述 本文是这个系列的第三篇,不出意外也是终结篇.因为使用经过重构后的控件已经可以快速实现市面上带 索引导航.悬停分组的列表界面了. 在前两篇里,我们从0开始,一步一步实现了仿微信通讯录.饿了么选餐界 ...

  6. android 美团下拉菜单,Android仿美团分类下拉菜单实例代码

    本文实例为大家分享了Android仿美团下拉菜单的实现代码,分类进行选择,供大家参考,具体内容如下 效果图 操作平台 AS2.0 第三方框架:butterknife build.gradle depe ...

  7. Android 仿美团网,探索使用ViewPager+GridView实现左右滑动查看更多分类的功能

    不记得什么时候,我留意到到美团网首页有使用ViewPager+GridView实现左右滑动查看更多分类的一个功能,感觉它很有趣,于是想着自己动手也实现这样一个功能,和往常一样,主要是想总结一下我在学习 ...

  8. Android仿美团外卖点菜联动列表

    Android高仿美团外卖点菜联动列表效果 最近项目中有一个添加购物车的需求,需要做成美团外卖点菜联动ListView的效果,可能有的朋友觉得这很简单,不就是2个Listview点击事件联动处理机制吗 ...

  9. android仿美团评论

    1.仿美团评论布局,效果图,其中引用了 compile 'com.github.ome450901:SimpleRatingBar:1.3.5' 2.listview_pingjia_item.xml ...

最新文章

  1. 批处理+定时任务实现定时休息提醒
  2. 完美解决方案 | 完全卸载任何版本office残余文件
  3. wireshark网络分析就这么简单_【读书笔记】2wireshark网络分析就这么简单——不同子网如何发送消息。...
  4. nodejs是用来做什么的?
  5. SAP UI5 应用开发教程之二十八 - SAP UI5 应用的集成测试工具 OPA 介绍
  6. docker Harbor2.3.4 https 搭建镜像仓库
  7. python粘性拓展_如何将tkinter小部件置于粘性框架中
  8. DPDK:不仅是加速
  9. 知识点2-1:设置开发环境
  10. 偷天换日?公众号广告新骗局 被坑的多是运营
  11. oracle 11g 监听bug,oracle 11g数据库使用XML Table的BUG解决
  12. 怎么用python画风车_小清新风车短教程:10步教你绘制一副插画
  13. 我的团长我的团第二十四集
  14. 宏基Acer笔记本热销火热机
  15. 1093:计算多项式的值
  16. 2019最应该投资什么?是你明年的北大核心
  17. 计算机专业中观分析是什么,计算机专业本科论文提纲模板样本 计算机专业本科论文大纲怎样写...
  18. 我的春招实习总结:已拿字节,快手,美团Offer
  19. 液晶显示器LCD与OLED详解
  20. Selenium 远程调用 Google Chrome 谷歌浏览器

热门文章

  1. 【排队叫号系统】与门诊预约共通、HIS系统共连 实现门诊“一站式”管理服务
  2. 对我来说值得纪念的日子.. ..^_^
  3. mysql 三主_LINUX下mysql 三主环线复制
  4. IgH详解十六、双主站
  5. 文档管理服务器kass,KASS文档管理系统
  6. 三个自定义图标设置,让你的iPhone可爱亿点点!
  7. 数据结构与算法python版 MOOC 第九周
  8. Dapper.Contrib扩展介绍
  9. 微博引流:五大高效核心策略带你玩转微博实战引流(100%独家原创)
  10. 从住房分配问题看梁山好汉尊卑之谜(上)