zoukankan      html  css  js  c++  java
  • 用kotlin方式打开《第一行代码:Android》之开发酷欧天气(最终版)

    参考:《第一行代码:Android》第2版——郭霖

    注1:本文为原创,例子可参考郭前辈著作:《第一行代码:Android》第2版

    注2:本文不赘述android开发的基本理论,不介绍入门知识,不介绍Android Studio基本安装,开门见山,直接使用kotlin改写郭前辈的《第一行代码:Android》中的部分例子,有机会的话自己做一些新例子出来!

    注3:本文尝试用Google新官推语言kotlin改写《第一行代码:Android》中的案例,偶尔涉及java作为对比

    注4:开发基于Android Studio 3.0,并且新建项目时勾选“support kotlin”

    进入实战——开发酷欧天气(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开发的一些优点:

    1. 100%兼容java所有类库
    2. 每一行kotlin可以节约3-4行的java代码
    3. anko库简直就是Android界的jQuery,简化和封装了许多原本很长参数很多的方法
    4. data class类让你少些多少文件,你没必要理会那些一个类一个文件的java pojo,也不需要自己写get set方法
    5. 严格且安全的null类型
    6. val,var变量自动推断变量类型
    7. val变量适合多线程,并发安全
    8. 没有无聊的分号!
    9. 性能,有过之而无不及
    10. 清晰的lambda表达式,可代替难看复杂的匿名类

    版权

    转载请注明:http://www.cnblogs.com/devilyouwei/p/6901264.html

  • 相关阅读:
    centos7.6安装Oracle11g过程记录(下)
    centos7.6 安装解压缩软件
    centos7.6 安装及配置ftp服务
    MySQL8.0的主从配置过程记录
    解决 /dev/mapper/centos-root 空间不足的问题
    ASP判断当前页面上是否有参数ID传递过来
    通过ASP禁止指定IP和只允许指定IP访问网站的代码
    asp自动补全html标签自动闭合(正则表达式)
    asp中utf8不会出现乱码的写法
    通过安全字符串过滤非法字符
  • 原文地址:https://www.cnblogs.com/devilyouwei/p/6901264.html
Copyright © 2011-2022 走看看