閒話
之前沒做過聯動菜單,最近項目中要用到幾個,百度網上有不少現成的控件實現,有用到spinner的,還有用到其他控件的,不過風格不是我需要的,索性自己來實現一下,雖然不如其他大神的方法簡便,但自己寫的,還是更喜歡。
正文
製作數據源
首先要有級聯用的數據源,我這裏有一份,我忘記是從哪位大神資源上下載的了,感謝,自己製作一份json數據也行:
省市區json下載
可以把這份json文檔放在項目目錄的assets下,以便調取。
佈局文件
因爲用到listview作爲展示級聯的控件,因此,主佈局很簡單,只用建立3個listview。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:background="@color/colorBackground">
<ListView
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:divider="@color/colorBackground"
android:dividerHeight="2dp"
android:id="@+id/lvProvince" />
<ListView
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:divider="@color/colorBackground"
android:dividerHeight="2dp"
android:id="@+id/lvCity"/>
<ListView
android:layout_marginLeft="5dp"
android:layout_marginRight="10dp"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:divider="@color/colorBackground"
android:dividerHeight="2dp"
android:id="@+id/lvArea"/>
</LinearLayout>
然後是item子佈局,寫demo懶一些,每個listview公用一個item佈局。
我寫佈局的時候,不知道爲什麼,如果把textview充滿父佈局,然後讓文字居中顯示android:gravity=”center”,這樣寫感覺沒什麼問題,但實際測試時發現,第一次展示數據的時候,不會有什麼問題,但是點擊幾次之後,有些item就不會在執行居中操作了,很奇怪,我也不知道爲什麼,所以才修改位textview自適應,佈局居中android:layout_centerInParent=”true”
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorWhite">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="@color/colorWord"
android:textSize="16sp"
android:text="省市區"
android:gravity="center"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:id="@+id/tvPCA"/>
</RelativeLayout>
Adapter
adapter也很簡單,繼承BaseAdapter。怎樣給選中的item修改顏色,以便提醒用戶,我的處理方法是在adapter中添加一個方法setSelectPosition(int position),傳入點擊的item,在getView中,判斷當前item是否是點擊的item,如果是,就設置選中的顏色。稍微有點麻煩,不過效果還可以。
public void setSelectPosition(int position){
this.selectPosition=position;
}
整個adapter代碼
public class AdapterForPCA extends BaseAdapter {
private List<String> list=null;
private Context context;
private int selectPosition;
public AdapterForPCA(List<String> list, Context context){
this.list=list;
this.context=context;
}
public void setSelectPosition(int position){
this.selectPosition=position;
}
@Override
public int getCount() {
if (list!=null){
return list.size();
}
return 0;
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder=null;
if (view==null){
view= LayoutInflater.from(context).inflate(R.layout.item_listview_province_city_area,null);
viewHolder=new ViewHolder();
viewHolder.tvPCA=Utils.findView(view,R.id.tvPCA);
view.setTag(viewHolder);
}else {
viewHolder=(ViewHolder)view.getTag();
}
viewHolder.tvPCA.setText(list.get(i));
viewHolder.tvPCA.setGravity(Gravity.CENTER);
//爲了讓點擊的item顯示不同的顏色,必須在item點擊後 重新 notifyDataSetChange
if (selectPosition==i){
viewHolder.tvPCA.setBackgroundColor(context.getResources().getColor(R.color.colorButton));
view.setBackgroundColor(context.getResources().getColor(R.color.colorButton));
viewHolder.tvPCA.setTextColor(context.getResources().getColor(R.color.colorWhite));
}else {
viewHolder.tvPCA.setBackgroundColor(context.getResources().getColor(R.color.colorWhite));
view.setBackgroundColor(context.getResources().getColor(R.color.colorWhite));
viewHolder.tvPCA.setTextColor(context.getResources().getColor(R.color.colorWord));
}
return view;
}
class ViewHolder{
TextView tvPCA;
}
}
Activity應用
先從assets中讀取數據源
/**
* 從文本中讀取 省市區城市列表
* @return
*/
private String readPCA(){
InputStream in=null;
ByteArrayOutputStream out=null;
try {
in=getAssets().open("PCA.json");
out=new ByteArrayOutputStream();
byte[] b=new byte[1024];
int length=-1;
while ((length=in.read(b))!=-1){
out.write(b,0,length);
}
return new String(out.toByteArray());
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
讀取之後先存在保存爲JSON數組
try {
jsonArrayP=new JSONArray(readPCA());
} catch (JSONException e) {
e.printStackTrace();
}
然後分別獲取省市區的列表
/**
* 獲取省列表
*/
private void initListP(){
try {
listP.clear();
if (jsonArrayP!=null) {
for (int i = 0; i < jsonArrayP.length(); i++) {
JSONObject objP = jsonArrayP.getJSONObject(i);//獲取省份對象
listP.add(objP.getString("name"));//獲取省份名字
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* 根據點擊的省份,獲取該省的城市列表
* @param provinceNum
*/
private void setListC(int provinceNum){
listC.clear();
try {
jsonObjP=jsonArrayP.getJSONObject(provinceNum);//獲取點擊的省份對象
jsonArrayC=jsonObjP.getJSONArray("city");//獲取該省份的城市數組
for (int i=0;i<jsonArrayC.length();i++){
JSONObject objC=jsonArrayC.getJSONObject(i);//獲取城市數組的城市對象
listC.add(objC.getString("name"));//獲取城市名字
}
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* 根據點擊的城市獲得地區列表
* @param cityNum
*/
private void setListA(int cityNum){
listA.clear();
try {
jsonObjC=jsonArrayC.getJSONObject(cityNum);//根據點擊的城市對象
JSONArray arrayA=jsonObjC.getJSONArray("area");//獲取該城市的地區列表
for (int i=0;i<arrayA.length();i++){
listA.add(arrayA.getString(i));//添加地區列表到list
}
} catch (JSONException e) {
e.printStackTrace();
}
}
最後做初始化UI 和 listview的點擊事件,註釋比較詳細,就不一一說明了
private void initUI(){
try {
jsonArrayP=new JSONArray(readPCA());
} catch (JSONException e) {
e.printStackTrace();
}
listP=new ArrayList<>();
initListP();
listC=new ArrayList<>();
pro=0;
setListC(pro);//設置默認展示的省份,根據json數組的系列號
listA=new ArrayList<>();
city=0;
setListA(city);//設置默認展示哪個城市的地區
lvProvince=Utils.findView(this,R.id.lvProvince);
adapterP=new AdapterForPCA(listP,this);
adapterP.setSelectPosition(pro);//設置默認選中的省份變顏色
lvProvince.setAdapter(adapterP);
lvProvince.setOnItemClickListener(this);
lvCity=Utils.findView(this,R.id.lvCity);
adapterC=new AdapterForPCA(listC,this);
adapterC.setSelectPosition(city);//設置默認選中的城市變顏色
lvCity.setAdapter(adapterC);
lvCity.setOnItemClickListener(this);
lvArea=Utils.findView(this,R.id.lvArea);
adapterA=new AdapterForPCA(listA,this);
adapterA.setSelectPosition(area);//設置默認選中的地區變顏色
lvArea.setAdapter(adapterA);
lvArea.setOnItemClickListener(this);
}
/**
* 每個list 點擊事件
* @param adapterView
* @param view
* @param i
* @param l
*/
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
switch (adapterView.getId()){
case R.id.lvProvince://點擊省列表
pro=i;//獲得選擇的省的item
adapterP.setSelectPosition(pro);//爲了讓選擇的item顯示不同的顏色
//每一次選擇省,都需要把 之前選擇的市和區的item初始化爲0,默認選擇第一個
city=0;
area=0;
adapterC.setSelectPosition(city);//爲了讓選擇的item顯示不同的顏色
adapterA.setSelectPosition(area);//爲了讓選擇的item顯示不同的顏色
//獲得點擊的省份對應的 城市列表
setListC(pro);
//點擊省份,顯示城市,由於還沒有點擊城市,所以默認選擇第一個城市,展示第一個城市的區列表
setListA(city);
//點擊省份,需要三個listview都刷新data ,以便執行 setSelectPosition ,刷新選擇的項目的顏色
adapterP.notifyDataSetChanged();
adapterC.notifyDataSetChanged();
adapterA.notifyDataSetChanged();
break;
case R.id.lvCity:
city=i;//點擊的城市的item
area=0;//由於還沒有選擇區,所以區默認選擇第一個
adapterC.setSelectPosition(city);//爲了讓選擇的item顯示不同的顏色
adapterA.setSelectPosition(area);//爲了讓選擇的item顯示不同的顏色
setListA(i);//獲取選擇的城市的區列表
//點擊城市,需要刷新 城市 和 區 listview, 以便執行 setSelectPosition ,刷新選擇的項目的顏色
adapterC.notifyDataSetChanged();
adapterA.notifyDataSetChanged();
break;
case R.id.lvArea:
area=i;//點擊的區
adapterA.setSelectPosition(area);////爲了讓選擇的item顯示不同的顏色
//點擊城市,需要刷新 區 listview, 以便執行 setSelectPosition ,刷新選擇的項目的顏色
adapterA.notifyDataSetChanged();
Toast.makeText(MainActivity.this, listP.get(pro)+""+listC.get(city)+""+listA.get(area), Toast.LENGTH_SHORT).show();
break;
}
}
好了,這樣,一個簡單的級聯菜單就做好了,看看效果