特别感谢github项目
直接上效果图



使用到的第三方依赖,具体详情请参照片头github链接
compile 'com.github.mcxtzhang:SuspensionIndexBar:V1.0.0'
1.布局分析
布局主要由3部分组成,自定义搜索框,快速索引栏,以及recycleView组成。
主播代码如下:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="44dp"
android:background="@color/bg_white"
android:gravity="center_vertical">
<ImageView
android:id="@+id/tv_selectCity_back"
android:layout_width="25dp"
android:layout_height="25dp"
android:padding="3dp"
android:src="@mipmap/back_dark_gray" />
<com.wyw.selectcitydemo.EditTextWithDel
android:id="@+id/etd_search"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_marginLeft="13dp"
android:layout_marginRight="15dp"
android:background="@drawable/shape_corner20_gray"
android:hint="输入城市名查询"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:singleLine="true"
android:textColor="@color/font_black"
android:textSize="14sp" />
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white" />
<com.mcxtzhang.indexlib.IndexBar.widget.IndexBar
android:id="@+id/indexBar"
android:layout_width="24dp"
android:layout_height="match_parent"
android:layout_gravity="right"
app:indexBarPressBackground="#00000000"
app:indexBarTextSize="16sp" />
<TextView
android:id="@+id/tvSideBarHint"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_gravity="center"
android:gravity="center"
android:textColor="#7BA0F6"
android:textSize="48sp"
android:visibility="gone"
tools:text="A"
tools:visibility="visible" />
</FrameLayout>
</LinearLayout>
2.代码分析
由于本项目中要求有,当前定位的城市,热门城市,以及历史访问城市3个部分,而且样式与主体城市列表不一致,参加图1,我姑且认为3个部分为3个头布局区域。
2.1控件的初始化
//初始化recycleview的分割线
mDecoration = new CharDecoration(mActivity, mAllDatas);
mDecoration.setmDatas(mAllDatas);
//indexbar初始化
mIndexBar.setmPressedShowTextView(mTvSideBarHint)//设置HintTextView
.setNeedRealIndex(true)//设置需要真实的索引
.setmLayoutManager(mManager);
mCityRcv.setAdapter(mHeaderAdapter);
2.2头布局区域的实现
这3个头布局可以理解完成写死的3个部分。所以本博客只列举当前定位城市代码实现。
String localCityName = getIntent().getStringExtra("local_city");
//通过地理坐标获取当前城市
localCityName = TextUtils.isEmpty(localCityName) ? "定位失败" : localCityName;
CityInfoBean localCity = new CityInfoBean().setName(localCityName).setTop(true).setBaseIndexTag(INDEX_STRING_LOCAL);
值得注意的是,创建的javaBean对象需要按照开篇中GitHub链接中继承父类BaseIndexPinyinBean
public class CityInfoBean extends BaseIndexPinyinBean implements Serializable{
@Expose
protected String id;
@Expose
protected String name;
}
将3个已经创建好的数据填充到界面对应的视图中
mHeaderAdapter.setHeaderView(0, R.layout.meituan_item_header_local, localHeader);//对应的是当前城市的数据
mHeaderAdapter.setHeaderView(1, R.layout.meituan_item_header, hotHeader);//热门城市的数据
mHeaderAdapter.setHeaderView(2, R.layout.meituan_history_header, HistoryHeader);//历史城市的记录数据
//通过对recycleview的adapter添加heardview或者footerview
mHeaderAdapter = new HeaderRecyclerAndFooterWrapperAdapter(filterAdapter) {
@Override
protected void onBindHeaderHolder(ViewHolder holder, int headerPos, int layoutId, Object o) {
final CityHeaderBean meituanHeaderBean = (CityHeaderBean) o;
switch (layoutId) {
case R.layout.meituan_item_header_local: { //当前定位城市
RecyclerView recyclerView = holder.getView(R.id.rvCity);
recyclerView.setAdapter(
new CommonAdapter<CityInfoBean>(mActivity, R.layout.item_local_city, meituanHeaderBean.getCityList()) {
@Override
public void convert(ViewHolder holder, final CityInfoBean cityName) {
holder.setText(R.id.tv_city, cityName.getTarget());
}
});
recyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
}
break;
...其他略
2.3城市数据的填充
主体主要也是采用recycleview来承载数据,同样参照头部视图数据的方法,创建一个内部类FilterAdapter继承至于CommonAdapter(因为涉及到后面的筛选操作,所以就不创建匿名内部类了)
核心代码如下:
class FilterAdapter extends CommonAdapter<BaseIndexPinyinBean> {
private CityFilter cityFilter = new CityFilter();
public FilterAdapter(Context context, int layoutId, List<BaseIndexPinyinBean> datas) {
super(context, layoutId, datas);
}
@Override
public void convert(ViewHolder holder, BaseIndexPinyinBean cityInfo) {
holder.setText(R.id.tv_item_credit_city_name, cityInfo.getTarget());
holder.setVisible(R.id.tv_item_credit_city_tag, false);
holder.itemView.setTag(R.id.cb_item_tag, cityInfo);
holder.itemView.setOnClickListener(MainActivity.this);
}
}
2.4 城市筛选的实现
该功能主要是由文本输入框内的字符数据来控制,所以需要重写文本框的监听方法。通过文本框内数据来控制recycleview中的数据变化。
/**
* 核心方法:1、通过过滤条件初始化数据;2、通过过滤条件控制辅助View;3、应用数据到核心显示控件
*
* @param filter
*/
public void filter(String filter) {
if (StringUtils.isEmpty(filter)) {//无过滤条件
mAllDatas.add(0, localHeader);
mAllDatas.add(1, hotHeader);
mAllDatas.add(2, HistoryHeader);
mHeaderAdapter.setHeaderView(0, R.layout.meituan_item_header_local, localHeader);//对应的是当前城市的数据
mHeaderAdapter.setHeaderView(1, R.layout.meituan_item_header, hotHeader);//热门城市的数据
mHeaderAdapter.setHeaderView(2, R.layout.meituan_history_header, HistoryHeader);//历史城市的记录数据
mCityRcv.addItemDecoration(mDecoration);
mIndexBar.setVisibility(View.VISIBLE);
} else {//过滤条件有效,开始初始化过滤条件
mAllDatas.remove(localHeader);
mAllDatas.remove(hotHeader);
mAllDatas.remove(HistoryHeader);
mHeaderAdapter.clearHeaderView();
mCityRcv.removeItemDecoration(mDecoration);
mIndexBar.setVisibility(View.GONE);
}
/**
* 应用过滤条件到各个View上
*/
mIndexBar.setmSourceDatas(mAllDatas).invalidate();
mIndexBar.getDataHelper().sortSourceDatas(mBodyDatas);
filterAdapter.setFilter(filter);
mHeaderAdapter.notifyDataSetChanged();
}
在内部类FilterAdapter中添加方法,以便控制adapter中的不同数据源在recycleview中展示。(图3)
public void setFilter(String filter) {
List<BaseIndexPinyinBean> datas = new ArrayList<>();
if (TextUtils.isEmpty(filter)) {
datas.clear();
datas.addAll(mAllDatas);
} else {
List<BaseIndexPinyinBean> temp = CollectionUtils.filter(mAllDatas, cityFilter.setFilter(filter));
datas.clear();
datas.addAll(temp);
}
//从过滤掉的数据中去除上面三区域的数据
datas.remove(localHeader);
datas.remove(hotHeader);
datas.remove(HistoryHeader);
setDatas(datas);
}
2.5 历史城市的选择实现
历史城市,我采用的是SharedPreferences来存储我们历史点击过的城市,当然我们也需要对点击过的城市,进行过滤操作,避免历史记录中存储相同的城市。
/**
* 添加城市到历史记录中去
*
* @param cityInfo
*/
private void addCity2History(CityInfoBean cityInfo) {
boolean notExit = true;
for (BaseIndexPinyinBean city : historyCitys) {
if (city instanceof CityInfoBean) {
String cityName = ((CityInfoBean) city).getName();
if (!TextUtils.isEmpty(cityName) && cityName.contains(cityInfo.getName())) {
notExit = false;
break;
}
}
}
if (notExit) {
if (historyCitys.size() >= 6) { //当历史记录里面有没有该城市的时候
ArrayList<CityInfoBean> tempLists = new ArrayList<>();
historyCitys.remove(0);
tempLists.addAll(historyCitys);
tempLists.add(cityInfo);
historyCitys.clear();
historyCitys.addAll(tempLists);
} else {
historyCitys.add(cityInfo);
}
}
String historyCityStr = JsonUtils.toJson(historyCitys);
sharedPreUtils.saveValue(CITY_KEY, historyCityStr);
}
最后附上完整的代码链接完整项目demo
网友评论