一个简单的天气APP

  • 效果演示视频
    • 简述
    • 天气JSON数据
      • 实况天气
      • 逐24小时天气预报
      • 未来七天天气预报
    • 天气详情页
      • 效果图
      • 获取JSON数据
      • URL请求
        • 实况天气URL
        • 逐24小时天气预报URL
        • 未来七天天气预报URL
      • 解析JSON数据
        • 解析实况天气数据
        • 解析逐小时天气预报
        • 解析未来七天天气预报
      • 初始化天气详情页
        • 获取当前位置
          • 获取经纬度
          • 经纬度转为地理信息
        • 页面初始化
        • ViewPager子页面编辑
          • 页面添加
          • 页面删除
        • 指示器
    • 天气编辑页
      • 效果图
      • 简版天气数据
        • 获取数据并初始化
        • 子项删除
      • 搜索
        • 效果图
        • 更改SearchView背景和字体样式
        • 搜索初始化
        • 搜索字符监听
  • 下载地址

效果演示视频

EasyWeather演示效果视频

简述

此天气数据源采用心知天气API(试用版),免费版获取数据有限,只能获取普通的温度、湿度等,例如压力、云量、可见度等均获取不到,试用版相当于正式版,可以获取大部分数据,试用日期是14天。

首页不同城市天气页面之间的滑动采用的是ViewPager,编辑界面的搜索栏采用的是SearchView+ListView,其中城市数据源是统计到一个xml文件中;通过点击搜索匹配项,插入至SQLite数据库中,然后刷新当前天气子项,然后通过EventBus通知首页更新views页面。处于编辑状态时,删除子项,同样使用EventBus通知首页更新;更新主要是页面数量更新和下方指示器更新。

天气JSON数据

实况天气

{"results":[{"location":{"id":"WKZTU85FVNSV","name":"娄底","country":"CN","path":"娄底,娄底,湖南,中国","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"now":{"text":"晴","code":"1","temperature":"25","feels_like":"26","pressure":"984","humidity":"56","visibility":"19.0","wind_direction":"北","wind_direction_degree":"342","wind_speed":"6.0","wind_scale":"2","clouds":"13","dew_point":""},"last_update":"2022-09-02T22:08:30+08:00"}]
}

逐24小时天气预报

{"results":[{"location":{"id":"WWYMRT0VRMUG","name":"大连","country":"CN","path":"大连,大连,辽宁,中国","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"hourly":[{"time":"2022-09-02T22:00:00+08:00","text":"多云","code":"4","temperature":"21","humidity":"81","wind_direction":"东","wind_speed":"7.52"},{"time":"2022-09-02T23:00:00+08:00","text":"阴","code":"9","temperature":"21","humidity":"82","wind_direction":"东北","wind_speed":"7.02"},{"time":"2022-09-03T00:00:00+08:00","text":"阴","code":"9","temperature":"21","humidity":"83","wind_direction":"东北","wind_speed":"7.81"},{"time":"2022-09-03T01:00:00+08:00","text":"阴","code":"9","temperature":"21","humidity":"84","wind_direction":"东北","wind_speed":"8.64"},{"time":"2022-09-03T02:00:00+08:00","text":"阴","code":"9","temperature":"21","humidity":"85","wind_direction":"东北","wind_speed":"9.54"},{"time":"2022-09-03T03:00:00+08:00","text":"阴","code":"9","temperature":"21","humidity":"85","wind_direction":"东北","wind_speed":"10.15"},{"time":"2022-09-03T04:00:00+08:00","text":"阴","code":"9","temperature":"21","humidity":"86","wind_direction":"东北","wind_speed":"10.73"},{"time":"2022-09-03T05:00:00+08:00","text":"阴","code":"9","temperature":"21","humidity":"86","wind_direction":"东北","wind_speed":"11.34"},{"time":"2022-09-03T06:00:00+08:00","text":"阴","code":"9","temperature":"21","humidity":"86","wind_direction":"东北","wind_speed":"13.68"},{"time":"2022-09-03T07:00:00+08:00","text":"阴","code":"9","temperature":"22","humidity":"85","wind_direction":"东北","wind_speed":"16.16"},{"time":"2022-09-03T08:00:00+08:00","text":"多云","code":"4","temperature":"23","humidity":"83","wind_direction":"东北","wind_speed":"18.72"},{"time":"2022-09-03T09:00:00+08:00","text":"多云","code":"4","temperature":"23","humidity":"81","wind_direction":"东","wind_speed":"18.18"},{"time":"2022-09-03T10:00:00+08:00","text":"多云","code":"4","temperature":"24","humidity":"79","wind_direction":"东","wind_speed":"18.97"},{"time":"2022-09-03T11:00:00+08:00","text":"晴","code":"0","temperature":"24","humidity":"77","wind_direction":"东","wind_speed":"20.92"},{"time":"2022-09-03T12:00:00+08:00","text":"多云","code":"4","temperature":"24","humidity":"76","wind_direction":"东","wind_speed":"19.84"},{"time":"2022-09-03T13:00:00+08:00","text":"阴","code":"9","temperature":"24","humidity":"76","wind_direction":"东","wind_speed":"19.12"},{"time":"2022-09-03T14:00:00+08:00","text":"阴","code":"9","temperature":"24","humidity":"75","wind_direction":"东","wind_speed":"18.83"},{"time":"2022-09-03T15:00:00+08:00","text":"阴","code":"9","temperature":"24","humidity":"76","wind_direction":"东","wind_speed":"19.44"},{"time":"2022-09-03T16:00:00+08:00","text":"阴","code":"9","temperature":"23","humidity":"77","wind_direction":"东","wind_speed":"20.09"},{"time":"2022-09-03T17:00:00+08:00","text":"阴","code":"9","temperature":"23","humidity":"77","wind_direction":"东","wind_speed":"20.77"},{"time":"2022-09-03T18:00:00+08:00","text":"阴","code":"9","temperature":"22","humidity":"78","wind_direction":"东","wind_speed":"19.66"},{"time":"2022-09-03T19:00:00+08:00","text":"阴","code":"9","temperature":"22","humidity":"78","wind_direction":"东","wind_speed":"18.58"},{"time":"2022-09-03T20:00:00+08:00","text":"阴","code":"9","temperature":"22","humidity":"78","wind_direction":"东","wind_speed":"17.53"},{"time":"2022-09-03T21:00:00+08:00","text":"阴","code":"9","temperature":"22","humidity":"78","wind_direction":"东","wind_speed":"15.7"}]}]
}

未来七天天气预报

{"results":[{"location":{"id":"WWYMRT0VRMUG","name":"大连","country":"CN","path":"大连,大连,辽宁,中国","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"daily":[{"date":"2022-09-02","text_day":"晴","code_day":"0","text_night":"阴","code_night":"9","high":"23","low":"17","rainfall":"0.00","precip":"0.00","wind_direction":"东北","wind_direction_degree":"65","wind_speed":"6.41","wind_scale":"2","humidity":"60"},{"date":"2022-09-03","text_day":"阴","code_day":"9","text_night":"阴","code_night":"9","high":"24","low":"21","rainfall":"0.00","precip":"0.00","wind_direction":"东","wind_direction_degree":"86","wind_speed":"19.44","wind_scale":"3","humidity":"83"},{"date":"2022-09-04","text_day":"小雨","code_day":"13","text_night":"阴","code_night":"9","high":"22","low":"19","rainfall":"0.69","precip":"0.91","wind_direction":"东北","wind_direction_degree":"51","wind_speed":"15.19","wind_scale":"3","humidity":"85"},{"date":"2022-09-05","text_day":"小雨","code_day":"13","text_night":"晴","code_night":"1","high":"24","low":"18","rainfall":"0.14","precip":"0.56","wind_direction":"西北","wind_direction_degree":"321","wind_speed":"18.54","wind_scale":"3","humidity":"86"},{"date":"2022-09-06","text_day":"晴","code_day":"0","text_night":"晴","code_night":"1","high":"23","low":"20","rainfall":"0.00","precip":"0.00","wind_direction":"西","wind_direction_degree":"288","wind_speed":"19.66","wind_scale":"3","humidity":"62"},{"date":"2022-09-07","text_day":"多云","code_day":"4","text_night":"阴","code_night":"9","high":"26","low":"20","rainfall":"0.00","precip":"0.00","wind_direction":"西南","wind_direction_degree":"229","wind_speed":"14.04","wind_scale":"3","humidity":"73"},{"date":"2022-09-08","text_day":"阴","code_day":"9","text_night":"阴","code_night":"9","high":"25","low":"22","rainfall":"0.00","precip":"0.00","wind_direction":"东南","wind_direction_degree":"149","wind_speed":"6.77","wind_scale":"2","humidity":"71"}],"last_update":"2022-09-02T20:00:00+08:00"}]
}

天气详情页

包括实况天气、逐24小时天气预、未来七天天气预报三部分数据

效果图

获取JSON数据

首先通过OKHttp使用get方式获取数据,不同的天气数据,传入不同的url,所以把请求作为公共方式,然后通过写一个回调接口,将数据源回调至外部。

public void Post(String url, HttpCallback callback){OkHttpClient client = new OkHttpClient();final Request request = new Request.Builder().url(url).get().build();client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {callback.onFailed(ErrorCodeParam.NetworkError);}@Overridepublic void onResponse(Call call, Response response) throws IOException {int code = response.code();if (code == 200){String json = response.body().string();callback.onResponse(json.toString());}else {callback.onFailed(ErrorCodeParam.PostError);}}});}

URL请求

实况天气URL

具体方法参考官网
官网所展示的请求参数并不是需要所有都添加,例如语言都有默认想,一般不需要提交,在官网也有标注,此处传入key(心知天气API密钥)和城市(你所想获取天气预报的城市,可以是汉字)

/*** 获取当前实况天气*/public String getNowUrl(String location) {HttpUrl.Builder builder = HttpUrl.parse(WeatherParam.NowWeatherURL).newBuilder();builder.addQueryParameter("key",WeatherParam.weatherToken);builder.addQueryParameter("location",location);return builder.build().toString();}

逐24小时天气预报URL

具体参考官网
前两项参数与实况天气一致,第三个参数是你需要获取多少小时的天气预报数据,以当前时间为第一个小时;例如:你传入5个小时,你当时系统时间是9点,那么返回的第一个数据就是9点,第二个为10…第5个为13点;以此类推

 /***  获取24小时天气情况*/public String getHourly24Url(String location,int hours) {HttpUrl.Builder builder = HttpUrl.parse(WeatherParam.Hourly24WeatherURL).newBuilder();builder.addQueryParameter("key",WeatherParam.weatherToken);builder.addQueryParameter("location",location);builder.addQueryParameter("hours",hours+"");return builder.build().toString();}

未来七天天气预报URL

具体参考官网
第三个参数与逐小时天气预报的小时一致,以当前日期为第一天;请求天数好像最大为15天

   /*** 获取未来七天天气预报情况*/public String getFuture7Url(String location,int days) {HttpUrl.Builder builder = HttpUrl.parse(WeatherParam.Future7WeatherURL).newBuilder();builder.addQueryParameter("key",WeatherParam.weatherToken);builder.addQueryParameter("location",location);builder.addQueryParameter("days",days+"");return builder.build().toString();}

解析JSON数据

解析实况天气数据

JSON数据格式在上文有提及,返回的数据较多,我们只需要关于天气的即可,地点可以不解析;此处将数据源解析成具体实体类,依旧通过回调接口,暴露给外部。

 /*** 获取当前天气情况*/fun getNowWeather(location: String,callback: WeatherCallback_now) {val url = HttpUtils.getInstance().getNowUrl(location)Log.d(TAG, "url = $url")HttpUtils.getInstance().Post(url, object : HttpCallback {override fun onFailed(ErrorCode: Int) {callback.onFailed(ErrorCode)}override fun onResponse(JSONData: String) {if (TextUtils.isEmpty(JSONData)) {callback.onFailed(0)return}Log.d(TAG, "now = $JSONData")try {val jsonObject = JSONObject(JSONData)val jsonArray = jsonObject.getJSONArray("results")val now = jsonArray.getJSONObject(0).getJSONObject("now")val time : String = jsonArray.getJSONObject(0).getString("last_update");val bean : WRealTimeBean? = HttpUtils.getInstance().fromJson(now.toString(),WRealTimeBean::class.java)if (bean != null) {bean.last_update = timebean.location = location;}callback.onSuccess(bean)} catch (e: JSONException) {e.printStackTrace()}}})}

解析逐小时天气预报

步骤与实况天气雷同,区别在于逐小时天气预报返回的数组,所以回调接口,返回也是list数据

/*** 获取24小时天气情况*/fun getHourly24Weather(location: String,callback: WeatherCallback_24H) {val url = HttpUtils.getInstance().getHourly24Url(location, 24)Log.d(TAG, "url = $url")HttpUtils.getInstance().Post(url, object : HttpCallback {override fun onFailed(ErrorCode: Int) {callback.onFailed(ErrorCode)}override fun onResponse(JSONData: String) {if (TextUtils.isEmpty(JSONData)) {callback.onFailed(0)return}Log.d(TAG, "hourlv24 = $JSONData")try {val jsonObject = JSONObject(JSONData)val jsonArray = jsonObject.getJSONArray("results")val hourly24 = jsonArray.getJSONObject(0).getJSONArray("hourly")val bean: MutableList<WHourly24Bean>? = HttpUtils.getInstance().fromListJson(hourly24.toString(),WHourly24Bean::class.java)callback.onSuccess(bean)} catch (e: JSONException) {e.printStackTrace()}}})}

解析未来七天天气预报

同样返回的是数组数据,完成数据解析并回调;值得注意的是,如果有存在重复的城市名称,返回的数据也是多份,例如请求的城市名称并不精准,本来想请求张家界的天气数据,但是只输入张家二字,系统后台会返回张家界和张家口的数据,所以我们默认取第一个数据源

   /*** 获取未来七天天气情况(包括今天)*/fun getFuture7Weather(location: String,callback: WeatherCallback_7D) {val url = HttpUtils.getInstance().getFuture7Url(location, 7)Log.d(TAG, "url = $url")HttpUtils.getInstance().Post(url, object : HttpCallback {override fun onFailed(ErrorCode: Int) {callback.onFailed(ErrorCode)}override fun onResponse(JSONData: String) {if (TextUtils.isEmpty(JSONData)) {callback.onFailed(0)return}Log.d(TAG, "future7 = $JSONData")try {val jsonObject = JSONObject(JSONData)val jsonArray = jsonObject.getJSONArray("results")val future7 = jsonArray.getJSONObject(0).getJSONArray("daily")val bean: MutableList<WFuture7Bean>? = HttpUtils.getInstance().fromListJson(future7.toString(),WFuture7Bean::class.java)callback.onSuccess(bean)} catch (e: JSONException) {e.printStackTrace()}}})}

初始化天气详情页

获取当前位置

为了减轻app负重,并未采用高德、百度等API进行位置定位,采用原始的定位方式,使用网络方式进行定位

获取经纬度

获取位置信息需要动态申请位置权限

public String getLocationInfo() {if (ActivityCompat.checkSelfPermission(BaseApplication.context,Group_Location[0]) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(BaseApplication.context, Group_Location[1]) != PackageManager.PERMISSION_GRANTED) {Log.d(TAG, "Don't apply for these permission");return null;}String strLocation = null;try {//获取系统的服务,locationManager = (LocationManager) BaseApplication.context.getSystemService(Context.LOCATION_SERVICE);//创建一个criteria对象Criteria criteria = new Criteria();criteria.setAccuracy(Criteria.ACCURACY_COARSE);//设置不需要获取海拔方向数据criteria.setAltitudeRequired(false);criteria.setBearingRequired(false);//设置允许产生资费criteria.setCostAllowed(true);//要求低耗电criteria.setPowerRequirement(Criteria.POWER_LOW);String provider = locationManager.getBestProvider(criteria, true);Location location = locationManager.getLastKnownLocation(provider);if (location == null)return null;//strLocation =  location.getLongitude()+","+location.getLatitude();strLocation = convertAddress(BaseApplication.context, location.getLatitude(), location.getLongitude());Log.d(TAG, "location is = " + strLocation);} catch (Exception e) {e.printStackTrace();}return strLocation;}
经纬度转为地理信息

将经纬度转为具体地理信息,由于心知天气API需要提供城市级城市数据,所以我们只需要xx市数据即可

 private String convertAddress(Context context, double latitude, double longitude) {Geocoder mGeocoder = new Geocoder(context, Locale.getDefault());try {List<Address> mAddresses = mGeocoder.getFromLocation(latitude, longitude, 1);if (mAddresses != null && mAddresses.size() > 0) {Address address = mAddresses.get(0);Log.d(TAG, "国家 is " + address.getCountryName());Log.d(TAG, "省 is " + address.getAdminArea());Log.d(TAG, "市 is " + address.getLocality());Log.d(TAG, "区/县 is " + address.getSubLocality());Log.d(TAG, "具体 is " + address.getFeatureName());return  address.getLocality();}} catch (Exception e) {e.printStackTrace();}return null;}

页面初始化

详情页采用的是viewPager添加多个views实现页面滑动;
首先获取数据库内所有存储的城市名称,然后通过get模式获取天气的网络数据,然后填充到adapter中;需要注意的是获取的本地位置在整个app都较为特殊,与其他添加的城市数据存在差距,因为需要改变其显示状态,具体后文会提交,所以并未将此添加到数据库中。

private void initViewPager(){dao = new Dao(this);locationList = dao.QueryAll();location = LocationUtils.getInstance().getLocationInfo();if (locationList == null || locationList.size() == 0){locationList = new ArrayList<>();locationList.add(location);}else {locationList.add(0,location);}for (int i = 0; i < locationList.size(); i++) {Message message = new Message();message.what = WEATHER_Start;message.obj = locationList.get(i);handler.sendMessage(message);}adapter.notifyDataSetChanged();}

单个页面初始化,通过遍历,将所有页面进行填充;每个界面背景图片会根据心知天气返回天气状态发生变化,由于素材有限,只适配了较为常见的几种状态,例如:晴、多云、阴、雨、雪、雷

 /*** viewPager页面添加,包括实况天气数据、预期24小时、预报7天*/private void addView(String location){View view = LayoutInflater.from(this).inflate(R.layout.activity_main,null,false);LinearLayout weatherLayout;TextView weatherLocation,weatherTemp,weatherStatus,weatherWindDirection,weatherWindSpeed,weatherCloud,weatherFellLikes,weatherHum,weatherVisibility,weatherPressure;RecyclerView weather7D,weather24H;weatherLayout = view.findViewById(R.id.mainLinearLayout);weatherLocation = view.findViewById(R.id.normal_city);weatherTemp = view.findViewById(R.id.normal_temp);weatherStatus = view.findViewById(R.id.normal_status);weatherWindDirection = view.findViewById(R.id.windDirection);weatherWindSpeed = view.findViewById(R.id.windSpeed);weatherCloud = view.findViewById(R.id.cloud);weatherFellLikes = view.findViewById(R.id.bodyTmp);weatherHum = view.findViewById(R.id.hum);weatherVisibility = view.findViewById(R.id.visibility);weatherPressure = view.findViewById(R.id.pressure);weather7D = view.findViewById(R.id.Recycler_7D);weather24H = view.findViewById(R.id.Recycler_24H);List<WHourly24Bean> hourlyBeanList = new ArrayList<>();List<WFuture7Bean> dailyBeanList = new ArrayList<>();weather24H.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));Weather24HAdapter  adapter24H = new Weather24HAdapter(hourlyBeanList);weather24H.setAdapter(adapter24H);weather7D.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));Weather7DAdapter adapter7D = new Weather7DAdapter(dailyBeanList);weather7D.setAdapter(adapter7D);viewList.add(view);titleList.add(location);adapter.notifyDataSetChanged();weatherLocation.setText(location);JsonUtils.INSTANCE.getNowWeather(location, new WeatherCallback_now() {@Overridepublic void onFailed(int ErrorCode) {}@Overridepublic void onSuccess(WRealTimeBean bean) {if (bean != null){runOnUiThread(()->{weatherTemp.setText(bean.getTemperature() + "°");//温度weatherStatus.setText(bean.getText());//天气状态weatherWindSpeed.setText(bean.getWindSpeed() + "km/h");//风速weatherWindDirection.setText(bean.getWindDirection()+bean.getWindDirectionDegree()+ "°");//风向weatherCloud.setText(bean.getClouds() + "%");//云量weatherFellLikes.setText(bean.getFeelsLike() + "°");//体感温度weatherHum.setText(bean.getHumidity() + "%");//湿度weatherVisibility.setText(bean.getVisibility() + "km");//可见度weatherPressure.setText(bean.getPressure() + "mb");//气压switch (bean.getText()){case "晴":weatherLayout.setBackground(getDrawable(R.drawable.icon_bg_sunny));break;case "多云":weatherLayout.setBackground(getDrawable(R.drawable.icon_bg_cloudy));break;case "阴":weatherLayout.setBackground(getDrawable(R.drawable.icon_bg_cloudy));break;case "雨":weatherLayout.setBackground(getDrawable(R.drawable.icon_bg_big_rain));break;case "雪":weatherLayout.setBackground(getDrawable(R.drawable.icon_bg_snow));break;case "雷":weatherLayout.setBackground(getDrawable(R.drawable.icon_bg_thunder));break;}});}else {Log.d(TAG,"bean is empty");}}});JsonUtils.INSTANCE.getFuture7Weather(location, new WeatherCallback_7D() {@Overridepublic void onFailed(int ErrorCode) {}@Overridepublic void onSuccess(List<WFuture7Bean> beanList) {if (beanList != null && beanList.size() > 0){if (dailyBeanList != null && dailyBeanList.size() > 0){dailyBeanList.clear();}dailyBeanList.addAll(beanList);runOnUiThread(()->{adapter7D.notifyDataSetChanged();});}}});JsonUtils.INSTANCE.getHourly24Weather(location, new WeatherCallback_24H() {@Overridepublic void onFailed(int ErrorCode) {}@Overridepublic void onSuccess(List<WHourly24Bean> beanList) {if (beanList != null && beanList.size() > 0){if (hourlyBeanList != null && hourlyBeanList.size() > 0){hourlyBeanList.clear();}hourlyBeanList.addAll(beanList);runOnUiThread(()->{adapter24H.notifyDataSetChanged();});}}});}

ViewPager子页面编辑

view添加和删除都是通过EventBus进行监听,然后操作adapter完成操作

 /*** 在城市页面进行数据添加或删除,使用EventBus进行监测*/@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)public void onEvent(LocationBusBean bean){if (bean != null){if (bean.getDeletePos() == -1){addView(bean.getLocation());updateIndicator();}else {removeView(bean.getLocation());}}}
页面添加

此功能就是重复调用单个views初始化方法,然后使用adapter进行notify和更新指示器即可

页面删除

通过从天气编辑页面点击的城市子项,传过来的城市数据,然后与views存在的数据进行匹配,然后删除对应的界面

   /*** 删除viewPager子项*/private void removeView(String location){int position = titleList.indexOf(location);if (position != -1){viewList.remove(position);titleList.remove(position);adapter.notifyDataSetChanged();}updateIndicator();}

指示器

指示器是通过两个xml定义不同的圆,然后通过selector文件进行选择,使用一个LinearLayout控件作为指示器控件,通过传入的指示器个数,添加多个view,然后设置背景为selector文件,通过设置其enable属性,改变圆形状态

  private void updateIndicator(){binding.indicatorLayout.removeAllViews();List<String> list = dao.QueryAll();/*** 因为有一个本地位置,所以需要+1*/int size = 1;if (list != null){size = list.size()+1;}for (int i = 0; i < size; i++) {View view = new View(this);view.setBackgroundResource(R.drawable.selector_indicator);view.setEnabled(false);LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(30,30);params.rightMargin = 15;params.leftMargin = 15;binding.indicatorLayout.addView(view,params);}}

天气编辑页

此页面功能包括searchView和listView匹配搜索、城市天津、城市天气简单版子项显示、天气子项编辑等功能

效果图


简版天气数据

此数据与天气详情页数据请求一致,只是数据更为简单,值得注意的是,从天气详情页传过来的城市,并不显示其具体名称,而是以我的位置代替,因为是子线程加载,获取的数据排列方式不一,所以在展示数据时,需要将本地数据一列移到到最前方。

获取数据并初始化

/*** 获取所以数据库中城市的天气*/private void getWeatherData() {if (dao == null) {dao = new Dao(this);}beanList.clear();//防止数据重复List<String> locationList = dao.QueryAll();if (locationList == null || locationList.size() == 0) {locationList = new ArrayList<>();}if (locationList.size() > 0) {String data = locationList.get(0);if (!data.equals(location)) {locationList.add(0, location);}}else {locationList.add(location);}for (int i = 0; i < locationList.size(); i++) {postWeather(locationList.get(i), locationList.size());}}

子项删除

通过EventBus通知天气详情页进行更新

adapter.setDelItemClickListener(new LocationAdapter.OnWeatherItemsClickListener() {@Overridepublic void onClickListener(int pos, String location) {beanList.remove(pos);dao.Delete(location);adapter.notifyDataSetChanged();EventBus.getDefault().postSticky(new LocationBusBean(location, pos));}});

搜索

效果图

更改SearchView背景和字体样式

定义一个xml文件,然后在SearchView的background属性引用即可改变其背景

 android:background="@drawable/searchview_bg"
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle"><corners android:radius="10dp" /><solid android:color="@color/searchViewBg" />
</shape>

更改SearchView字体颜色,需要获取其内置的一个控件id,然后通过EditText进行改变即可,TextView也可以

 /*** 更改searchView字体颜色*/private void initSearchViewStyle() {EditText editText = (EditText) binding.searchView.findViewById(androidx.appcompat.R.id.search_src_text);if (editText != null) {editText.setTextColor(getColor(R.color.white));editText.setHintTextColor(getColor(R.color.searchHintColor));editText.setTextSize(14);SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);binding.searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));} else {Log.d("MoreLocationActivity", "empty");}}

搜索初始化

重点在于xml城市列表文件,然后采用默认的adapter作为适配

private void initListView() {String[] cityArray = getResources().getStringArray(R.array.city);binding.locationList.setAdapter(new ArrayAdapter(this, R.layout.searchview_item, cityArray));/*** 属性为true表示listview获得当前焦点的时候,与相应用户输入的匹配符进行比对,筛选出匹配的ListView的列表中的项*/binding.locationList.setTextFilterEnabled(true);binding.searchView.setOnQueryTextListener(this);binding.searchView.setSubmitButtonEnabled(false);binding.locationList.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {String str = adapterView.getAdapter().getItem(i).toString();if (dao == null) {dao = new Dao(MoreLocationActivity.this);}boolean flag = dao.Query(str);if (flag) {Toast.makeText(MoreLocationActivity.this, "该城市已添加到天气列表,请勿重复添加", Toast.LENGTH_SHORT).show();} else {dao.Insert(str);EventBus.getDefault().postSticky(new LocationBusBean(str, -1));handler.sendEmptyMessage(1);Toast.makeText(MoreLocationActivity.this, "添加成功", Toast.LENGTH_SHORT).show();}binding.locationList.clearTextFilter();binding.locationList.setVisibility(View.GONE);}});}

搜索字符监听

一开始那个提示黑框就比较呆,所以可以通过后面语句进行隐藏,因为搜索列表是覆盖简版天气子项的,所以当搜索列表显示时,天气简版隐藏,反之,亦然;

 @Overridepublic boolean onQueryTextSubmit(String query) {return false;}@Overridepublic boolean onQueryTextChange(String newText) {if (TextUtils.isEmpty(newText)) {binding.locationList.clearTextFilter();binding.locationList.setVisibility(View.GONE);} else {binding.locationList.setVisibility(View.VISIBLE);binding.locationList.setFilterText(newText);//隐藏黑框binding.locationList.dispatchDisplayHint(View.INVISIBLE);}return true;}

下载地址

Gitte下载链接

Android——一个简单的天气APP相关推荐

  1. Android——一个简单的音乐APP(二)

    一个简单的音乐APP 效果视频 前言 音乐下载 音乐下载效果图 实习步骤&思想 添加到下载队列 单任务下载 多任务下载 音乐下载 获取音乐下载源 创建本地路径 创建目录 开始音乐下载 下载进度 ...

  2. Android——一个简单的闹钟APP

    一个简单的闹钟 效果视频 闹钟子项 子项布局效果图 闹钟适配器类 闹钟初始数据 侧滑删除闹钟子项 添加依赖 布局设置 实现侧滑回调方法 绑定RecyclerView 删除子项 新增闹钟子项 序列化实体 ...

  3. Android——一个简单的APP模版

    Android--一个简单的APP模版 述 效果视频 技术栈 地图.定位.导航 地图 定位 导航 偏好设置 当前导航信息 预定车位和订单结算 创建订单 计时服务传导 后台服务计时 订单结算 个人信息 ...

  4. 使用Flutter编写一个简单的天气查询App

    使用Flutter编写一个简单的天气查询App Flutter项目目录分析 入口函数 home:主页面 编写天气应用 网络请求 数据解析 布局编写 Flutter里基础的Widget 上 中 下 Fl ...

  5. 用Android Studio设计的一个简单的闹钟APP

    该闹钟是用Android Studio为安卓手机设计的一个简单的闹钟APP 一.介绍系统的设计界面 闹钟的布局文件代码如下 <?xml version="1.0" encod ...

  6. 一个简单的手电筒APP源码分享(支持Android O(8.0)及以下版本)

    一个简单的手电筒APP(无闪光灯的设备开启屏幕照明模式) GitHub地址: https://github.com/djzhao627/SimpleTorch 打包下载 http://download ...

  7. 使用Android studio做一个简单的网站APP

    1.首先创建一个空白Android项目 2.然后打开项目,切换为Android视图,这时候会看到三个文件夹,分别是manifests.java.res.首先修改res/layout下的activity ...

  8. android studio的GearVR应用开发(二)、一个简单的VR app(Oculus官方GearVR开发教程,翻译转载)

    声明:本文是Oculus官方的GearVR开发教程,为本人翻译转载,供广大VR开发爱好者一同学习进步使用. 原文章 一个简单的VR app 概观 在搭建好GearVR框架后,让我们一起来创建第一个VR ...

  9. 简单Android手机APP地图,android最简单手机地图APP(只需5分钟)

    android最简单手机地图APP--只有三部分. 第一部分 首先建立一个MapActivity在setContentView(R.layout.activity_map);中创建一个代码如下. [h ...

最新文章

  1. 运行php能运行asp么,配置使web server即能运行asp又能运行PHP(不装Apache)
  2. 从数据库到迁移调优,鲲鹏高校行太原站正式启动
  3. 文件分布式存储实现例程
  4. php替换文件中的数据库,批量替换php文件中的class,id的值
  5. pytorch 之 加载不同形式的预训练模型
  6. SAP UI5 DatePicker setDateValue(tempString)
  7. Android开发之RecyclerView嵌套ListView自动计算高度的方法
  8. 判断是否是2的N次方各方法运行速度比较
  9. Chapter 2 Open Book——5
  10. git修改user.name 和user.email
  11. 剑指offer——16.数值的整数次方
  12. eclipse data source explorer 编辑触发器
  13. php网页电话外呼,一种基于web网页端的电话外呼方法与流程
  14. pythonQQ连连看秒杀脚本
  15. [RK3288][Android7.1]调试笔记 --- 内置高版本的APK编译User版本出错解决
  16. 跨越阶层,至少需要三代人的努力;看千年前的眉山五苏是如何完成的
  17. QChart数据可视化应用
  18. Java学习之路1——安装JDK1.8||安装idea2022||Java项目创建【重拾Java】
  19. 构建银行人工智能用户画像和自动营销体系
  20. 使用Cesium for Unreal插件构建航班轨迹

热门文章

  1. Git怎么操作 一文学会使用Git (比视频还详细)
  2. php 积分商城_积分商城 - 基于ThinkPHP和FastAdmin开发的积分商城系统 – 基于ThinkPHP和Bootstrap的极速后台开发框架...
  3. 蒲公英分发安装iOS应用
  4. 世界级医学院校--哈佛医学院进修各大教学附属医院申请指南
  5. go map fatal error:concurrent map read and map write
  6. SpringBoot 指标监控
  7. [案例分享]激活电力价值,八大案例深度解析电力大数据应用
  8. 【前端】实现视频自定义字幕,中英文,彩色,你也可以
  9. 今年6月起噪声污染防治法开始实施
  10. CDMA EVDO 模块