效果动图:
(后面补上)
总体布局:
Fragment中实现:
listCitylist = (ListView) mView.findViewById(R.id.list_citylist);
listCitylist.addHeaderView(headerView);
listCitylist.setOnScrollListener(onScrollListener);
/**
* 城市列表适配
*/
private void setCityListAdapter() {
if (null == cityListAdapter) {
cityListAdapter = new CityListAdapter(getActivity(), cityListDatas);
listCitylist.setAdapter(cityListAdapter);
}else {
//第二次进入城市列表的时候,list和adapter实例还在,但是list和adapter失去关联,所以要重新绑定
listCitylist.setAdapter(cityListAdapter);
cityListAdapter.updateData(cityListDatas);
}
cityListAdapter.setOnCityListClickListener(onCityListItemClickListener);
zMIndex = cityListAdapter.getzMIndexs();
letter.bringToFront();
}
/**
* 城市列表点击事件
*/
private CityListAdapter.OnCityListItemClickListener onCityListItemClickListener = new CityListAdapter.OnCityListItemClickListener() {
@Override
public void onCityListClickListener(View v) {
CityInfo c = (CityInfo) v.getTag();
((CommunityManagerActivityNew)getActivity()).formCityListToSelectComm(c.name, c.code);
}
};
/**
* 城市列表滑动监听,控制字母吸顶
*/
AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL://拖动
case AbsListView.OnScrollListener.SCROLL_STATE_FLING://惯性滑动
// 显示滑动时屏幕可见条目中离标题栏最近的第一行
int position = listCitylist.getFirstVisiblePosition();
//由于listview添加了一个header,所以position=0代表的是header
//这里获取到的position和下面onscroll方法里的firstVisibleItem值一致
break;
case AbsListView.OnScrollListener.SCROLL_STATE_IDLE://手指离开或者惯性滑动停止
break;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem > 0) {
topTitle.setVisibility(View.VISIBLE);
topTitle.bringToFront();
if(firstVisibleItem >= zMIndex[25] + 1 && zMIndex[25] != 0) {
topTitle.setText(letters[25]);
}else if(firstVisibleItem >= zMIndex[24] + 1 && zMIndex[24] != 0) {
topTitle.setText(letters[24]);
}else if(firstVisibleItem >= zMIndex[23] + 1 && zMIndex[23] != 0) {
topTitle.setText(letters[23]);
}else if(firstVisibleItem >= zMIndex[22] + 1 && zMIndex[22] != 0) {
topTitle.setText(letters[22]);
}else if(firstVisibleItem >= zMIndex[21] + 1 && zMIndex[21] != 0) {
topTitle.setText(letters[21]);
}else if(firstVisibleItem >= zMIndex[20] + 1 && zMIndex[20] != 0) {
topTitle.setText(letters[20]);
}else if(firstVisibleItem >= zMIndex[19] + 1 && zMIndex[19] != 0) {
topTitle.setText(letters[19]);
}else if(firstVisibleItem >= zMIndex[18] + 1 && zMIndex[18] != 0) {
topTitle.setText(letters[18]);
}else if(firstVisibleItem >= zMIndex[17] + 1 && zMIndex[17] != 0) {
topTitle.setText(letters[17]);
}else if(firstVisibleItem >= zMIndex[16] + 1 && zMIndex[16] != 0) {
topTitle.setText(letters[16]);
}else if(firstVisibleItem >= zMIndex[15] + 1 && zMIndex[15] != 0) {
topTitle.setText(letters[15]);
}else if(firstVisibleItem >= zMIndex[14] + 1 && zMIndex[14] != 0) {
topTitle.setText(letters[14]);
}else if(firstVisibleItem >= zMIndex[13] + 1 && zMIndex[13] != 0) {
topTitle.setText(letters[13]);
}else if(firstVisibleItem >= zMIndex[12] + 1 && zMIndex[12] != 0) {
topTitle.setText(letters[12]);
}else if(firstVisibleItem >= zMIndex[11] + 1 && zMIndex[11] != 0) {
topTitle.setText(letters[11]);
}else if(firstVisibleItem >= zMIndex[10] + 1 && zMIndex[10] != 0) {
topTitle.setText(letters[10]);
}else if(firstVisibleItem >= zMIndex[9] + 1 && zMIndex[9] != 0) {
topTitle.setText(letters[9]);
}else if(firstVisibleItem >= zMIndex[8] + 1 && zMIndex[8] != 0) {
topTitle.setText(letters[8]);
}else if(firstVisibleItem >= zMIndex[7] + 1 && zMIndex[7] != 0) {
topTitle.setText(letters[7]);
}else if(firstVisibleItem >= zMIndex[6] + 1 && zMIndex[6] != 0) {
topTitle.setText(letters[6]);
}else if(firstVisibleItem >= zMIndex[5] + 1 && zMIndex[5] != 0) {
topTitle.setText(letters[5]);
}else if(firstVisibleItem >= zMIndex[4] + 1 && zMIndex[4] != 0) {
topTitle.setText(letters[4]);
}else if(firstVisibleItem >= zMIndex[3] + 1 && zMIndex[3] != 0) {
topTitle.setText(letters[3]);
}else if(firstVisibleItem >= zMIndex[2] + 1 && zMIndex[2] != 0) {
topTitle.setText(letters[2]);
}else if(firstVisibleItem >= zMIndex[1] + 1 && zMIndex[1] != 0) {
topTitle.setText(letters[1]);
}else if(firstVisibleItem >= zMIndex[0] + 1) {
topTitle.setText(letters[0]);
}
} else {
topTitle.setVisibility(View.GONE);
}
}
};
城市实体类:
/**
* Created by Luzj on 2018/8/23.
* 城市列表item
*/
public class CityInfo implements Serializable,Comparable {
/**
* 省市区标识
*/
public int regionId;
/**
* 层级:0级为省、1级为市、2级为区
*/
public int level;
/**
* 城市名称
*/
public String name;
/**
* 拼音简称
*/
public String pySname;
/**
* 编码
*/
public String code;
/**
* 状态:0是无效,1是有效
*/
public int status;
/**
* item类型:0为字母,1为城市带下黑线,2为城市不带下黑线
*/
public int infoType;
@Override
public int compareTo(@NonNull CityInfo o) {
CityInfo cityInfo = o;
return this.pySname.compareTo(o.pySname);
}
}
附加Adapter(因城市列表数据给的是单独无序的一个列表,没有做字母排序,更没有用字母分字列表,所以拿到数据之后还要做按字母排序、分字母):
/**
* Created by Luzj on 2018/8/23.
*/
public class CityListAdapter extends BaseAdapter {
private Context context;
/**
* 接口返回数据
*/
private List datas;
/**
* 接口数据加上字母
*/
private ArrayList allDatas = new ArrayList<>();
/**
* item type,0为字母,1为带下黑线,2不带下黑线
*/
private final int ZIMU = 0, ITEM_LINE = 1, ITEM_NOLINE = 2;
/**
* 字母所带列表
*/
private ArrayList aL = new ArrayList<>();
private ArrayList bL = new ArrayList<>();
private ArrayList cL = new ArrayList<>();
private ArrayList dL = new ArrayList<>();
private ArrayList eL = new ArrayList<>();
private ArrayList fL = new ArrayList<>();
private ArrayList gL = new ArrayList<>();
private ArrayList hL = new ArrayList<>();
private ArrayList iL = new ArrayList<>();
private ArrayList jL = new ArrayList<>();
private ArrayList kL = new ArrayList<>();
private ArrayList lL = new ArrayList<>();
private ArrayList mL = new ArrayList<>();
private ArrayList nL = new ArrayList<>();
private ArrayList oL = new ArrayList<>();
private ArrayList pL = new ArrayList<>();
private ArrayList qL = new ArrayList<>();
private ArrayList rL = new ArrayList<>();
private ArrayList sL = new ArrayList<>();
private ArrayList tL = new ArrayList<>();
private ArrayList uL = new ArrayList<>();
private ArrayList vL = new ArrayList<>();
private ArrayList wL = new ArrayList<>();
private ArrayList xL = new ArrayList<>();
private ArrayList yL = new ArrayList<>();
private ArrayList zL = new ArrayList<>();
private static final String[] letters = {"A","B","C","D","E","F","G","H","I","J","K","L"
,"M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
/**
* 总列表,每个元素都是一个字母列表
*/
private ArrayList zMList = new ArrayList<>();
/**
* 字母下标位置数组
*/
private int[] zMIndexs = new int[26];
public CityListAdapter(Context context, List datas) {
this.context = context;
this.datas = datas;
if(null != datas && datas.size() >= 0) {
initDatas();
}
}
private void initDatas() {
String firstChar;
StringBuffer sb;
//城市数据按照字母排序
Collections.sort(datas);
//获取每个城市首字母进行归类
for(int i = 0; i < datas.size(); i++) {
CityInfo c = datas.get(i);
sb = new StringBuffer(c.pySname);
firstChar = sb.delete(1, sb.length()).toString();
if("a".equals(firstChar)) {
aL.add(c);
}else if("b".equals(firstChar)) {
bL.add(c);
}else if("c".equals(firstChar)) {
cL.add(c);
}else if("d".equals(firstChar)) {
dL.add(c);
}else if("e".equals(firstChar)) {
eL.add(c);
}else if("f".equals(firstChar)) {
fL.add(c);
}else if("g".equals(firstChar)) {
gL.add(c);
}else if("h".equals(firstChar)) {
hL.add(c);
}else if("i".equals(firstChar)) {
iL.add(c);
}else if("j".equals(firstChar)) {
jL.add(c);
}else if("k".equals(firstChar)) {
kL.add(c);
}else if("l".equals(firstChar)) {
lL.add(c);
}else if("m".equals(firstChar)) {
mL.add(c);
}else if("n".equals(firstChar)) {
nL.add(c);
}else if("o".equals(firstChar)) {
oL.add(c);
}else if("p".equals(firstChar)) {
pL.add(c);
}else if("q".equals(firstChar)) {
qL.add(c);
}else if("r".equals(firstChar)) {
rL.add(c);
}else if("s".equals(firstChar)) {
sL.add(c);
}else if("t".equals(firstChar)) {
tL.add(c);
}else if("u".equals(firstChar)) {
uL.add(c);
}else if("v".equals(firstChar)) {
vL.add(c);
}else if("w".equals(firstChar)) {
wL.add(c);
}else if("x".equals(firstChar)) {
xL.add(c);
}else if("y".equals(firstChar)) {
yL.add(c);
}else if("z".equals(firstChar)) {
zL.add(c);
}
}
//依次将字母列表顺序添加进总列表
zMList.add(aL); zMList.add(bL); zMList.add(cL); zMList.add(dL); zMList.add(eL);
zMList.add(fL); zMList.add(gL); zMList.add(hL); zMList.add(iL); zMList.add(jL); zMList.add(kL);
zMList.add(lL); zMList.add(mL); zMList.add(nL); zMList.add(oL); zMList.add(pL);
zMList.add(qL); zMList.add(rL); zMList.add(sL); zMList.add(tL); zMList.add(uL);
zMList.add(vL); zMList.add(wL); zMList.add(xL); zMList.add(yL); zMList.add(zL);
CityInfo cZiMu;
CityInfo c;
for(int i = 0; i < letters.length; i++) {
if(zMList.get(i).size() > 0) {
//每开始取一个字母列表,先添加进该字母
cZiMu = new CityInfo();
cZiMu.infoType = 0;
cZiMu.name = letters[i];
allDatas.add(cZiMu);
zMIndexs[i] = allDatas.indexOf(cZiMu);
for(int j = 0; j < zMList.get(i).size(); j++) {
c = (CityInfo) zMList.get(i).get(j);
if(j == (zMList.get(i).size() - 1)) {
//当该城市是该字母列表最后一个时,取消下黑线
c.infoType = 2;
}else {
c.infoType = 1;
}
allDatas.add(c);
}
}
}
}
/**
* @return 字母下标数组
*/
public int[] getzMIndexs() {
return zMIndexs;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = allDatas.get(position).infoType;
if(ZIMU == type) {
convertView = getZiMuView(position, convertView, parent);
}else {
convertView = getItemLineView(position, convertView, parent);
}
return convertView;
}
public void updateData(List datas) {
this.datas = datas;
notifyDataSetChanged();
}
private Map letterIndex = new HashMap<>();
private View getZiMuView(int position, View convertView, ViewGroup parent) {
ZiMuHolder zM;
if(null == convertView) {
convertView = LayoutInflater.from(context).inflate(R.layout.citylist_item0, null);
zM = new ZiMuHolder(convertView);
convertView.setTag(zM);
}else {
zM = (ZiMuHolder) convertView.getTag();
}
zM.citylistZm.setText(allDatas.get(position).name);
return convertView;
}
private View getItemLineView(int position, View convertView, ViewGroup parent) {
CityListItem1 c1;
if(null == convertView) {
convertView = LayoutInflater.from(context).inflate(R.layout.citylist_item1, parent, false);
c1 = new CityListItem1(convertView);
convertView.setTag(c1);
}else {
c1 = (CityListItem1) convertView.getTag();
}
c1.citylistItem1.setText(allDatas.get(position).name);
c1.citylistItem1.setTag(allDatas.get(position));
if(ITEM_LINE == allDatas.get(position).infoType) {
c1.line.setVisibility(View.VISIBLE);
}
return convertView;
}
@Override
public int getItemViewType(int position) {
CityInfo cityInfo = allDatas.get(position);
int type = cityInfo.infoType;
if(ZIMU == type) {
return ZIMU;
}else if(ITEM_LINE == type) {
return ITEM_LINE;
}else {
return ITEM_NOLINE;
}
}
@Override
public int getViewTypeCount() {
return 3;
}
@Override
public int getCount() {
return null == allDatas ? 0 : allDatas.size();
}
@Override
public Object getItem(int position) {
if(null == allDatas) {
return null;
}else {
return allDatas.get(position);
}
}
@Override
public long getItemId(int position) {
return position;
}
public List getAllDatas() {
return allDatas;
}
class ZiMuHolder {
TextView citylistZm;
ZiMuHolder(View v) {
citylistZm = (TextView) v.findViewById(R.id.citylist_zm);
}
}
class CityListItem1 {
TextView citylistItem1;
View line;
CityListItem1(View v) {
citylistItem1 = (TextView) v.findViewById(R.id.citylist_item1);
citylistItem1.setOnClickListener(onClickListener);
line = v.findViewById(R.id.bottom_line);
line.setVisibility(View.GONE);
}
}
private View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
onCityListClickListener.onCityListClickListener(v);
}
};
private OnCityListItemClickListener onCityListClickListener;
public interface OnCityListItemClickListener {
void onCityListClickListener(View v);
}
public void setOnCityListClickListener(OnCityListItemClickListener listener) {
this.onCityListClickListener = listener;
}
}
后台数据接口:
{
"code":"mock", //类型:String 必有字段 备注:响应状态码 0成功、!=0 不成功
"message":"mock", //类型:String 必有字段 备注:响应提示消息
"data": - { //类型:Object 必有字段 备注:无
"cityList": - [ //类型:Array 必有字段 备注:城市列表
- { //类型:Object 必有字段 备注:无
"regionId":1, //类型:Number 必有字段 备注:省市区标识
"level":1, //类型:Number 必有字段 备注:层级:0级为省、1级为市、2级为区
"name":"mock", //类型:String 必有字段 备注:城市名称
"pySname":"mock", //类型:String 必有字段 备注:拼音简称
"code":"mock", //类型:String 必有字段 备注:编码
"status":1 //类型:Number 必有字段 备注:状态:0是无效,1是有效
}
]
}
}