Android開發實現選擇城市介面,可根據拼音、首字母進行搜尋
阿新 • • 發佈:2019-01-04
短短的國慶8天假期一眨眼就過去了,下次長假只有等到過年了,本寶寶不開心。既然已經開始工作了,就要好好多學習點新知識,來提高自己的程式碼能力,今天帶大家去實現簡易的選擇城市介面,並且可以根據城市首字母或者拼音搜尋。先來看下我的效果圖:
在寫程式碼之前先準備好一個城市列表的json檔案以及去百度或者高德申請定位功能,Demo在文章最後。(Demo裡沒有寫定位功能,需要自己實現哦~)
實現方法也比較簡單,我就簡單的給大家說一下,整個城市列表是用的一個ListView,方便我們後面監聽它的滾動狀態,右邊的快速定位欄是自定義View,程式碼比較簡單,我就直接放程式碼了。不知道大家發現沒有,有些字母開頭的城市是沒有的,比如 I,O,U,V等等,為了節約空間我就把那幾個字母去掉了,有的軟體是把26個字母全部保留了。
(其實都不影響,看你個人吧,我反正是有強迫症的。)注意,在自定義View的時候三種構造方法都要寫上,千萬不要偷懶!!!哎,都是淚 o(╥﹏╥)o
public class LetterListView extends View {
OnTouchingLetterChangedListener onTouchingLetterChangedListener;
public static String[] b = {"定位", "熱門", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K",
"L", "M", "N", "P", "Q", "R", "S", "T", "W", "X", "Y", "Z"};
int choose = -1;
Paint paint = new Paint();
boolean showBkg = false;
private Context mContext;
public LetterListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mContext = context;
}
public LetterListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
}
public LetterListView(Context context) {
super(context);
this.mContext = context;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (showBkg) {
canvas.drawColor(Color.parseColor("#40000000"));
}
int height = getHeight();
int width = getWidth();
int singleHeight = height / b.length;
for (int i = 0; i < b.length; i++) {
paint.setColor(Color.parseColor("#50B3DA"));
paint.setTextSize(DisplayUtil.sp2px(mContext, 12));
paint.setAntiAlias(true);
float xPos = width / 2 - paint.measureText(b[i]) / 2;
float yPos = singleHeight * i + singleHeight;
canvas.drawText(b[i], xPos, yPos, paint);
paint.reset();
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float y = event.getY();
final int oldChoose = choose;
final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
final int c = (int) (y / getHeight() * b.length);
switch (action) {
case MotionEvent.ACTION_DOWN:
showBkg = true;
if (oldChoose != c && listener != null) {
if (c >= 0 && c < b.length) {
listener.onTouchingLetterChanged(b[c]);
choose = c;
invalidate();
}
}
break;
case MotionEvent.ACTION_MOVE:
if (oldChoose != c && listener != null) {
if (c >= 0 && c < b.length) {
listener.onTouchingLetterChanged(b[c]);
choose = c;
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
showBkg = false;
choose = -1;
invalidate();
break;
}
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
public void setOnTouchingLetterChangedListener(
OnTouchingLetterChangedListener onTouchingLetterChangedListener) {
this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
}
public interface OnTouchingLetterChangedListener {
void onTouchingLetterChanged(String s);
}
}
使用的時候設定好寬度和高度:
<com.kairui.kyb.ui.view.widget.LetterListView
android:id="@+id/total_city_letters_lv"
android:layout_width="25dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_marginRight="2dp"
android:layout_marginTop="7dp"
android:layout_marginBottom="7dp"/>
接下來就是關鍵的地方了,咳咳~
一、 既然左邊用到了ListView,那就少不了介面卡和城市模型。
①、城市模型:其中拼音和首字母是方便使用者搜尋的時候進行篩選,CityCode你們可以不用去管,我主要是用來請求資料。
public class CityEntity {
private String name;
private String key;
private String pinyin; //全拼
private String first; //首字母
private String cityCode;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getPinyin() {
return pinyin;
}
public void setPinyin(String pinyin) {
this.pinyin = pinyin;
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getCityCode() {
return cityCode;
}
public void setCityCode(String cityCode) {
this.cityCode = cityCode;
}
}
②、全部城市列表的介面卡:我這裡將全部城市列表分成了三種類型,第一種就是當前定位城市的佈局,第二種就是熱門城市的佈局,(當然了,熱門城市你們可以從後臺獲取,我圖方便就寫死在本地了),第三種就是全部城市的佈局了
/**
* 總城市介面卡
*/
private class CityListAdapter extends BaseAdapter {
private Context context;
private List<CityEntity> totalCityList;
private List<CityEntity> hotCityList;
private LayoutInflater inflater;
final int VIEW_TYPE = 3;
CityListAdapter(Context context,
List<CityEntity> totalCityList,
List<CityEntity> hotCityList) {
this.context = context;
this.totalCityList = totalCityList;
this.hotCityList = hotCityList;
inflater = LayoutInflater.from(context);
alphaIndexer = new HashMap<>();
for (int i = 0; i < totalCityList.size(); i++) {
// 當前漢語拼音首字母
String currentStr = totalCityList.get(i).getKey();
String previewStr = (i - 1) >= 0 ? totalCityList.get(i - 1).getKey() : " ";
if (!previewStr.equals(currentStr)) {
String name = getAlpha(currentStr);
alphaIndexer.put(name, i);
}
}
}
@Override
public int getViewTypeCount() {
return VIEW_TYPE;
}
@Override
public int getItemViewType(int position) {
return position < 2 ? position : 2;
}
@Override
public int getCount() {
return totalCityList == null ? 0 : totalCityList.size();
}
@Override
public Object getItem(int position) {
return totalCityList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final TextView curCityNameTv;
ViewHolder holder;
int viewType = getItemViewType(position);
if (viewType == 0) { // 定位
convertView = inflater.inflate(R.layout.select_city_location_item, null);
LinearLayout noLocationLl = (LinearLayout) convertView.findViewById(R.id.cur_city_no_data_ll);
TextView getLocationTv = (TextView) convertView.findViewById(R.id.cur_city_re_get_location_tv);
curCityNameTv = (TextView) convertView.findViewById(R.id.cur_city_name_tv);
if (TextUtils.isEmpty(locationCity)) {
noLocationLl.setVisibility(View.VISIBLE);
curCityNameTv.setVisibility(View.GONE);
getLocationTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
initLocation();
}
});
} else {
noLocationLl.setVisibility(View.GONE);
curCityNameTv.setVisibility(View.VISIBLE);
curCityNameTv.setText(locationCity);
curCityNameTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!locationCity.equals(UserConstant.curSelCity)) {
//設定城市程式碼
String cityCode = "";
for (CityEntity cityEntity : AppCache.getInstance().getCurCityList()) {
if (cityEntity.getName().equals(locationCity)) {
cityCode = cityEntity.getCityCode();
break;
}
}
showSetCityDialog(locationCity, cityCode);
} else {
ToastUtils.show("當前定位城市" + curCityNameTv.getText().toString());
}
}
});
}
} else if (viewType == 1) { //熱門城市
convertView = inflater.inflate(R.layout.recent_city_item, null);
GridView hotCityGv = (GridView) convertView.findViewById(R.id.recent_city_gv);
hotCityGv.setAdapter(new HotCityListAdapter(context, this.hotCityList));
hotCityGv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
CityEntity cityEntity = hotCityList.get(position);
showSetCityDialog(cityEntity.getName(), cityEntity.getCityCode());
}
});
} else {
if (null == convertView) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.city_list_item_layout, null);
ViewBinder.bind(holder, convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
CityEntity cityEntity = totalCityList.get(position);
holder.cityKeyTv.setVisibility(View.VISIBLE);
holder.cityKeyTv.setText(getAlpha(cityEntity.getKey()));
holder.cityNameTv.setText(cityEntity.getName());
if (position >= 1) {
CityEntity preCity = totalCityList.get(position - 1);
if (preCity.getKey().equals(cityEntity.getKey())) {
holder.cityKeyTv.setVisibility(View.GONE);
} else {
holder.cityKeyTv.setVisibility(View.VISIBLE);
}
}
}
return convertView;
}
private class ViewHolder {
@Bind(R.id.city_name_tv)
TextView cityNameTv;
@Bind(R.id.city_key_tv)
TextView cityKeyTv;
}
}
1)、當前定位城市佈局:定位失敗的話顯示出來,並且提示使用者去開啟GPS
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="vertical">
<TextView
android:id="@+id/city_item_hint_tv"
android:layout_width="match_parent"
android:layout_height="25dp"
android:background="@color/mainGray"
android:gravity="center_vertical"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:drawablePadding="5dp"
android:drawableLeft="@drawable/ic_location"
android:text="當前定位城市"
android:textColor="@color/gray_9"
android:textSize="14sp" />
<TextView
android:id="@+id/cur_city_name_tv"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="10dp"
android:background="@drawable/shape_gray_border_pres_style"
android:ellipsize="end"
android:gravity="center"
android:textColor="@color/mainColor"
android:textSize="14sp"
android:visibility="gone" />
<LinearLayout
android:id="@+id/cur_city_no_data_ll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_weight="1"
android:text="無法獲取您的定位地址" />
<TextView
android:id="@+id/cur_city_re_get_location_tv"
android:layout_width="120dp"
android:layout_height="35dp"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:background="@drawable/round_btn_pres_style"
android:gravity="center"
android:text="重新獲取"
android:textColor="@color/white" />
</LinearLayout>
</LinearLayout>
2)、熱門城市佈局:記住,這裡直接使用GridView的話只會顯示一行資料,需要繼承GridView並重寫OnMeasure方法,網上有很多案例,就不放程式碼了。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="25dp"
android:background="@color/mainGray"
android:drawableLeft="@drawable/ic_hot"
android:drawablePadding="5dp"
android:gravity="center_vertical"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:text="熱門城市"
android:textColor="@color/gray_9"/>
<com.kairui.kyb.ui.view.widget.ScrollWithGridView
android:id="@+id/recent_city_gv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="30dp"
android:layout_marginTop="10dp"
android:horizontalSpacing="10dp"
android:listSelector="@android:color/transparent"
android:numColumns="3"
android:verticalSpacing="10dp" />
</LinearLayout>
3)、全部城市的單行佈局:每繪製一條資料判斷前一個數據的Key是否與現在的相同,true則不顯示 city_key_tv,保證同一個Key的城市只有一個顯示。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/recycler_bg"
android:orientation="vertical">
<TextView
android:id="@+id/city_key_tv"
android:layout_width="match_parent"
android:layout_height="25dp"
android:background="@color/mainGray"
android:gravity="center_vertical"
android:paddingLeft="32dp"
android:paddingRight="32dp"
android:textColor="@color/gray_9"
android:textSize="14sp" />
<TextView
android:id="@+id/city_name_tv"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:textColor="@color/gray_6"
android:textSize="15sp" />
<View
style="@style/default_line"
android:layout_marginLeft="@dimen/common_20"
android:layout_marginRight="@dimen/common_20" />
</LinearLayout>
③、熱門城市列表介面卡:比較簡單,不過多說明。
/**
* 熱門城市介面卡
*/
private class HotCityListAdapter extends BaseAdapter {
private List<CityEntity> cityEntities;
private LayoutInflater inflater;
HotCityListAdapter(Context mContext, List<CityEntity> cityEntities) {
this.cityEntities = cityEntities;
inflater = LayoutInflater.from(mContext);
}
@Override
public int getCount() {
return cityEntities == null ? 0 : cityEntities.size();
}
@Override
public Object getItem(int position) {
return cityEntities.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (null == convertView) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.city_list_grid_item_layout, null);
ViewBinder.bind(holder, convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
CityEntity cityEntity = cityEntities.get(position);
holder.cityNameTv.setText(cityEntity.getName());
return convertView;
}
private class ViewHolder {
@Bind(R.id.city_list_grid_item_name_tv)
TextView cityNameTv;
}
}
二、初始化首字母提示框,並且設定ListVIew的滾動監聽以及自定義View的Touch事件
/**
* 初始化漢語拼音首字母彈出提示框
*/
private void initOverlay() {
mReady = true;
LayoutInflater inflater = LayoutInflater.from(this);
overlay = (TextView) inflater.inflate(R.layout.overlay, null);
overlay.setVisibility(View.INVISIBLE);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
PixelFormat.TRANSLUCENT);
WindowManager windowManager = (WindowManager) this
.getSystemService(Context.WINDOW_SERVICE);
windowManager.addView(overlay, lp);
}
private class LetterListViewListener implements
LetterListView.OnTouchingLetterChangedListener {
@Override
public void onTouchingLetterChanged(final String s) {
isScroll = false;
if (alphaIndexer.get(s) != null) {
int position = alphaIndexer.get(s);
totalCityLv.setSelection(position);
overlay.setText(s);
overlay.setVisibility(View.VISIBLE);
handler.removeCallbacks(overlayThread);
// 延遲讓overlay為不可見
handler.postDelayed(overlayThread, 700);
}
}
}
/**
* 設定overlay不可見
*/
private class OverlayThread implements Runnable {
@Override
public void run() {
overlay.setVisibility(View.GONE);
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_TOUCH_SCROLL
|| scrollState == SCROLL_STATE_FLING) {
isScroll = true;
} else {
isScroll = false;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (!isScroll) {
return;
}
if (mReady) {
String key = getAlpha(totalCityList.get(firstVisibleItem).getKey());
overlay.setText(key);
overlay.setVisibility(View.VISIBLE);
handler.removeCallbacks(overlayThread);
// 延遲讓overlay為不可見
handler.postDelayed(overlayThread, 700);
}
}
其中最主要關鍵的地方就是以上這些了
最後我就放上demo的下載地址了,Demo點我
就是這些了,祝大家第一天工作愉快~