前言
我們知道,地址選擇器是一個(gè)通用組件密幔,網(wǎng)上的開(kāi)源項(xiàng)目也有很多楔脯。那么為什么還會(huì)有這篇文章呢?因?yàn)槲以谡{(diào)研過(guò)程中發(fā)現(xiàn)老玛,雖然都是地址選擇器淤年,但是實(shí)現(xiàn)的方式卻各不相同。以下是調(diào)研地址選擇器的一些總結(jié)和思考實(shí)踐蜡豹。希望對(duì)大家有幫助麸粮,大家有什么更好的想法,也要告知我哦~
地址選擇器實(shí)現(xiàn)方式介紹
-
本地存放area.db文件
大多數(shù)App都是此種實(shí)現(xiàn)- 優(yōu)點(diǎn):?jiǎn)?dòng)快镜廉,不受網(wǎng)絡(luò)影響
- 缺點(diǎn):
- 不能實(shí)時(shí)更新弄诲,數(shù)據(jù)更新依賴發(fā)布新版本。(或則自己實(shí)現(xiàn)一套更新機(jī)制娇唯,文件從后端下載齐遵,實(shí)現(xiàn)較為復(fù)雜)
- 各端(服務(wù)器、前端塔插、移動(dòng)端)需要維護(hù)一份相同的地址信息表梗摇,維護(hù)成本高。
- 地址信息表本地保存想许,增大安裝包體積
-
一次性從服務(wù)端拉取所有地址信息
在App啟動(dòng)時(shí)候伶授,一次性拉取所有地址信息。- 優(yōu)點(diǎn):數(shù)據(jù)可配置流纹、請(qǐng)求少
- 缺點(diǎn):
- 每次啟動(dòng)都請(qǐng)求服務(wù)器糜烹,大部分是無(wú)用請(qǐng)求,浪費(fèi)了服務(wù)器資源漱凝。
- 請(qǐng)求數(shù)據(jù)量大
- 優(yōu)化方案:更換請(qǐng)求時(shí)機(jī)疮蹦,點(diǎn)擊選擇地址的時(shí)候才去一次性請(qǐng)求所有數(shù)據(jù)。但是還是不能避免請(qǐng)求數(shù)據(jù)量過(guò)大問(wèn)題茸炒。
-
實(shí)時(shí)獲取省愕乎、市阵苇、區(qū)信息,選中上一級(jí)才去獲取對(duì)應(yīng)的下一級(jí)數(shù)據(jù)
如:京東- 優(yōu)點(diǎn):數(shù)據(jù)可配置妆毕,請(qǐng)求數(shù)據(jù)量小
- 缺點(diǎn):請(qǐng)求較多慎玖,需要處理的異常case較多
- 思考優(yōu)化方案:每次喚起地址選擇器時(shí),緩存獲取的地址信息歷史數(shù)據(jù)笛粘。顯示地址信息的時(shí)候趁怔,只有本地緩存沒(méi)有當(dāng)前數(shù)據(jù),才向服務(wù)端發(fā)送請(qǐng)求薪前。(其實(shí)這種場(chǎng)景很少出現(xiàn)润努,但是再小的蒼蠅也是肉,能優(yōu)化就優(yōu)化了吧……)
因?yàn)橛脩艋臼窃谟芯W(wǎng)絡(luò)的情況下才會(huì)使用示括,所以選擇聯(lián)網(wǎng)獲取地址信息也是合理的铺浇。又考慮到網(wǎng)絡(luò)請(qǐng)求的大小,服務(wù)端性能影響垛膝,安裝包大小以及地址信息可配置性等因素鳍侣,實(shí)時(shí)獲取地址信息是一個(gè)不錯(cuò)的方式。下面介紹地址選擇器實(shí)現(xiàn)一些關(guān)鍵點(diǎn)吼拥。
實(shí)時(shí)獲取信息的地址選擇器設(shè)計(jì)
先來(lái)看效果倚聚,如下圖所示。喚起地址選擇器會(huì)有一次省份的網(wǎng)絡(luò)請(qǐng)求凿可,之后每一級(jí)數(shù)據(jù)都是實(shí)時(shí)去獲取惑折。在當(dāng)前地址選擇器喚起狀態(tài),獲取之后就將歷史數(shù)據(jù)保存在本地枯跑,下一次就不再發(fā)送網(wǎng)絡(luò)請(qǐng)求了惨驶。
數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)
當(dāng)前的地址信息按照省、市敛助、區(qū)/縣粗卜、街道四級(jí)劃分,后一級(jí)總是和前一級(jí)相關(guān)聯(lián)纳击。
- Province
public class Province {
public long id;
public String name;
}
- City
public class City {
public long id;
public long province_id;
public String name;
}
- County
public class County {
public long id;
public long city_id;
public String name;
}
- Street
public class Street {
public long id;
public long county_id;
public String name;
}
回調(diào)接口設(shè)計(jì)
主要提供了4個(gè)回調(diào)方法:
public interface OnAddressSelectedListener {
// 獲取地址完成回調(diào)
void onAddressSelected(Province province, City city, County county, Street street);
// 選取省份完成回調(diào)
void onProvinceSelected(Province province);
// 選取城市完成回調(diào)
void onCitySelected(City city);
// 選取區(qū)/縣完成回調(diào)
void onCountySelected(County county);
}
這里著重說(shuō)一下onAddressSelected
回調(diào)方法续扔,其是在地址選擇完成的時(shí)候調(diào)用。那么是如何判斷地址選擇已經(jīng)完成呢评疗。這個(gè)在AddressSelector
中有如下一個(gè)機(jī)制测砂。
每次一個(gè)級(jí)別選擇完成后茵烈,會(huì)獲取下一個(gè)級(jí)別的數(shù)據(jù)(網(wǎng)絡(luò)請(qǐng)求或者緩存獲劝俅摇)進(jìn)行顯示。顯示的時(shí)候有這么一個(gè)邏輯呜投,當(dāng)前級(jí)別有數(shù)據(jù)加匈,則正常顯示存璃;若沒(méi)有,則說(shuō)明地址選擇已經(jīng)完成雕拼,此時(shí)調(diào)用onAddressSelected
方法纵东。
緩存設(shè)計(jì)
我們這里默認(rèn)服務(wù)端每個(gè)地址的id都是唯一的。緩存機(jī)制比較簡(jiǎn)單啥寇,可以看下如下的流程圖偎球。
- 建立三個(gè)緩存map,分別緩存
始稹-市
衰絮、市-區(qū)
和區(qū)-街道
。
/** 緩存數(shù)據(jù):省-市 */
private ArrayMap<Long, List<City>> province2city = new ArrayMap<>();
/** 緩存數(shù)據(jù):市-區(qū) */
private ArrayMap<Long, List<County>> city2county = new ArrayMap<>();
/** 緩存數(shù)據(jù):區(qū)-街道 */
private ArrayMap<Long, List<Street>> county2street = new ArrayMap<>();
- 選擇省磷醋、市猫牡、區(qū)某一級(jí)時(shí),先查看是否有緩存數(shù)據(jù)邓线,若有則使用緩存數(shù)據(jù)淌友;若沒(méi)有,則向服務(wù)端發(fā)送網(wǎng)絡(luò)請(qǐng)求骇陈。
// 有緩存則直接使用緩存,否則去重新請(qǐng)求
if(province2city.containsKey(province.id)){
setCities(province2city.get(province.id));
} else {
progressBar.setVisibility(View.VISIBLE);
listener.onProvinceSelected(province);
}
3震庭、每次獲取的歷史數(shù)據(jù),存儲(chǔ)在相應(yīng)的緩存map中缩歪。
province2city.put(provinceId, cities);
控件使用
- gradle導(dǎo)入控件
compile 'com.zr.addressselector:library:1.0.1'
- Activity實(shí)現(xiàn)
OnAddressSelectedListener
接口
public class MainActivity extends AppCompatActivity implements OnAddressSelectedListener
- 展現(xiàn)地址選擇器
dialog = new BottomSelectorDialog(MainActivity.this);
dialog.setOnAddressSelectedListener(MainActivity.this);
dialog.show();
- 根據(jù)網(wǎng)絡(luò)返回归薛,設(shè)置數(shù)據(jù)
dialog.getSelector().setProvinces(Collections.singletonList(province));
- 控件提供的幾個(gè)設(shè)置方法
/**
* 設(shè)置回調(diào)接口
* @param listener
*/
public void setOnAddressSelectedListener(OnAddressSelectedListener listener) {
this.listener = listener;
}
/**
* 設(shè)置省列表
* @param provinces 省份列表
*/
public void setProvinces(List<Province> provinces){
handler.sendMessage(Message.obtain(handler, WHAT_PROVINCES_PROVIDED, provinces));
}
/**
* 設(shè)置市列表
* @param cities 城市列表
*/
public void setCities(List<City> cities){
handler.sendMessage(Message.obtain(handler, WHAT_CITIES_PROVIDED, cities));
}
/**
* 設(shè)置區(qū)列表
* @param countries 區(qū)/縣列表
*/
public void setCountries(List<County> countries){
handler.sendMessage(Message.obtain(handler, WHAT_COUNTIES_PROVIDED, countries));
}
/**
* 設(shè)置街道列表
* @param streets 街道列表
*/
public void setStreets(List<Street> streets){
handler.sendMessage(Message.obtain(handler, WHAT_STREETS_PROVIDED, streets));
}
寫(xiě)在最后
雖然提供了gradle compile導(dǎo)入的方式,但是整個(gè)控件的源碼其實(shí)是很簡(jiǎn)單的匪蝙。如果有定制需求主籍,可以到這里下載源碼。
項(xiàng)目源碼下載:ZRAddressSelector
特別感謝
JDAddressSelector逛球,本組件設(shè)計(jì)也是受此開(kāi)源項(xiàng)目啟發(fā)千元,非常感謝作者開(kāi)源~