文章目录

    • 一、在Java代码中初始化界面
    • 二、子线程中开启网络请求
    • 三、网络请求返回Json数据解析
    • 四、界面呈现
      • 1. 获取天气数据并展示
      • 2. 根据天气情况显示对应图片
      • 3. 显示逐小时天气情况
      • 4. 点击事件监听器
    • 五、数据库创建和功能实现
    • 六、城市管理界面功能实现
      • 1. listView适配器配置
      • 4. 控件监听事件
    • 七、添加城市界面功能实现
      • 1. 适配器配置、通过省会添加城市功能实现
      • 4. 通过搜索添加城市功能实现
      • 5. 通过广播接收城市信息更新消息

一、在Java代码中初始化界面

在MainActivity中初始化ViewPager界面

    private void initPager() {//创建Fragment对象添加到ViewPager数据源当中for (int i=0;i<cityList.size();i++){  //使用for循环将城市集合中的内容添加到fragment集合中WeatherFragment weatherFragment = new WeatherFragment(); Bundle bundle = new Bundle();    bundle.putString("city",cityList.get(i));  //使用bundle存储城市名,传到fragment中weatherFragment.setArguments(bundle);fragmentList.add(weatherFragment);}//将fragment集合传入fragment适配器中FragmentPagerAdapter fragmentPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager(),fragmentList);showVp.setAdapter(fragmentPagerAdapter);}

在WeatherFragment中获取从MainActivity中传入的城市

//通过activity传值获取到当前fragment加载的是哪个地方的天气情况
Bundle arguments = getArguments();
String city=arguments.getString("city");
getWeatherCity(city);

编写FragmentPagerAdapter用于fragment的显示

public class FragmentPagerAdapter extends FragmentStatePagerAdapter {List<Fragment> fragmentList;  //主界面传入的fragmenr的集合public FragmentPagerAdapter(FragmentManager fragmentManager,List<Fragment> fragments) {super(fragmentManager);this.fragmentList=fragments;}@NonNull@Overridepublic Fragment getItem(int position) {  //根据位置获取集合条目内容return fragmentList.get(position);}@Overridepublic int getCount() {   //获取集合条目个数return fragmentList.size();}
}

二、子线程中开启网络请求

编写NetUtil类,在类中编写两个静态方法doGet和getWeatherOfCity

doGet方法用于从网络中获取数据,getWeatherOfCity方法用于拼接url之后,调用doGet方法传入url获取天气信息

    public static final String URL_WEATHER="https://tianqiapi.com/api?unescape=1&version=v1&appid=22444194&appsecret=EG7XHDop";public static String doGet(String urlString){String result="";String line;StringBuilder stringBuilder=null;BufferedReader bufferedReader=null;//连接网络HttpURLConnection connection=null;InputStreamReader isr=null;try {URL url=new URL(urlString);connection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");  //链接方式connection.setConnectTimeout(5000);   //超时时间//从连接中读取数据(二进制)InputStream inputStream=connection.getInputStream();//对数据流进行加工isr=new InputStreamReader(inputStream);//创建缓冲区,将二进制流送入bufferedReader=new BufferedReader(isr);//从缓冲区一行一行读取字符串stringBuilder=new StringBuilder();while ((line=bufferedReader.readLine())!=null){stringBuilder.append(line); //进行拼接}result=stringBuilder.toString();} catch (Exception e) {e.printStackTrace();}finally {try {//关闭流connection.disconnect();bufferedReader.close();isr.close();} catch (IOException e) {e.printStackTrace();}}return result;}//拼接出来获取天气的urlpublic static String getWeatherOfCity(String city){String url=URL_WEATHER+"&city="+city;Log.i("Aye","URL:"+url);Log.i("Aye","URLResult:"+doGet(url));return  doGet(url);}

编写getWeatherCity方法,开启子线程,调用NetUtil类中的静态方法getWeatherOfCity来获取天气数据,并通过handler将数据传递给主线程

private void getWeatherCity(String selectCity) {//开启子线程,请求网络new Thread(new Runnable() {@Overridepublic void run() {//请求网络String weatherOfCity=NetUtil.getWeatherOfCity(selectCity);//使用handler将数据传递给主线程Message message=Message.obtain();message.what=0;message.obj=weatherOfCity;handler.sendMessage(message);}}).start();
}

三、网络请求返回Json数据解析

使用在线工具解析Json数据并生成JavaBean导入包中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zEJn14mR-1632214033044)(C:UsersparanoiaAppDataRoamingTyporatypora-user-imagesimage-20210915094807564.png)]

接收子线程传递的数据并使用gson解析

    private Handler handler=new Handler(Looper.myLooper()){@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);if (msg.what==0){//主线程收到的天气数据String weather= (String) msg.obj;Log.i("Aye","主线程收到的天气数据:"+weather);//使用gson解析Gson gson=new Gson();JsonRootBean jsonRootBean=gson.fromJson(weather, JsonRootBean.class);updateWeather(jsonRootBean); //更新天气数据并显示}}};

四、界面呈现

1. 获取天气数据并展示

编写updateWeather方法,用于获取数据并显示在界面上

    //数据显示private void updateWeather(JsonRootBean jsonRootBean) {if (jsonRootBean!=null){List<Data> dayWeather = jsonRootBean.getData(); //获取每一天的数据Data todayWeather = dayWeather.get(0);//获取今天的数据//不为空则显示今天天气数据if (todayWeather!=null){tempTv.setText(todayWeather.getTem1());mainWeatherTv.setText(todayWeather.getWea());;todayTv.setText("今天:"+todayWeather.getWea());todayAirTv.setText("空气:"+todayWeather.getAir_level());todayTempTv.setText(todayWeather.getTem()+"~"+todayWeather.getTem2());todayIconIv.setImageResource(getImg(todayWeather.getWea_img()));windTv.setText(todayWeather.getWin_speed());humidityTv.setText(todayWeather.getHumidity());pressureTv.setText(todayWeather.getPressure()+"hPa");windTv1.setText(todayWeather.getWin().get(0));sunriseTv.setText("日出:"+todayWeather.getSunrise());sunsetTv.setText("日落:"+todayWeather.getSunset());}//获取明天的数据Data tomorrowWeather = dayWeather.get(1);//不为空则显示明天数据if (tomorrowWeather!=null){tomorrowTv.setText("明天:"+tomorrowWeather.getWea());tomorrowAirTv.setText("空气:"+tomorrowWeather.getAir_level());tomorrowTempTv.setText(tomorrowWeather.getTem()+"~"+tomorrowWeather.getTem2());tomorrowIconIv.setImageResource(getImg(tomorrowWeather.getWea_img()));}//获取后天的数据Data afterWeather = dayWeather.get(2);//不为空则显示后天天气数据if (afterWeather!=null){afterTv.setText("后天:"+afterWeather.getWea());afterAirTv.setText("空气:"+afterWeather.getAir_level());afterTempTv.setText(afterWeather.getTem()+"~"+afterWeather.getTem2());afterIconIv.setImageResource(getImg(afterWeather.getWea_img()));}//获取指数信息List<Index> index = todayWeather.getIndex();//不为空则显示if (index!=null) {//紫外线指数UVTitle = index.get(0).getTitle();UVLevel = index.get(0).getLevel();UVDesc = index.get(0).getDesc();//穿衣指数clotheTitle = index.get(3).getTitle();clotheLevel = index.get(3).getLevel();clotheDesc = index.get(3).getDesc();//运动指数sportTitle=index.get(1).getTitle();sportLevel=index.get(1).getLevel();sportDesc=index.get(1).getDesc();//洗车指数carTitle=index.get(4).getTitle();carLevel=index.get(4).getLevel();carDesc=index.get(4).getDesc();//血糖指数sickTitle=index.get(2).getTitle();sickLevel=index.get(2).getLevel();sickDesc=index.get(2).getDesc();//空气污染指数airTitle=index.get(5).getTitle();airLevel=index.get(5).getLevel();airDesc=index.get(5).getDesc();}//获取逐小时天气情况,传递给Adapter用于显示List<Hours> timeBean = todayWeather.getHours();weatherAdapter=new WeatherAdapter(getActivity(),timeBean);LinearLayoutManager manager=new LinearLayoutManager(getActivity(), RecyclerView.HORIZONTAL,false);hoursRv.setAdapter(weatherAdapter);hoursRv.setLayoutManager(manager);}}

2. 根据天气情况显示对应图片

编写getImg方法用于根据天气情况显示图片

    private int getImg(String wea_img) {int result = 0;switch (wea_img) {case "qing": //晴天result=R.mipmap.sun;break;case "yin":  //阴天result=R.mipmap.yin;break;case "yu":  //雨天result=R.mipmap.yu;break;case "yun":    //多云result=R.mipmap.yun;break;case "bingbao":   //冰雹result=R.mipmap.bingbao;break;case "wu":   //雾result=R.mipmap.wu;break;case "shachen":  //沙尘暴result=R.mipmap.shachen;break;case "lei":    //雷result=R.mipmap.lei;break;case "xue":  //雪result=R.mipmap.xue;break;default:  //如果都不是则显示晴result=R.mipmap.sun;break;}return result;}

3. 显示逐小时天气情况

编写WeatherAdapter,用于显示逐小时天气情况

public class WeatherAdapter extends RecyclerView.Adapter<WeatherAdapter.WeatherViewHolder> {private Context context;  //上下文private List<Hours>  timeBean;   //天气信息public WeatherAdapter(Context context, List<Hours> timeBean) {this.context = context;this.timeBean = timeBean;}@NonNull@Override  //创建ViewHolderpublic WeatherViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view= LayoutInflater.from(context).inflate(R.layout.recylerview_item,parent,false);WeatherViewHolder weatherViewHolder=new WeatherViewHolder(view);return weatherViewHolder;}@Override   //绑定ViewHolder,显示数据public void onBindViewHolder(@NonNull WeatherViewHolder holder, int position) {Hours hoursBean = timeBean.get(position); //根据位置获取该小时天气信息并显示holder.timeTv.setText(hoursBean.getHours());holder.timeWeatherTv.setText(hoursBean.getWea());holder.timeTempTv.setText(hoursBean.getTem());holder.timeWindTv.setText(hoursBean.getWin()+" "+hoursBean.getWin_speed());}@Override  //获取条目个数public int getItemCount() {return timeBean.size();}//ViewHolderclass WeatherViewHolder extends RecyclerView.ViewHolder {TextView timeTv;TextView timeTempTv;TextView timeWeatherTv;TextView timeWindTv;public WeatherViewHolder(@NonNull View itemView) {   //对象与控件绑定super(itemView);timeTv = itemView.findViewById(R.id.timeTv);timeTempTv = itemView.findViewById(R.id.timeTempTv);timeWeatherTv = itemView.findViewById(R.id.timeWeatherTv);timeWindTv = itemView.findViewById(R.id.timeWindTv);}}
}

4. 点击事件监听器

设置点击事件,用于显示各种指数的详细信息和跳转浏览器查看更多天气

    @Overridepublic void onClick(View v) {switch (v.getId()){case R.id.webTv:   //点击"查看更多天气",跳转到浏览器界面Uri uri=Uri.parse("https://tianqi.qq.com/index.htm");Intent intent=new Intent();intent.setAction("android.intent.action.VIEW");intent.setData(uri);startActivity(intent);break;case R.id.clotheIv:   //点击穿衣指数弹出对话框showAlertDialog(clotheTitle,clotheLevel,clotheDesc);break;case R.id.UVIv:  //点击紫外线指数showAlertDialog(UVTitle,UVLevel,UVDesc);break;case R.id.sportIv:  //点击运动指数showAlertDialog(sportTitle,sportLevel,sportDesc);break;case R.id.carIv:  //点击洗车指数showAlertDialog(carTitle,carLevel,carDesc);break;case R.id.airPollutionIv:  //点击空气污染指数showAlertDialog(airTitle,airLevel,airDesc);break;case R.id.sickIv:  //点击血糖指数showAlertDialog(sickTitle,sickLevel,sickDesc);break;}}

编写showAlertDialog方法,用于显示对话框

    private void showAlertDialog(String title, String level, String desc) {AlertDialog.Builder builder= new AlertDialog.Builder(MainActivity.this);builder.setTitle(title).setMessage("n"+level+"nn"+desc).create().show();}

五、数据库创建和功能实现

编写CityDBHelper,继承SQLiteOpenHelper并重写方法

  private static SQLiteDatabase db;ContentValues values=new ContentValues();private long flag=0;public CityDBHelper(@Nullable Context context) {super(context, "city.db", null, 1);db=this.getWritableDatabase();}@Overridepublic void onCreate(SQLiteDatabase db) {//创建表String sql = "create table city(id integer primary key autoincrement,city varchar(20) unique not null)";db.execSQL(sql);}
//添加数据public boolean addCity(String city){values.put("city",city);flag=db.insert("city",null,values);return flag>0?true:false;}
//根据城市名删除数据public boolean deleteCity(String city){flag=db.delete("city","city=?",new String[]{city});return flag>0?true:false;}
//查询全部数据public List<String> queryCity(){List<String> cityList=new ArrayList<>();Cursor cursor=db.query("city",null,null,null,null,null,null);if (cursor!=null){while (cursor.moveToNext()){String city=cursor.getString(1);cityList.add(city);}}return cityList;}
//根据城市名查询数据public boolean findCity(String city) {Cursor cursor = db.query("city", null, "city=?", new String[]{city}, null, null, null);if (cursor != null) {while (cursor.moveToNext()) {String findCity = cursor.getString(1);Log.i("Aye",findCity+"findCity");if (findCity != null) {return false;} else {return true;}}}return true;}@Override  //更新数据库public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}

六、城市管理界面功能实现

1. listView适配器配置

编写CityAdapter继承自BaseAdapter,为listView适配器

    private Context context;private List<String> cityList;public CityAdapter(Context context, List<String> cityList) {this.context = context;this.cityList = cityList;}@Overridepublic int getCount() {  //集合条目个数return cityList.size();}@Overridepublic Object getItem(int position) {   //根据位置获取条目信息return cityList.get(position);}@Overridepublic long getItemId(int position) {  //获取位置return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder=null;if (convertView==null){  //如果convertView为空则初始化并绑定viewHolder=new ViewHolder();convertView=View.inflate(context, R.layout.city_listview_item,null);viewHolder.cityNameTv=convertView.findViewById(R.id.cityNameTv);convertView.setTag(viewHolder);}else {viewHolder=(ViewHolder)convertView.getTag();}//获取数据并显示viewHolder.cityNameTv.setText(cityList.get(position));return convertView;}class ViewHolder{TextView cityNameTv;}

4. 控件监听事件

为界面控件添加监听事件,用于跳转到添加城市界面和删除城市

        //添加城市,跳转到添加城市界面findTv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startActivity(new Intent(CityActivity.this,AddActivity.class));}});//长按删除城市showLv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {String deleteCity=cityList.get(position);   //根据长按点击位置获取内容//弹出对话框AlertDialog.Builder builder= new AlertDialog.Builder(CityActivity.this);builder.setTitle("提示").setMessage("是否删除该城市天气信息?").setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {}}).setPositiveButton("删除", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {//调用数据库方法删除条目flag=cityDBHelper.deleteCity(deleteCity); if (flag){Toast.makeText(CityActivity.this,"删除成功",Toast.LENGTH_SHORT).show();initView();//发送广播,通知更新天气数据Intent intent=new Intent("UPDATE1");sendBroadcast(intent);}else {Toast.makeText(CityActivity.this,"删除失败",Toast.LENGTH_SHORT).show();}}}).create().show();return true;}});

七、添加城市界面功能实现

1. 适配器配置、通过省会添加城市功能实现

    private Context context;private List<String> cityList;private CityDBHelper cityDBHelper;public AddAdapter(Context context, List<String> cityList,CityDBHelper cityDBHelper) {this.context = context;this.cityList = cityList;this.cityDBHelper=cityDBHelper;}@NonNull@Override  //创建ViewHolderpublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view=View.inflate(context,R.layout.add_recylerview_item,null);ViewHolder viewHolder = new ViewHolder(view);return viewHolder;}@Override  //绑定ViewHolder,显示数据,添加数据public void onBindViewHolder(@NonNull ViewHolder holder, int position) {holder.cityTv.setText(cityList.get(position));holder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String clickCity=cityList.get(position);  //获取点击条目信息//判断点击条目是否为空,然后判断该城市是否已经添加到数据库中,最后判断是否添加成功if (clickCity!=null){if (cityDBHelper.findCity(clickCity)) {if (cityDBHelper.addCity(clickCity)) {Toast.makeText(context,"添加成功",Toast.LENGTH_SHORT).show();//发送广播,通知更新天气数据Intent intent=new Intent("UPDATE");context.sendBroadcast(intent);}else {Toast.makeText(context,"添加失败",Toast.LENGTH_SHORT).show();}}else {Toast.makeText(context,"已存在该城市",Toast.LENGTH_SHORT).show();}}}});}@Overridepublic int getItemCount() {  //获取集合数目return cityList.size();}
//ViewHolderclass ViewHolder extends RecyclerView.ViewHolder {TextView cityTv;public ViewHolder(@NonNull View itemView) {super(itemView);cityTv=itemView.findViewById(R.id.itemCityTv);}}

适配器绑定,数据传递

private String[] cityStrings=new String[]{"北京","天津","哈尔滨","沈阳","石家庄", "兰州", "西安", "郑州", "太原", "长沙", "南京", "贵阳",   "杭州", "广州", "台北","上海" , "重庆", "长春", "呼和浩特", "乌鲁木齐", "西宁", "银川", "济南", "合肥", "武汉", "成都", "拉萨", "昆明", "南昌", "福州", "海口", "澳门"};
cityList=new ArrayList<>();
for (int i=0;i<cityStrings.length;i++){  //使用for集合将数组数据传入集合中cityList.add(cityStrings[i]);
}addAdapter=new AddAdapter(this,cityList,cityDBHelper);
cityRv.setLayoutManager(new GridLayoutManager(this,3));  //适配器布局GridLayout,一行三个控件
cityRv.setAdapter(addAdapter);

4. 通过搜索添加城市功能实现

搜索键监听事件
findTv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String findCity=findEt.getText().toString();    //获取输入框中的内容//判断输入内容是否为空,再判断该城市是否已经存在数据库中,最后判断是否添加成功if (findCity!=null){if(cityDBHelper.findCity(findCity)){if (cityDBHelper.addCity(findCity)) {Toast.makeText(AddActivity.this,"添加成功",Toast.LENGTH_SHORT).show();//使用广播,通知天气信息更新Intent intent=new Intent("UPDATE");sendBroadcast(intent);}else {Toast.makeText(AddActivity.this,"添加失败",Toast.LENGTH_SHORT).show();}}else {Toast.makeText(AddActivity.this,"已存在该城市",Toast.LENGTH_SHORT).show();}}else {Toast.makeText(AddActivity.this,"输入城市为空",Toast.LENGTH_SHORT).show();}}
});

5. 通过广播接收城市信息更新消息

在CityActivity文件中创建广播接收者

    @Overrideprotected void onCreate(Bundle savedInstanceState) {      
myReceiver=new MyReceiver();//实例化过滤器并设置过滤的广播IntentFilter intentFilter=new IntentFilter();intentFilter.addAction("UPDATE");registerReceiver(myReceiver,intentFilter);  //注册广播}private class MyReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {initView();  //收到广播信息后,更新界面}}

在MainActivity中也创建广播接收者

    @Overrideprotected void onCreate(Bundle savedInstanceState) {myReceiver=new MyReceiver();//实例化过滤器并设置过滤的广播IntentFilter intentFilter=new IntentFilter();intentFilter.addAction("UPDATE");intentFilter.addAction("UPDATE1");registerReceiver(myReceiver,intentFilter); //注册广播}private class MyReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {initPager();/收到广播信息后,更新界面}}