进入实战——开发酷欧天气(3)
14.6 手动更新天气和切换城市(原书p532)
不知不觉已经接触kotlin的第四天了,原书中的最后一个实践项目“酷欧天气”也改写的差不多了,稍后会将源码上传至csdn!作为代码样本吧!
每日一图做背景
虽然说现在我们已经把天气界面编写的非常不错了,不过和市场上的一些天气软件的界面比起来,仍然还有一定差距的。(原书p526)
配置build.gradle
为了加载bing.com的每日一图到本地缓存,我们需要添加一个外部类库glide
关于glide:http://blog.csdn.net/fancylovejava/article/details/44747759
build.gradle:
dependencies { ...... compile "com.github.bumptech.glide:glide:3.8.0" }
添加glide到gradle,sync一下
最终布局activity_weather.xml:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorPrimary"> <ImageView android:id="@+id/bing_pic_img" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" /> <android.support.v4.widget.DrawerLayout android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipe_refresh" android:layout_width="match_parent" android:layout_height="match_parent"> <ScrollView android:id="@+id/weather_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:overScrollMode="never" android:scrollbars="none"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true" android:orientation="vertical"> <include layout="@layout/title" /> <include layout="@layout/now" /> <include layout="@layout/forecast" /> <include layout="@layout/aqi" /> <include layout="@layout/suggestion" /> </LinearLayout> </ScrollView> </android.support.v4.widget.SwipeRefreshLayout> <fragment android:id="@+id/choose_area_fragment" android:name="cn.cslg.weatherkotlin.ChooseAreaFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start" /> </android.support.v4.widget.DrawerLayout> </FrameLayout>
注意:可以看到新增了一个ImageView,他就是我们用来放背景的容器,由于他的外部是一个FrameLayout,所以他和他的兄弟节点的内容会靠左上角停放,那么这个ImageView将和其他的内容重叠,造出一种背景的效果
修改WeatherActivity.kt:
...... class WeatherActivity : AppCompatActivity() { ...... private var bingImg: ImageView? = null ...... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //状态栏透明化 if (Build.VERSION.SDK_INT >= 21) { val v = window.decorView v.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE window.statusBarColor = Color.TRANSPARENT } bingImg = find<ImageView>(R.id.bing_pic_img) ...... } ...... //获取每日一图的API private fun loadBingImg() { val url = "http://guolin.tech/api/bing_pic" async { val s = URL(url).readText() uiThread { Glide.with(this@WeatherActivity).load(s).into(bingImg) } } } }
注意:我们添加了loadBingImg方法,专门加载每日一图,其中多线程结束后,使用了Glide将图片在进入ImageView当中变成背景。
我们还把状态栏设置为透明,并且将他变成应用的一部分(应用全屏了),此时状态栏可能会和下面的内容挨得太近了,需要在xml中设置:fitsSystemWindows="true",空出一段空间来
手动更新天气
实现下拉刷新当前选定城市的天气信息
布局文件activity_weather.xml上面已经给出最终版本
主要添加了一个SwipeRefreshLayout容器,这个容器可以使用下拉刷新功能,将需要刷新的页面全部包含进去
修改WeatherActivity.kt:
...... class WeatherActivity : AppCompatActivity() { ...... var swipeRefresh: SwipeRefreshLayout? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_weather) swipeRefresh = find<SwipeRefreshLayout>(R.id.swipe_refresh) swipeRefresh!!.setColorSchemeResources(R.color.colorPrimary) ...... val weatherId = defaultSharedPreferences.getString("weather_id","") weatherLayout!!.visibility = View.INVISIBLE requestWeather(weatherId) swipeRefresh!!.setOnRefreshListener { //刷新当前的城市weather_id requestWeather(defaultSharedPreferences.getString("weather_id","")) } } //从服务器加载天气信息 fun requestWeather(wid: String) { val url = "http://guolin.tech/api/weather?cityid=" + wid + "&key=" + KEY async { val s = URL(url).readText() uiThread { val weather = Gson().fromJson(s, Weather::class.java) //关闭下拉刷新 swipeRefresh!!.isRefreshing = false Log.d("url",url) Log.d("url",weather.toString()) showWeatherInfo(weather.HeWeather[0]) } } } ...... }
注意:获取到了SwipeRefreshLayout控件,使用setColorSchemeResources设置了颜色,setOnRefreshListener设置了下拉刷新事件的监听。
切换城市
将ChooseAreaFragment这个碎片放到了offcanvas(侧滑)当中,实现侧滑后选择其他城市
在title.xml添加一个显示offcanvas的按钮:
<?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="?attr/actionBarSize" > <Button android:id="@+id/nav_button" android:layout_width="30dp" android:layout_height="30dp" android:layout_alignParentLeft="true" android:layout_centerInParent="true" android:layout_marginLeft="10dp" android:background="@android:drawable/ic_menu_sort_by_size" /> ...... </RelativeLayout>
修改activity_weather.xml(详见前面的最终布局)
主要添加了一个DrawerLayout,用于存放两个直子控件,第一个是主屏幕显示内容,第二个是侧滑内容
修改WeatherActivity.kt:
...... class WeatherActivity : AppCompatActivity() { ...... private var navButton:Button?=null var drawLayout:DrawerLayout?=null ...... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ..... drawLayout = find<DrawerLayout>(R.id.drawer_layout) navButton = find<Button>(R.id.nav_button) navButton!!.setOnClickListener{ drawLayout!!.openDrawer(GravityCompat.START) } } ...... }
注意:仅仅是添加了按钮触发事件和获取DrawerLayout的控件
修改:ChooseAreaFragment.kt
...... class ChooseAreaFragment : Fragment() { ...... override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) //列表点击监听事件 listView!!.setOnItemClickListener { _, _, position, _ -> when (current_level) { ...... LEVEL_COUNTY -> { selectedCounty = countyList[position] defaultSharedPreferences.edit().putString("weather_id", selectedCounty!!.weather_id).apply() if (activity is MainActivity) { startActivity<WeatherActivity>() activity.finish() //将MainActivity销毁掉 } else if (activity is WeatherActivity) { val act = activity as WeatherActivity act.drawLayout!!.closeDrawers() act.swipeRefresh!!.isRefreshing = true //显示下拉刷新 act.requestWeather(selectedCounty!!.weather_id) } } } } ...... } ...... }
注意:在Fragment获取activity中的控件,kotlin的anko库可以直接使用activity,然后调用其它activity里的属性,比如swipeRefresh和drawLayout属性,当然前提是他们是public,不可以像其他仅仅在当前类下用的控件那样设置为private!
当用户触发选择了城市的事件后,将会去请求服务器的天气信息,在这之前应该将选择的城市的weather_id保存到SharedPreferences中,这样用户不必每次打开app时都要选择城市,app可以自己根据上次的选择请求天气数据
kotlin中使用is来代替java中的instanceof,可以用来判断当前实例所属类
如果是MainActivity,则进入WeatherActivity中(其实这表示app是用户第一次打开,还没有选择过城市)
anko库提供了startActivity<>方法,直接启动其他的活动
anko库的一些辅助方法:http://www.tuicool.com/articles/VrIjIjq
如果是WeatherActivity则关闭侧滑和下拉刷新,立即请求数据去!
结语
至此,就把原书中的“酷欧天气”的例子使用kotlin语言重写了
代码下载:http://download.csdn.net/detail/u014466109/9851378
虽然我在这之前从来没有接触过kotlin语言,甚至闻所未闻(希望不要说我孤陋寡闻,毕竟我在这之前连Android都没写过,我的专长算是web)
但此时我想我爱上了kotlin这门现代语言
总结一下相对java开发的一些优点:
100%兼容java所有类库
每一行kotlin可以节约3-4行的java代码
anko库简直就是Android界的jQuery,简化和封装了许多原本很长参数很多的方法
data class类让你少些多少文件,你没必要理会那些一个类一个文件的java pojo,也不需要自己写get set方法
严格且安全的null类型
val,var变量自动推断变量类型
val变量适合多线程,并发安全
没有无聊的分号!
性能,有过之而无不及
清晰的lambda表达式,可代替难看复杂的匿名类
共同学习,写下你的评论
评论加载中...
作者其他优质文章