1、需求背景
app中有一个列表,里面内容较多,但是每个用户实际去操作的内容可能就那么几个,也可能随着时间变化而变化,因此用户想在进入列表之前有一个快捷入口来显示用户常操作的几项内容。
2、需求定义
和项目经理讨论后最终决定在原先入口界面的上级菜单中最多显示四个最近操作项的快捷操作入口,没有就不显示,即0~4个选项,并且要区分用户和区分站点,而且需要持久化记录操作,用户退出程序后重新进入还需要保存之前的记录。
3、设计实现
在界面这块,刚开始想着是不是要用代码来动态设置布局,即布局所有的参数都用代码动态设置,后来由于自己对布局的操作还不是那么熟练,加之需求实现时间有限,显示布局比较固定,因此直接在原来的布局中增加了组件,然后用代码来控制显示和隐藏:
数据实现方面,从需求的理解来看,需要一个存储四个对象的容器,并且不断更新,老的数据将新的数据替换掉,是要用队列还是栈模型来实现,当然用队列,先进先出,久远的操作早就被挤出去了。然后就想着是不是有定义固定大小的队列,发现好像没有,于是就用代码自己定义了,使用的是工具类:
Deque 的实现类 LinkedList
https://developer.android.google.cn/reference/java/util/LinkedList
//limit the size
if (mRecentUsedInspectDeque.size() >= MAX_RECENT_USED_INSPECT_COUNT) {
mRecentUsedInspectDeque.poll();
mRecentUsedInspectDeque.offer(rti);
} else {
mRecentUsedInspectDeque.offer(rti);
}
在后来的调试中还发现需要进行去重处理,由于是对象的比较,所以重写了对象类中的equals方法,根据网上的资料,重写equals后需要重新hashCode方法:
//remove duplicated
if(mRecentUsedInspectDeque.contains(rti)){
mRecentUsedInspectDeque.remove(rti);
}
// add for recent used remove duplicate data
@Override
public boolean equals(Object obj){
if(!(obj instanceof RealTimeInspect)){
return false;
}
RealTimeInspect rti = (RealTimeInspect)obj;
//此处比较String类型属性的时候不能使用==比较,必须使用equals方法
//比较,==号比较的是String的地址
return this.getName().equals(rti.getName())
&& this.getUrl().equals(rti.getUrl())
&& this.getId().equals(rti.getId());
}
@Override
public int hashCode(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return Objects.hash(id, name, url);
}else {
int result = 17;
result = 31 * result + id.hashCode();
result = 31 * result + name.hashCode();
result = 31 * result + url.hashCode();
return result;
}
}
为了实现持久化,需要根据需求分站分用户分别存储各自的操作,将最近使用记录持久化到文件中进行读取和保存操作处理:
/**
* 当用户登陆选择站点之后,需要根据原先存储的ini文件初始化最近使用数据
*/
public void initRecentUsedInspectData(){
// TODO: init recent used inspect data from ini before initview
mRecentUsedInspectDeque.clear();
String stationId = EBApplication.getInstance().getStationId();
String userId = EBApplication.getInstance().getUser().getId();
String basePath = "xxxx/ini/"+ URLUtil.getServerIP()+ "/" + userId + "/" + stationId;
File baseFile = new File(Environment.getExternalStorageDirectory(), basePath);
File recentUsedInspectFile = new File(baseFile, "recentUsedInspect.ini");
//saved the recent used inspect ini
IniFile recentUsedInspectIni = new IniFile(recentUsedInspectFile);
//section name
String[] sectionNameForIni = new String[]{"RecentUsed0", "RecentUsed1", "RecentUsed2", "RecentUsed3"};
for (int i = MAX_RECENT_USED_INSPECT_COUNT-1; i >= 0; i--){
Object menuName = recentUsedInspectIni.get(sectionNameForIni[i], "menuName");
Object url = recentUsedInspectIni.get(sectionNameForIni[i], "url");
if ((GeneralUtils.isNotNull(menuName) && !menuName.toString().equals(""))
&& (GeneralUtils.isNotNull(url) && !url.toString().equals(""))){
RealTimeInspect rti = new RealTimeInspect("0", (String)menuName, (String)url);
//init data, no need to check duplicate, but the size need control.
if (mRecentUsedInspectDeque.size() >= MAX_RECENT_USED_INSPECT_COUNT) {
mRecentUsedInspectDeque.poll();
mRecentUsedInspectDeque.offer(rti);
} else {
mRecentUsedInspectDeque.offer(rti);
}
}else {
Log.d("EBApplication","no recent used inspect data for " + sectionNameForIni[i]);
}
}
}
/**
* 当用户退出主界面时,需要保存用户之前点击过的实时画面
*/
public void saveRecentUsedInspectDataToIni(){
long start = System.currentTimeMillis();
String stationId = EBApplication.getInstance().getStationId();
String userId = EBApplication.getInstance().getUser().getId();
String basePath = "xxxx/ini/"+ URLUtil.getServerIP()+ "/" + userId + "/" + stationId;
File baseFile = new File(Environment.getExternalStorageDirectory(), basePath);
File recentUsedInspectFile = new File(baseFile, "recentUsedInspect.ini");
IniFile recentUsedInspectIni = new IniFile(recentUsedInspectFile);
Iterator<RealTimeInspect> tempIterator = mRecentUsedInspectDeque.descendingIterator();
String[] sectionNameForIni = new String[]{"RecentUsed0", "RecentUsed1", "RecentUsed2", "RecentUsed3"};
int index = 0;
while (tempIterator.hasNext()){
RealTimeInspect temp = tempIterator.next();
if (null != temp){
String menuNameTemp = temp.getName();
String urlTemp = temp.getUrl();
recentUsedInspectIni.set(sectionNameForIni[index], "url", urlTemp);
recentUsedInspectIni.set(sectionNameForIni[index], "menuName", menuNameTemp);
index++;
}
}
try {
recentUsedInspectIni.save(new File(GeneralUtils.getIniPath(basePath),"recentUsedInspect.ini"));
} catch (IOException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
Log.d("EBApplication", "saveRecentUsedInspectDataToIni spent time=" + (end - start) + "ms");
}
需求的实现方式可能很多,但是需要因时因地使用合适的,当然考虑问题要全面,不要因为一个简单的功能造成后期多方面的修补。