preface
As we know, address pickers are a common component, and there are many open source projects on the web. So why this article? Because I found in the research process, although they are address pickers, but the way of implementation is different. Here are some summary and thinking practices for investigating address pickers. Hope to have help to everybody, everybody has what better idea, also want to inform me ~
The implementation of address selector is introduced
-
Local storage of area.db files is implemented in most apps
- Advantages: Fast startup and not affected by the network
- Disadvantages:
- Cannot be updated in real time, data update depends on the release of new version. (Or implement your own update mechanism, download files from the back end, implementation is more complicated)
- Each end (server, front-end, and mobile terminal) needs to maintain the same address information table, which is costly.
- The address information table is saved locally to increase the size of the installation package
-
Pull all address information from the server at a time When the App starts, pull all address information at a time.
- Advantages: Configurable data and fewer requests
- Disadvantages:
- Every startup requests the server, mostly useless requests, wasting server resources.
- Large amount of requested data
- Optimization: Change the timing of the request, and only request all data at once when you click to select the address. However, the problem of large data requests cannot be avoided.
-
Real-time access to provincial, city, district information, select the upper level to obtain the corresponding lower level data such as: JINGdong
- Advantages: Data can be configured and the amount of requested data is small
- Disadvantages: Many requests and many exception cases need to be handled
- Consider an optimization: Cache the history of the address information each time the address selector is invoked. When the address information is displayed, a request is sent to the server only when there is no current data in the local cache. (In fact, this kind of scene is very rare, but no matter how small the fly is meat, it can be optimized to optimize it…)
Since users will use it only when there is a network, it is reasonable to choose the network to obtain address information. Considering the size of network requests, server performance impact, installation package size, and configurability of address information, real-time access to address information is a good approach. Here are some key points of address picker implementation.
Design of address selector for real-time information acquisition
Take a look at the effect first, as shown below. The evocative address selector makes a network request for the province, after which each level of data is retrieved in real time. The current address selector invokes the state, and the historical data is saved locally and no network request is sent next time.
The overall framework design class diagram is as follows:
Data structure design
The current address information is divided into four levels: province, city, district/county, and street, with the latter level always associated with the former.
- Province
public class Province {
public long id;
public String name;
}
Copy the code
- City
public class City {
public long id;
public long province_id;
public String name;
}
Copy the code
- County
public class County {
public long id;
public long city_id;
public String name;
}
Copy the code
- Street
public class Street {
public long id;
public long county_id;
public String name;
}
Copy the code
Callback interface design
Four callback methods are provided:
Access the address complete public interface OnAddressSelectedListener {/ / callback void onAddressSelected (Province Province, City, City, County county, Street street); Void onProvinceSelected(Province Province); Void onCitySelected(City City); // Complete the callback void onCountySelected(County County); }Copy the code
Here we focus on the onAddressSelected callback method, which is called when the address selection is complete. So how do you know that the address selection is complete? And this has the following mechanism in AddressSelector. After each level selection is complete, the next level of data (network request or cache fetch) is fetched for display. If there is data at the current level, it will be displayed normally. If not, the address selection is complete and the onAddressSelected method is called.
Cache design
The default id of each server address is unique. Click to select the address of the cache operation, you can see the following flow chart.
- Create three cache maps and cache them separately
Provincial and municipal
,The municipal and district
andStreet area -
.
Private ArrayMap<Long, List<City>> province2City = new ArrayMap<>(); Private ArrayMap<Long, List<County>> city2county = new ArrayMap<>(); Private ArrayMap<Long, List<Street>> County2street = new ArrayMap<>();Copy the code
- When selecting a level of a province, city, or district, check whether there is cached data first. If so, use cached data. If no network request is sent to the server.
// If there is a cache, use the cache directlyif(province2city.containsKey(province.id)){
setCities(province2city.get(province.id));
} else {
progressBar.setVisibility(View.VISIBLE);
listener.onProvinceSelected(province);
}
Copy the code
3. The historical data obtained each time is stored in the corresponding cache map.
province2city.put(provinceId, cities);
Copy the code
Tab switch animation implementation
I don’t know if you’ve noticed, but when we select the address, there’s a little bar under the top TAB, and it’s going to follow us as we select the address. The animation of horizontal bars mainly consists of two parts:
- Smooth moves under the text of the selected TAB
- According to the length of TAB content, the length of the bar is dynamically changed during the movement of the bar. When the move ends, it changes just to match the TAB content length.
ValueAnimator can change the length of the bar dynamically. Finally, AnimatorSet can set the two animations to run together. Control encapsulates a method to implement this animation effect.
private AnimatorSet buildIndicatorAnimatorTowards(TextView tab) {
ObjectAnimator xAnimator = ObjectAnimator.ofFloat(indicator, "X", indicator.getX(), tab.getX() + tab.getPaddingLeft() * 2);
final ViewGroup.LayoutParams params = indicator.getLayoutParams();
ValueAnimator widthAnimator = ValueAnimator.ofInt(params.width, tab.getMeasuredWidth() - tab.getPaddingLeft() * 2);
widthAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { params.width = (int) animation.getAnimatedValue(); indicator.setLayoutParams(params); }}); AnimatorSetset = new AnimatorSet();
set.setInterpolator(new FastOutSlowInInterpolator());
set.playTogether(xAnimator, widthAnimator);
return set;
}
Copy the code
Control using
- Gradle imports controls
compile 'com. Zr. Addressselector: library: 1.0.1'
Copy the code
- The Activity implementation
OnAddressSelectedListener
interface
public class MainActivity extends AppCompatActivity implements OnAddressSelectedListener
Copy the code
- Display address picker
dialog = new BottomSelectorDialog(MainActivity.this);
dialog.setOnAddressSelectedListener(MainActivity.this);
dialog.show();
Copy the code
- Set the data according to the network return
dialog.getSelector().setProvinces(Collections.singletonList(province));
Copy the code
- Control provides several setting methods
/** * set the callback interface * @param listener */ public voidsetOnAddressSelectedListener(OnAddressSelectedListener listener) { this.listener = listener; } /** * set provinces * @param provinces */ public void provincessetProvinces(List<Province> provinces){ handler.sendMessage(Message.obtain(handler, WHAT_PROVINCES_PROVIDED, provinces)); } /** * Set the list of cities * @param Cities */ public voidsetCities(List<City> cities){ handler.sendMessage(Message.obtain(handler, WHAT_CITIES_PROVIDED, cities)); } /** * set the list of zones * @param countries/counties */ public voidsetCountries(List<County> countries){ handler.sendMessage(Message.obtain(handler, WHAT_COUNTIES_PROVIDED, countries)); } /** * set the street list * @param streets List */ public voidsetStreets(List<Street> streets){
handler.sendMessage(Message.obtain(handler, WHAT_STREETS_PROVIDED, streets));
}
Copy the code
Write in the last
Although gradle compile provides the way to import, but the entire control source is actually very simple. If you have custom requirements, you can download the source code here.
Project source code download: ZRAddressSelector
Special thanks to
JDAddressSelector, this component design is also inspired by this open source project, very grateful to the author open source ~