SunnyWeather

[TOC]

前言:好久没有管理自己的博客了,最近心血来潮回来捯饬了一下自己的博客感觉还是很不错的,一开始嫌麻烦觉得在这里写文章很麻烦,但是自己真正用心来弄的时候会发现这个东西并不难。确实,很多时候我们在做什么之前都会去考虑所做之事自己是否能够完成或轻松的完成,而我在很多时候遇到一些比较复杂的事情或者难题的时候,都会有一种畏难心理,导致自己心理上去抵制它哈哈哈,属实是不够成熟。好伐啦,我们准备进入正题之前,我想大致分享一下本篇博客的创作源泉以及创作目的。最近在学习Kotlin的天气,发现自己暑假看书得来的知识没有掌握好,并且对很多Kotlin的知识都不够熟悉不够了解,甚至一些基本用法自己都还不会,然后不得不花费很多时间去改bug(抄书都抄错┭┮﹏┭┮),害,不过还是成功的解决了问题,也在bug中加深了自己对一些知识的理解,就还挺不错的哈哈哈。昨天已经写(写(×)抄(√))完天气了,虽然完成的比较草率(?)哈哈哈,不过也是在低下的效率中逐渐掌握了一些新的知识,故写这篇博客来记录一下自己的学习记录!好啦让我们一起开启SunnyWeather的知识之旅☞!

一、功能需求及技术可行性分析

⭐我们在做一个项目之前,首先应该对程序进行需求分析,想一想我们的项目里面需要有什么,需要实现什么功能…我们才动手去实现这些功能。目前SunnyWeather中需要具备一下功能:

🌼可以搜索全球大多数国家的各个城市数据

🌼可以查看全球绝大多数城市的天气信息(准确程度取决于你所使用的天气API)

🌼可以自由切换城市,查看其他城市的数据

🌼可以手动刷新天气

以上是该项目主要的功能点,如果需要全部实现这些功能需要用到网络、数据存储、协程、线程等等技术,还是有一定的难度的,但是既然你发现这这篇宝藏博客,那么我将手把手教会你👊!

⭐上面只说了项目的主要功能以及需要实现的相关技术,却没有涉及一个关键的点,那就是天气的信息该怎么获取,这个不用担心,已经有大佬为你做好了,你只需要会调用就行了。

本篇博客以彩云天气为例,简单介绍如何获得彩云天气的API。

🍃首先登录彩云天气官网注册:https://dashboard.caiyunapp.com/

🍃注册完了以后,申请令牌(重要),这里还需要让你填写应用链接,由于还没有创建,可以先不填。

🍃拿到令牌以后就可以使用彩云天气提供的各种API接口了。

二、Git代码托管

⭐这里就对Git就不介绍了,毕竟你都能看见我的博客,那你肯定也略知一二,我自己其实也不够了解没什么发言权。好了进入正题,首先你需要有一个Git账号,然后新建一个仓库(Repository),不需要勾选其他的东西,默认就行了,然后点击Create repository按钮就创建成功了。

🍌接下来就需要创建项目了,在Android studio中新建一个Kotlin项目,创建完需要去将仓库的远程版本库克隆到本地,将仓库的版本库中的Https复制一份,然后打卡cmd,进入你这个项目的目录下,输入git clone (这里是你项目仓库的Https),克隆成功需要做一件事情,那就是将SunnyWeather项目中的文件都复制到上一层目录,这样做的目的:可以将整个项目工程目录添加到版本控制中去,这里有个坑,在这些文件里面有一个隐藏文件(.git),需要你文件夹设置一下才能看见这些隐藏的文件,把所有的文件复制到上一层的时候会有一个文件.gitignote文件,直接覆盖就好,复制完将SunnyWeather文件夹删除。哈哈可能你会觉得有点麻烦,没办法,我一开始接触的时候也觉得麻烦,因为对计算机这些指令不熟悉很陌生,不过没关系,以后就会习惯了。最后,将SunnyWeather项目中的文件提交到Github上面。

具体指令如下(接着上面的操作继续):

🍍git add . 添加操作

🍍git commit -m “(这里可以描述你这次提交代码的简单说明,比如:First commit)”

🍍git push origin main

👁到这一步完成就成功的实现了Git代码托管。

三、搭建MVVM项目框架

⭐由于这是我第一次搭建MVVM项目框架加上自己本身对MVVM也不够了解,我就不展开介绍了,详细可以看这个大佬的文章,我们的项目需要严格按照MVVM项目框架进行搭建。在你的项目的包下新建几个包,项目结构如下图所示。

图中的logic包用于存放业务逻辑相关的代码,ui包用于存放页面展示相关的代码,而logic包下的dao、mode、network分别用于存放数据访问对象、对象模型以及网络相关的代码,ui包下的place和weather则是SunnyWeather中的两个主要界面。由于这些操作会涉及网络和对象存储等操作,需要导包,编辑app/build.gradle文件,如下图所示:

四、搜索全球城市数据

⭐在开始写代码之前,我们可以先准备一些方便后期编程的一些工具类,这里需要写一样获得全局Context的工具类,并且将令牌配置在这里面。在你的项目的包下,新建一个SunnyWeatherApplycation类,代码如下图所示:

红色箭头所指填你申请到的令牌~

然后我们还需要在AndroidManifest.xml文件中更改android:name指定的内容,改为.SunnyWeatherApplication。

根据彩云天气的API文档,接下来我们开始定义相关的数据模型。

⭐在logic/model包下新建PlaceResponse.kt文件,并在这个文件中编写如下代码:

⭐接着定义彩云天气测试搜索API的Retrofit接口,在logic/network包下新建PlaceService接口,代码如下图所示:

⭐定义好了PlaceSertvice接口,我们需要使用它的前提得需要有一个Retrofir构建器,在logic/network包下新建一个ServiceCreator单例类,代码如下所示:

⭐然后我们需要再定义一个统一的网络数据源访问入口,对所有散落请求的API进行封装。同样在logic/network包下新建一个SunnyWeatherNetwork单例类,代码如下所示:

⭐另外我们需要在logic包下新建一个Repository单例类,这是仓库层的统一封装入口,代码如下图所示:

⭐到这里,逻辑层的实现就只剩最后一步了,定义ViewModel层,这里对ViewModel层解释一下,它相当于逻辑层和UI层的一个桥梁,虽然它更偏向于逻辑层。在ui/place包下新建一个PlaceViewModel,代码如下所示:

🍉由于作者很懒,就不进行详细讲解了,天气的UI层代码以及RecyclerView的适配器和展示天气的PlaceFragment可以去我仓库clone查看,我就不在码字了,我的Github,仓库名字即SunnyWeather。

注意:由于PlaceFragment中实现了一个搜索框布局,需要修改原生的ActionBar,修改res/values/theme.xml中的代码

改成这样

1
<style name="Theme.SunnyWeather" parent="Theme.MaterialComponents.Light.NoActionBar">

最后别忘记添加网络权限,AndroidManifest.xml文件中添加如下代码:

1
<uses-permission android:name="android.permission.INTERNET" />

完成这些就可以实现全球城市的搜索了,可以看我的Demo,这里不多放图了。

好了,关于城市搜索这块的代码就先写到这,现在提交代码到Github中去。

🍃git add . 添加操作

🍃git commit -m “(这里可以描述你这次提交代码的简单说明,比如:实现搜索全球城市数据功能)”

🍃git push origin main

五、显示天气信息

这一部分的逻辑和实现全球城市的搜索类似,根据API天气信息接口返回的JSON数据进行解析,具体操作如下:

⭐首先建立数据模型,在logic/model包下新建DailyResponse.kt文件,代码如下:

🥥接着定义一个用于访问天气API的Retrofit接口,在logic/network包下新建WeatherService接口,代码如下:

⚡在SunnyWeatherNetwork这个网络数据源访问入口对新增的WeatherService接口进行封装,添加如下代码到SunnyWeatherNetwork:

💧完成了网络层的代码编写,和之前一样,创建仓库层,不过前面已经创建仓库层Repository了,在Repository中添加如下代码:

🍇到这逻辑层就只剩最后一步了,定义ViewModel层,在ui/weather包下新建一个WeatherViewModel,代码如下所示:

⚽又到了我要偷懒的UI层了,和上面的ui层一样去我仓库clone项目自己去看把,我手敲的好累。

📕天气的页面布局完成以后,我们还需要一个转换函数,将获得到的天气代码转成一个Sky对象,为什么要这样,因为返回的数据是这样的…哈哈哈哈,在logic/model包下新建一个Sky.kt文件,代码如下所示:(这里太长了就不截图了,截图截不全)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Sky(val info:String,val icon:Int,val bg:Int)
private val sky = mapOf(
"CLEAR_DAY" to Sky("晴", R.drawable.one_background, R.drawable.img),
"CLEAR_NIGHT" to Sky("晴", R.drawable.one_background, R.drawable.img),

"PARTLY_CLOUDY_DAY" to Sky("多云", R.drawable.one_background, R.drawable.img),

"PARTLY_CLOUDY_NIGHT" to Sky("多云", R.drawable.one_background, R.drawable.img),

"CLOUDY" to Sky("阴", R.drawable.one_background, R.drawable.img),

"WINDY" to Sky("大风", R.drawable.one_background, R.drawable.img),

"LIGHT_RAIN" to Sky("小雨", R.drawable.one_background, R.drawable.img),

"MODERATE_RAIN" to Sky("中雨", R.drawable.one_background, R.drawable.img),

"HEAVY_RAIN" to Sky("大雨", R.drawable.one_background, R.drawable.img),
"STORM_RAIN" to Sky("暴雨", R.drawable.one_background, R.drawable.img),
"THUNDER_SHOWER" to Sky("雷阵雨", R.drawable.one_background, R.drawable.img),
"SLEET" to Sky("雨夹雪", R.drawable.one_background, R.drawable.img),
"LIGHT_SHOW" to Sky("小雪", R.drawable.one_background, R.drawable.img),
"MODERATE_SNOW" to Sky("中雪", R.drawable.one_background, R.drawable.img),
"HEAVY_SNOW" to Sky("大雪", R.drawable.one_background, R.drawable.img),
"STORM_SNOW" to Sky("暴雪", R.drawable.one_background, R.drawable.img),
"HAIL" to Sky("冰雹", R.drawable.one_background, R.drawable.img),
"LIGHT_HAZE" to Sky("轻度雾霾", R.drawable.one_background, R.drawable.img),
"MODERATE_HAZE" to Sky("重度雾霾", R.drawable.one_background, R.drawable.img),
"HEAVY_HAZE" to Sky("重度雾霾", R.drawable.one_background, R.drawable.img),
"FOG" to Sky("雾", R.drawable.one_background, R.drawable.img),
"DUST" to Sky("浮尘", R.drawable.one_background, R.drawable.img)

)
fun getSky(skycon:String):Sky{
return sky[skycon] ?:sky["CLEAR_DAY"]!!
}

由于省事,我就没有给相应的天气设置相应的背景图和图标了,你有兴趣的话可以自己去找图片和图标添加,关于天气自定义图标添加可以去看我在csdn写的一篇博客☞Android studio使用阿里巴巴图标矢量图库

六、手动刷新天气和切换城市

☀手动刷新天气简单,这里就不详细说了,使用一个SwipeRefreshLayout布局就行,详细步骤看我的项目demo,里面很详细。

🌙切换城市也是一个DrawerLayout布局的事情,也不细说了,看demo。需要注意的是,在侧滑菜单消失以后记得隐藏输入法,你可以试试不隐藏输入法时的效果,在DrawerLayout的监听里实现相关代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
drawerLayout.addDrawerListener(object :DrawerLayout.DrawerListener{

override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
}

override fun onDrawerOpened(drawerView: View) {
}

@SuppressLint("ServiceCast")
override fun onDrawerClosed(drawerView: View) {
//因为弹出这个滑动菜单以后,如果想输入新的地址会弹出一个输入法,如果在你输完以后直接就关闭滑动菜单的话需要把输入法也关闭
//关闭弹出的输入法
val manager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
manager.hideSoftInputFromWindow(drawerView.windowToken,InputMethodManager.HIDE_NOT_ALWAYS)
}

override fun onDrawerStateChanged(newState: Int) {
}
})

最后一个阶段的开发任务也完成了,记得提交代码

🍓git add . 添加操作

🍓git commit -m “(这里可以描述你这次提交代码的简单说明,比如:新增切换城市和手动更新天气的功能)”

🍓git push origin main

七、项目提升

这个项目我是根据郭霖的《第一行代码》写的,我的demo过于简陋,很多能简单的就简单,以及一些ui可能你会觉得好丑或者什么,你可以自行修改;或者说你觉得天气显示的太少了或者不够精美不够详细,你也可以根据彩云天气的API自行修改……终于敲完了,好累好累好累!


SunnyWeather
https://crwei996.github.io/2022/09/13/SunnyWeather/
Author
Crwei996
Posted on
September 13, 2022
Licensed under