首页 > 代码库 > Kotlin编程开发Android运用程序(Volley+Gson依赖库)

Kotlin编程开发Android运用程序(Volley+Gson依赖库)

技术分享

在2017年Google IO大会中,宣布Kotlin 作为官方语言。跟着党走总没错的想法,开始满怀激情的开始Kotlin之旅。

历经一个下午的探索Kotlin编程后。昨晚按耐不住激动心情,边摸石头边过河的方式,花了一个晚上时间,撸了本项目代码。过完一段时间的后,Kotlin理解提高了,再回头看下本项目,肯定是左右嫌弃的,但是这毕竟是本人的第一个Kotlin项目。

1. AndroidStudio支持Kotlin的配置

若是使用的AndroidStudio3.0以下,是默认不支持Kotlin语言的,需要自行配置。

Androistudio 3.0以上是自带支持Kotlin。

具体配置与使用,可以参考上篇讲解的文章, Kotlin编程之AndroidStudio(包括3.0与2.x版本)配置与使用。

2. 在Gralde中添加依赖库

注意点:这里展示的项目已经支持Kotlin编写,在Project的Gralde 和Moudle的Gralde已经有Kotlin配置。

dependencies {    compile fileTree(include: [‘*.jar‘], dir: ‘libs‘)    androidTestCompile(‘com.android.support.test.espresso:espresso-core:2.2.2‘, {        exclude group: ‘com.android.support‘, module: ‘support-annotations‘    })    compile ‘com.android.support:appcompat-v7:25.3.1‘    compile ‘com.android.support.constraint:constraint-layout:1.0.2‘    compile ‘com.android.support:recyclerview-v7:25.3.1‘    testCompile ‘junit:junit:4.12‘    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"    //官方框架    compile ‘com.android.volley:volley:1.0.0‘    compile ‘com.google.code.gson:gson:2.8.0‘}

3. 根据需求开始用Kotlin编写

使用AndroidStudio怎么创建Kotlin文件或者项目,请阅读 Kotlin编程之AndroidStudio使用。

项目需求

  • 在豆豌API中搜索导演的电影
  • 在里列表中加载电影

实现

  • Kotlin语言编写
  • Volley+Gson官方框架异步加载和解析
  • RecyclerView中显示电影

根据分析,开始编写代码

1. 编写Volley的单例操作类

  • object关键字声明单例类

  • lateinit关键字声明声明一个非空变量,好处不需要设置初始值。

  • lazy() 延迟属性,同步的产生一个单个对象。

  • Val关键字声明一个只读对象,Var关键字声明一个可写对象。

  • 注意点: 创建对象不需要使用Java中new关键字。

/*** *  object 用于单例模式 * *  object声明对象名后,通过对象名来访问,但是不能使用 = 右边赋值。 * */object  VolleySingletion{    /**     * lateinit 声明一个非空变量,且不需要设置初始值。     */    private lateinit  var context:Context    /**     *  这里使用 延迟属性(lazy properties):首次访问时计算结果,以后再次访问时候,将拷贝第一次记录的结果。     *     *     *   使用形式: var p: String by lazy {   }     *     *   lazy()返回一个lazy<T> 的 T 对象.     *     *   注意点: lazy属性的计算结果的过程是同步锁的(synchronized)。     *     *   作用: 单例对象     */    val  requestQueque :RequestQueue by lazy {        Volley.newRequestQueue(context)    }    val  imageLoader :ImageLoader by lazy {        // 不需要调用  new  关键字才创建对象        ImageLoader(requestQueque,LruBtimapCache() )    }    fun  initConfi(context:Context){        this.context =context.applicationContext    }}

2. 编写ImageLoader需要用到的LruCache

  • 一个类继承父类和实现接口的方式 : class 类名 :超类名(),接口名

  • LruBitmapCache主构造函数中,指定参数类型为Int,同时也指定一个默认值。

  • LruBitmapCache带有主构造函数,因此超类(这里是LruCache)必须在主构造函数中初始化。

  • Companion关键字,修饰伴生对象。伴生对象类名,可以省略
/*** *   LruBitmapCache主构造函数中,指定一个默认值。 * *   LruBitmapCache带有主构造函数,因此超类(这里是LruCache)必须在主构造函数中初始化。 * */class LruBtimapCache (size: Int= defaultSize ): LruCache<String,Bitmap>(size) ,ImageLoader.ImageCache{    override fun getBitmap(url: String): Bitmap ?{        return get(url)    }    override fun putBitmap(url: String?, bitmap: Bitmap?) {        put(url,bitmap)    }    override fun sizeOf(key: String, value: Bitmap): Int {        return   value.rowBytes*value.height/1024    }    /**     * 使用Companion关键字,伴生对象类名,可以省略。     */    companion object{        /**         *  val声明一个只读的变量         *         */         val  defaultSize: Int get() {             val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()             val cacheSize = maxMemory / 8             return cacheSize         }    }}

3. 编写自定义的Application:

  • override关键字用于复写超类或者接口中存在的方法
class BaseApplication :Application(){    override fun onCreate() {        super.onCreate()        //初始化Volley配置        VolleySingletion.initConfi(this)    }}

在AndroidManifest.xml中配置:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.xingen.kotlindemo">   //联网权限    <uses-permission android:name="android.permission.INTERNET"/>    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:name=".BaseApplication"  //使用自定义的Application        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>    </application></manifest>

4. 编写实体类

这里也存在一个点疑问:data关键字修饰数据对象,但是必须带有主构造器函数。
因此,这里并未使用data修饰数据对象。

  • 类型后面带有?是允许该对象为空
//https://api.douban.com/v2/movie/search?q=张艺谋,返回的数据结构。class MovieList {    lateinit var subjects: List<Movie>    class Movie {       lateinit var year: String        var title: String? = null        var id: String? = null        lateinit  var images: Images        class Images {            var small: String?= null            var large: String? = null        }    }}

5. 编写RecyclerView的Adapter

  • this引用是最内层的对象,若是引用外层对象,需要使用this@类名

  • 除开下文的初始化全局变量方式外,还可以在主构造函数中声明类型的方式

class ImageListAdapter(movieList: List<MovieList.Movie>) : RecyclerView.Adapter<ImageListAdapter.ViewHolder>() {    /**     * 指定一个全局的变量,从主构造函数中获取到参数,进行初始化     */    var list = movieList    /**     * 加载的数量     */    override fun getItemCount(): Int {        return list.size    }    /**     * 创建 ViewHolder     */    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {        var rootView = View.inflate(parent.context, R.layout.item_imagelist_layout, null)        return ViewHolder(rootView)    }    /**     *  绑定ViewHolder,进行加载数据     */    override fun onBindViewHolder(holder: ViewHolder, position: Int) {        holder.loadImage(position)    }    /**     *  inner修饰内部类     */    inner class ViewHolder(var rootView: View) : RecyclerView.ViewHolder(rootView) {        /**         * 构建一个加载数据的方法,参数为RecyclerView中的当前的位置         */        fun loadImage(position: Int) {            var iv = rootView.findViewById(R.id.imagelist_iv) as NetworkImageView            var title = rootView.findViewById(R.id.imagelist_title) as TextView            /**             *  this@类名的方式,拿到需要对应类的this指向。             */            var  adapter=this@ImageListAdapter            /**             *   NetWorkImageView开始加载图片             */            iv.setDefaultImageResId(R.mipmap.ic_launcher)            iv.setErrorImageResId(R.mipmap.ic_launcher)            iv.setImageUrl(adapter.list[position].images.large,VolleySingletion.imageLoader)            title.text=adapter.list[position].title        }    }}

6. 编写MainActivity,进行发送请求和更新数据

/** *  一个类继承父类和实现接口的方式; class 类名 :超类名(),接口名 */class MainActivity : AppCompatActivity() {    /**     * override用于覆写继承父类或者实现接口中方法。     *     * fun 用于标识方法     *     * 参数形式: 参数名: 类型     *     *  ? 是用于指定可以为空对象     *     */    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        shwoDiaglog()        this.initView()        this.sendRequest()    }    lateinit var recyclerView: RecyclerView    /**     * 初始化控件     */    fun initView() {        recyclerView = this.findViewById(R.id.main_recycler_view) as RecyclerView    }    /**     *  将网络数据加载到RecyclerView     */    fun loadData(movieList: List<MovieList.Movie>) {        recyclerView.layoutManager = LinearLayoutManager(this)        recyclerView.adapter = ImageListAdapter(movieList)    }   lateinit var progressDialog: ProgressDialog    /**     * 显示dialog     */    fun shwoDiaglog() {        progressDialog = ProgressDialog(this)        progressDialog.show()    }    /**     * 取消dialog     */    fun cancleDialog() {        progressDialog.dismiss()    }    /**     * Toast显示     */    fun loadToast(content: String?) {        Toast.makeText(this, content, Toast.LENGTH_SHORT).show()    }    /**     * 发送请求,这里使用douban公开的搜索电影的API     */    fun sendRequest() {        var url = "https://api.douban.com/v2/movie/search?q=张艺谋"        val request = StringRequest(url, Response.Listener<String> {            response ->             //请求成功,Gson解析json            var movilist = Gson().fromJson(response, MovieList::class.java)            loadData(movilist.subjects)            cancleDialog()        }, Response.ErrorListener {            error ->             loadToast(error.message)            cancleDialog()        })        // 单利类中对象的引用        VolleySingletion.requestQueque.add(request)    }}

activity_main.xml代码如下:

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.xingen.kotlindemo.MainActivity">  <android.support.v7.widget.RecyclerView      android:id="@+id/main_recycler_view"      android:layout_width="match_parent"      android:layout_height="match_parent">  </android.support.v7.widget.RecyclerView></android.support.constraint.ConstraintLayout>

item_imagelist_layout.xml代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal" android:layout_width="match_parent"    android:layout_height="wrap_content"    android:padding="10dp">    <com.android.volley.toolbox.NetworkImageView        android:layout_width="100dp"        android:layout_height="100dp"        android:scaleType="centerCrop"        android:id="@+id/imagelist_iv"/>    <TextView        android:layout_width="wrap_content"        android:id="@+id/imagelist_title"        android:layout_gravity="center_vertical"        android:layout_marginLeft="20dp"        android:layout_height="wrap_content" /></LinearLayout>

项目的最终目录结构,如下:

技术分享

项目运行结果

技术分享

项目代码:https://github.com/13767004362/KotlinVolleyDemo

资源参考:

  • AndroidStudio的配置:http://blog.csdn.net/hexingen/article/details/72621795

  • Kotlin中英文比对的官网:https://www.kotlincn.net/

  • Kotlin中文:https://huanglizhuo.gitbooks.io/kotlin-in-chinese/content/

  • Kotlin中文翻译组:https://github.com/huanglizhuo/kotlin-in-chinese

Kotlin编程开发Android运用程序(Volley+Gson依赖库)