android中利用实现二级联动的效果

时间:2023-03-08 16:18:21
android中利用实现二级联动的效果

按照惯例,首先上一张效果图。

android中利用实现二级联动的效果

本篇文章实现的效果就是如图中所圈的那样,实现类似于HTML中的二级联动的效果。

对于第一个选项我们读取的是本地xml文件来填充数据的,

对于第二个选项我们读取的是通过*气象台提供的API返回的xml格式的数据来填充的。

首先是主页面排版,由于我做的是一个天气预报的功能,所以添加了很多与本文无关的控件,在代码注释中写的很清楚,大家可以直接略过。

public class WeatherPage extends RelativeLayout{
private Context parentContext; /**监听*/
private MyButtonListen mylisten; /**定义天气对象*/
private GetWeatherService service; private RelativeLayout toplayout;
private int toplayoutid=100;
private RelativeLayout centerlayout;
private int centerlayoutid=200;
private RelativeLayout footlayout;
private int footlayoutid=300; /** topView*/
private ImageView dogpetimg;
private TextView titleview;
private ImageButton switchBtn; /** showweatherView*/
private RelativeLayout showWeather;
private int showWeatherid=201;
private TextView datetext; /** functionView*/
private LinearLayout functionLayout;
private TextView selectCitytext;
private Spinner selectProvince;
private ArrayAdapter<CharSequence> adapterProvince;
private Map<String,String> provincemap; private Spinner selectCity;
private Map<Integer,CityWeather> citymap;
private ArrayAdapter<CharSequence> adapterCity; private ImageButton okBtn;
private ImageButton selectBtn; /** 本类对象的样式 */
private LayoutParams lp;//准备放入ViewFlipper中,所以暂且准备一下, public WeatherPage(Context context) {
super(context);
this.parentContext=context;
mylisten=new MyButtonListen();
service=new GetWeatherService();
init();
}
private void init() {
toplayout=new RelativeLayout(parentContext);
toplayout.setId(toplayoutid);
LayoutParams lptoplayout=new LayoutParams(-1,-2);
lptoplayout.setMargins(0, 20, 0, 0);
//添加组件
addTopLayout(lptoplayout);
addView(toplayout,lptoplayout); centerlayout=new RelativeLayout(parentContext);
centerlayout.setId(centerlayoutid);
LayoutParams lpcenterlayout=new LayoutParams(-1,370);
lpcenterlayout.setMargins(0, 30, 0, 0);
lpcenterlayout.addRule(RelativeLayout.BELOW,toplayoutid);
//添加组件
addCenterLayout(lpcenterlayout);
addView(centerlayout,lpcenterlayout); footlayout=new RelativeLayout(parentContext);
footlayout.setBackgroundColor(Color.RED);
footlayout.setId(footlayoutid);
LayoutParams lpfootlayout=new LayoutParams(-1,-2);
lpfootlayout.setMargins(20, 10, 20, 0);
//添加组件
addFootLayout(lpfootlayout);
lpfootlayout.addRule(RelativeLayout.BELOW,centerlayoutid);
addView(footlayout,lpfootlayout); } public LayoutParams getLp() {
this.lp=new LayoutParams(-1,-1);
return lp;
} public void addTopLayout(LayoutParams lp){
dogpetimg=new ImageView(parentContext);
LayoutParams lpdogpetimg=new LayoutParams(60,60);
lpdogpetimg.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
dogpetimg.setBackgroundResource(R.drawable.dogsmall);
lpdogpetimg.setMargins(10, 0, 0, 0); titleview=new TextView(parentContext);
titleview.setText("天气预报");
titleview.setTextColor(Color.BLUE);
LayoutParams lptitleview=new LayoutParams(-2,-2);
lptitleview.addRule(RelativeLayout.CENTER_HORIZONTAL); switchBtn=new ImageButton(parentContext);
//先进行判断,判断原来的开关状态,然后再添加背景图片,标记位设置在helper类中。
switchBtn.setBackgroundResource(R.drawable.start);
LayoutParams lpswitchBtn=new LayoutParams(40,40);
lpswitchBtn.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
lpswitchBtn.setMargins(0, 20, 50, 0);
//添加监听
switchBtn.setOnClickListener(mylisten); toplayout.addView(dogpetimg,lpdogpetimg);
toplayout.addView(titleview, lptitleview);
toplayout.addView(switchBtn,lpswitchBtn); } public void addCenterLayout(LayoutParams lp) {
showWeather=new RelativeLayout(parentContext);
showWeather.setId(showWeatherid);
LayoutParams lpshowWeather=new LayoutParams(400,300);
lpshowWeather.addRule(RelativeLayout.CENTER_HORIZONTAL);
showWeather.setBackgroundColor(Color.CYAN); datetext=new TextView(parentContext);
LayoutParams lpdatetext=new LayoutParams(400,-2);
lpdatetext.addRule(RelativeLayout.CENTER_HORIZONTAL);
lpdatetext.addRule(RelativeLayout.BELOW,showWeatherid);
lpdatetext.setMargins(20, 20, 20, 0);
// datetext.setBackgroundColor(Color.LTGRAY);
datetext.setText(TimeHelper.getDateInChina());
datetext.setGravity(Gravity.CENTER_HORIZONTAL); centerlayout.addView(showWeather, lpshowWeather);
centerlayout.addView(datetext, lpdatetext);
} public void addFootLayout(LayoutParams lp) { functionLayout=new LinearLayout(parentContext);
functionLayout.setId(301);
// functionLayout.setBackgroundColor(Color.YELLOW);
LayoutParams lpfunctionLayout=new LayoutParams(-2,-2);
lpfunctionLayout.addRule(RelativeLayout.ALIGN_PARENT_TOP);
lpfunctionLayout.addRule(RelativeLayout.CENTER_HORIZONTAL);
lpfunctionLayout.setMargins(10, 0, 0, 0); //添加显示文字
selectCitytext=new TextView(parentContext);
selectCitytext.setText("请设置:"); //添加选择省
selectProvince=new Spinner(parentContext);
selectProvince.setPrompt("请选择省份");
//获取省份Map<序列号,省份对象>
provincemap=service.getProvinceMap();
String[] provinceData=service.getProvinceArray(provincemap);
//定义下拉列表适配器,用于填充内容
adapterProvince=null;
adapterProvince=new ArrayAdapter<CharSequence>(parentContext,android.R.layout.simple_spinner_item,provinceData);
//设置列表显示风格
adapterProvince.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
selectProvince.setAdapter(adapterProvince);
selectProvince.setOnItemSelectedListener(new MyOnItemSelectedListen()); //添加选择市
selectCity=new Spinner(parentContext);
selectCity.setPrompt("请选择城市");
//定义下拉列表适配器,用于填充内容
adapterCity=null;
//设置列表显示风格
selectCity.setAdapter(adapterCity); functionLayout.addView(selectCitytext);
functionLayout.addView(selectProvince);
functionLayout.addView(selectCity); okBtn=new ImageButton(parentContext);
okBtn.setBackgroundResource(R.drawable.okbtn);//给绑定按钮添加背景图片
LayoutParams lpokBtn=new LayoutParams(-2,-2);
lpokBtn.setMargins(20, 20, 0, 0);
lpokBtn.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
lpokBtn.addRule(RelativeLayout.BELOW,301);
//添加监听
okBtn.setOnClickListener(mylisten); selectBtn=new ImageButton(parentContext);
selectBtn.setBackgroundResource(R.drawable.selectbn);//给查询按钮添加背景图片
LayoutParams lpselectBtn=new LayoutParams(-2,-2);
lpselectBtn.setMargins(0, 20, 20, 0);
lpselectBtn.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
lpselectBtn.addRule(RelativeLayout.BELOW,301);
//添加监听
selectBtn.setOnClickListener(mylisten); footlayout.addView(functionLayout,lpfunctionLayout);
footlayout.addView(okBtn, lpokBtn);
footlayout.addView(selectBtn, lpselectBtn);
} /**
* 监听各种按钮的点击事件
* @author Administrator
*/
class MyButtonListen implements OnClickListener{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v==switchBtn){
//关闭外显示功能
System.out.println("点击外显示开关");
//换色
switchBtn.setBackgroundResource(R.drawable.end);
}else if(v==okBtn){
//确定,对城市进行保存。写入小型数据库
System.out.println("点击保存开关"); }else if(v==selectBtn){
//确定,对城市进行查询,不保存
System.out.println("点击查询开关");
}else{ Log.e("tag", "问题,输入的值不对");
}
}
}
/**
* 监听第一个选项的选择,当地一个选项框被选择数据的时候出发该类中的事件
* @author Administrator
*/
class MyOnItemSelectedListen implements OnItemSelectedListener{ @Override
public void onItemSelected(AdapterView<?> item, View view,
int position, long arg3) { String provincename = item.getAdapter().getItem(position).toString();
System.out.println(provincename);
String pinyin = provincemap.get(provincename);
Map<String, String> cityIdMap = service.getCityMap(pinyin);
String[] cityArray = service.getCityArray(cityIdMap); adapterCity=new ArrayAdapter<CharSequence>(parentContext,android.R.layout.simple_spinner_item,cityArray);
//设置列表显示风格
adapterCity.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
selectCity.setAdapter(adapterCity);
} @Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub }
}
}

完成了这些代码,已经基本可以实现骨架的二级联动了。

我这里第一级的连动使用的是读取本地xml文件,

第二级的联动使用的读取API,所以专门建立了一个service类来处理这些事件:

public class GetWeatherService {

	private GetWeatherDao dao;

	public GetWeatherService() {
dao=new GetWeatherDao();
} /**
* 获取城市气象信息的流程
* @param province
* @param selectcity
* @return
*/
public CityWeather getWeatherDay(String province,String selectcity){
//首先,获取所有省份名和拼音的map
Map<String, String> provincemap = getProvinceMap();
//根据所有的省份名 获取 所需要的省份名拼音
String provincexml = provincemap.get(province);
if(provincexml==null){
//有问题,省份不存在
return null;
}
//其次,输入值省份名拼音,获取指定市的城市气象id
Map<String, String> citymap = getCityMap(provincexml); if(citymap==null){
//有问题,城市不存在
return null;
} String cityid = citymap.get(selectcity); //根据id获取城市的json数据,并且解析该数据返回城市未来七天气象信息
CityWeather cityWeather = getCityWeather(cityid);
if(cityWeather==null){
//有问题,解析不正确
return null;
}
return cityWeather;
} /**
* 获取所有省份名和拼音的map
*/
public Map<String, String> getProvinceMap() {
Map<String, String> proMap = new HashMap<String, String>();
String res = "";
try {
InputStream in = Helper.getHelper().getContext().getResources()
.getAssets().open("province.xml");
InputStream is = IOHelper.fromInputStreamToInputStreamInCharset(in,"utf-8");
// int length = in.available();
// byte[] buffer = new byte[length];
// in.read(buffer);
// res = EncodingUtils.getString(buffer, "UTF-8");
// InputStream is = IOHelper.fromStringToIputStream(res);
SAXReader sr = new SAXReader();// 获取读取xml的对象。
Document document = sr.read(is);
Element root = document.getRootElement();
List<?> elementlist = root.elements("city");
for (Object obj : elementlist) {
Element row = (Element) obj;
String quName = row.attribute("quName").getText();
String pyName = row.attribute("pyName").getText();
proMap.put(quName, pyName);
}
} catch (Exception e) {
Log.e("tag", "读取配置失败");
e.printStackTrace();
}
return proMap;
} /**
* 获取指定省份的城市列表
*/
public Map<String, String> getCityMap(String provincename) {
InputStream cityXml = dao.getCityXml(provincename); Map<String,String> citymap=new HashMap<String,String>();
try {
SAXReader sr = new SAXReader();// 获取读取xml的对象。
Document document = sr.read(cityXml);
Element root = document.getRootElement();
List<?> elementlist = root.elements("city"); for (Object obj : elementlist) {
Element row = (Element) obj;
String cityname = row.attribute("cityname").getText();
String cityid =row.attribute("url").getText();
citymap.put(cityname,cityid);
}
} catch (Exception e) {
e.printStackTrace();
Log.e("tag", "问题"+e.toString());
return null;
}
return citymap;
} /**
* 根据指定的城市气象id获取该城市未来七天的天气信息
*/
public CityWeather getCityWeather(String cityid) {
CityWeather cityWeather=null;
String weatherJson = dao.getWeatherJson(cityid);
weatherJson="["+weatherJson+"]";
try {
// 对json数组进行循环,一般应该只返回一个。
JSONTokener jsonParser = new JSONTokener(weatherJson);
JSONObject object = (JSONObject) jsonParser.nextValue();
// 接下来的就是JSON对象的操作了
JSONObject weatherday = object.getJSONObject("weatherinfo"); cityWeather = new CityWeather();
String city = weatherday.get("city").toString();
String city_en = weatherday.get("city_en").toString();
String date_y = weatherday.get("date_y").toString();
String week = weatherday.get("week").toString(); String temp1 = weatherday.get("temp1").toString();// 今天温度
String temp2 = weatherday.get("temp2").toString();// 明天温度
String temp3 = weatherday.get("temp3").toString();// 后天温度 String weather1 = weatherday.get("weather1").toString();// 今天温度
String weather2 = weatherday.get("weather2").toString();// 明天温度
String weather3 = weatherday.get("weather3").toString();// 后天温度 cityWeather.setCityname(city);
cityWeather.setCity_en(city_en);
cityWeather.setCityid(cityid);
cityWeather.setDate_y(date_y);
cityWeather.setWeek(week);
cityWeather.setTempToday(temp1);
cityWeather.setTempTommorrow(temp2);
cityWeather.setTempAt(temp3);
cityWeather.setWeatherToday(weather1);
cityWeather.setWeatherTommorrow(weather2);
cityWeather.setWeatherAt(weather3);
} catch (Exception e) {
// TODO: handle exception
return null;
}
return cityWeather;
} /**
* 返回省名称的数组
* @param map
* @return
*/
public String[] getProvinceArray(Map<String, String> map){
String[] provinceArray=new String[map.size()];
int i=0;
for(String key:map.keySet()){
provinceArray[i++]=key;
}
return provinceArray;
} public String[] getCityArray(Map<String, String> map){
String[] cityArray=new String[map.size()];
int k=0;
for(String key:map.keySet()){
cityArray[k++]=key;
}
return cityArray;
}
}

程序中经常涉及到io流的处理,所以为了方法处理,专门写了一个工具类,为了便于大家调试,也附上代码:

public class IOHelper {
/**
* 输入InputStream流,返回字符串文字。
* @param is
* @return
*/
public static String fromIputStreamToString(InputStream is){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i = -1;
try {
while ((i = is.read()) != -1) {
baos.write(i);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return baos.toString();
} /**
* 输入InputStream流和文件地址,返回成功与否。
* @param is
* @return
*/
public static boolean fromIputStreamToFile(InputStream is,String outfilepath){
byte[] b=new byte[1024];
FileOutputStream fos=null;
try {
fos=new FileOutputStream(new File(outfilepath));
while((is.read(b, 0, 1024))!=-1){
fos.write(b);
}
fos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}finally{
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return true;
}
/**
* 输入文件地址,返回inputStream流。
* @param is
* @return
*/
public static InputStream fromFileToIputStream(String infilepath){
FileInputStream fis=null;
try {
fis=new FileInputStream(new File(infilepath));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return fis;
} public static InputStream fromStringToIputStream(String s) {
if (s != null && !s.equals("")) {
try { ByteArrayInputStream stringInputStream = new ByteArrayInputStream(
s.getBytes());
return stringInputStream;
} catch (Exception e) { e.printStackTrace();
}
}
return null;
}
//把输入流转换为存有UTF-8格式数据的输入流
public static InputStream fromInputStreamToInputStreamInCharset(
InputStream in, String charset) throws Exception {
StringBuilder builder=new StringBuilder();
byte[] buffer = new byte[2048];
int len = -1;
while ((len = in.read(buffer)) != -1) {
builder.append(EncodingUtils.getString(buffer, 0, len, "UTF-8"));
}
return IOHelper.fromStringToIputStream(builder.toString());
} }

本类中的主要实现效果是二级联动,所以API调用的DAO层代码我就不上传了。。

疑问:大家可能也注意到了,我的IOHelper类中,专门写了一个方法fromInputStreamToInputStreamInCharset来处理输入流,那是因为我发现我直接通过SAXReader.read(io)读取的时候,出现了乱码的现象,我知道是编码格式的问题,本来想直接让SAXReader以UTF-8格式来读取,可以搜了一圈,没找到合适的方法就专门写了一个这样的方法。希望更好处理方式的朋友麻烦告之下。

另外我现在实现的方式两个选项栏里面的数据都是无序的,大家有兴趣的可以写一个比较器来实现排序的效果。

最后附上最终实现的效果图:

android中利用实现二级联动的效果