首先附上一张效果图;
主要功能介绍:
最顶部始终显示一个当前的分组,并且当下一个分组即将完全“推”上去上一个分组、或者下一个分组即将被“拉”下来的时候,最顶部的view会有一个退出和进入的动画效果;
列表的顶部是一个GridView,可对其进行另外的定制,比如显示热门的城市、热门的商品等效果;
对列表的图片加载进行了优化、加入了磁盘缓存、内存缓存、图片压缩、以及列表快速的大量的滑动过程中进行了图片加载的优化,解决列表卡顿的问题;
右边的字母栏列表可以快速的对列表的数据进行定位搜索,便于查找;
接下来简单谈谈具体的实现过程;
第一步:滑动列表listview(不包括右侧的拼音栏)的实现:上面的9个热门城市采用了GridView来实现,加载之后,调用listview.addHeaderView完成添加;
这一步需要注意的地方,gridview需要重写其onmeasure方法;
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = View.MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
View.MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
第二步:右边字母栏与滑动列表的联动实现,这一步最主要的就是,对源数据的分组及对分组控制;系统也为我们提供了一个SectionIndexer的分组接口,并且也为我们提供了一个具体的实现——AlphabetIndexer,直接采用AlphabetIndexer,也可以自己去实现接口的功能;
第三步:对于“大数据”的支持,主要工作是进行图片方面的优化;包括:
A:图片缓存,包括内存缓存和磁盘缓存,分别对应于LruCache和DiskLruCache;LruCache系统本身就为我们提供的有了,于此不在多说;DiskLruCache系统本身没有为我们提供,但是却得到了Android官方的推荐,所以需要我们自己的引入,在此,引入的是Jake warthon的开源版本(compile 'com.jakewharton:disklrucache:2.0.2'),github地址为https://github.com/JakeWharton/DiskLruCache;在此项目中,只做了初始化的工具,具体的缓存实现还没有完成,请知悉:
B:优化卡顿,解决思路就是不要在UI线程中做太耗时的操作即可提高滑动的流畅度,主要包含几个反面;
B1:不要在Adapter的getview方法中执行耗时操作,此案例中就是不要在getview方法中加载图片,需要异步的方式来实现;
B2:控制异步任务的执行频率;如果用户频繁快速的进行列表滑动,也会瞬间产生上百个异步任务,这些异步任务会导致线程池的拥堵并随即带来大量的UI更新操作;就会造成一定程度的卡顿。思路就是在列表滑动停止的时候再加载图片;首先,给listview设置setonscrolllistener(),并在onscrolllistener的onscrollstatechanged方法中判断列表是否处于滑动状态,如果是就停止加载图片;
然后,在getview方法中,仅当列表静止的时候再加载图片;
//只有在列表静止的时候,我们再加载图片;
if (isListViewIdle){
//加载图片操作
}
C:图片压缩,其实现也很简单;
public Bitmap decodeSampleBitmapFromResource(Resources resources,int resId,int reqWidth,int reqHeight){
final BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeResource(resources,resId,options);
options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight);
options.inJustDecodeBounds=false;
return BitmapFactory.decodeResource(resources,resId,options);
}
第四步:分组之间挤压动画的实现;属于润色的功能;其具体实现也很简单,首先看看布局情况;
当下一个分组与顶部的分组接触时,顶部的view通过计算滑动距离,不断的上移就可以了,代码如下:
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem != lastFirstVisibleItem) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) title_ll.getLayoutParams();
params.topMargin = 0;
title_ll.setLayoutParams(params);
}
int section = adapter.getSectionForPosition(firstVisibleItem);
int lastSection ;
if (firstVisibleItem==0){
lastSection=-1;
title_tv.setText("热门城市");
}else {
lastSection= adapter.getSectionForPosition(firstVisibleItem - 1);
title_tv.setText(adapter.getHeaderText(firstVisibleItem-1));
}
if (lastSection != section) {
View childView = view.getChildAt(0);
if (childView != null) {
int titleHeight = title_ll.getHeight();
int bottom = childView.getBottom();
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) title_ll
.getLayoutParams();
if (bottom < titleHeight) {
float pushedDistance = bottom - titleHeight;
params.topMargin = (int) pushedDistance;
title_ll.setLayoutParams(params);
} else {
if (params.topMargin != 0) {
params.topMargin = 0;
title_ll.setLayoutParams(params);
}
}
}
}
lastFirstVisibleItem = firstVisibleItem;
}
最后附上demo的github地址:https://github.com/jyvvip/address-list-and-city-selector,欢迎各位看官star!