为了账号安全,请及时绑定邮箱和手机立即绑定

Kotlin项目Android MVP项目(RxJava、Rerotfit、OkHttp、Glide)

标签:
Android

Kotlin编写MVP案例


使用Kotlin编程实现一个MVP案例,实现电影列表功能:

5b76dab90001aa4204030691.jpg

前期准备

项目中,在Gradle中引入框架的配置如下和使用Kotlin Android扩展:

apply plugin: 'com.android.application'apply plugin: 'kotlin-android'apply plugin: 'kotlin-android-extensions'//扩展插件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:design:25.3.1'
    testCompile 'junit:junit:4.12'
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    //Glide v4
    compile 'com.github.bumptech.glide:glide:4.0.0-RC0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0-RC0'
    //Retrofit 2.x
    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
    //OkHttp 3.x
    compile 'com.squareup.okhttp3:okhttp:3.8.0'
    compile 'com.squareup.okhttp3:logging-interceptor:3.8.0'
    //RxJava 1.x
    compile 'io.reactivex:rxjava:1.3.0'
    compile 'io.reactivex:rxandroid:1.2.1'}
须注意点:本篇中无findViewById获取控件对象,采用Kotlin Android Extensions方式获取控件对象

AndroidManifest.xml中添加权限:

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

除之外,还有各种框架的混淆规则,这里省略不贴。

开始编写代码

1. 项目通用的BasePrester和BaseView接口:

项目中通用的BasePresenter接口,具备订阅和取消订阅的行为:

interface  BasePresenter{    /**
     * 订阅
     */
    fun subscribe()    /**
     * 取消订阅
     */
     fun unsubscribe()
}

项目中通用的BaseView接口,具备绑定Presenter的行为:

interface BaseView<T>{    /**
     * 设置Presenter的方法
     */
    fun  setPresenter(presenter: T)
}

2. 开始编写Model中远程数据源

采用Retrofit作为网络异步框架,OkHttp作为传输层。

这里,采用搜索张艺谋的电影,在douban的API中:https://api.douban.com/v2/movie/search?q=张艺谋

Retrofit的请求和响应结果的配置:请求中发送的Body和Header,Respose的接口 :需要添加retrofit:adapter-rxjava库,实现适配器功能

interface DouBanService {    /**
     *     这里返回一个Observable,用于RxJava结合使用
     */
    @GET("{path}")
    fun movieList(@Path("path") path:String, @QueryMap options: Map<String,String> ):Observable<MovieList>
}

Retrofit的操作类:添加OkHttp作为传输层,RxJava适配器,Gson解析的转换器。

object RemoteDataSource{
    val baseURL="https://api.douban.com/v2/movie/"
    val retrofit:Retrofit by lazy {
        Retrofit.Builder().baseUrl(baseURL)                .client(OkHttpProvider.createOkHttpClient())                .addConverterFactory(GsonConverterFactory.create())                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())                .build()
    }
    var douBanService= retrofit.create(DouBanService::class.java)

    fun movieList(subscriber:Subscriber<List<MovieList.Movie>>):Subscription{
        val url="search"
        var map= hashMapOf("q" to "张艺谋" )
         var result= douBanService.movieList(url,map).flatMap {
            item: MovieList ->Observable.just(item.subjects)
        }.subscribeOn(Schedulers.io())        .unsubscribeOn(Schedulers.io()) 
        .observeOn(AndroidSchedulers.mainThread()) 
        .subscribe(subscriber)

        return result
    }
}

OkHttp的配置:需 引入OkHttp库和OkHttp:logging-interceptor库

internal class OkHttpProvider{
    companion object{        /**
         * 自定义配置OkHttpClient
         */
        fun createOkHttpClient():OkHttpClient{           var  builder=OkHttpClient.Builder()            var  loggingInterceptor=HttpLoggingInterceptor()
            loggingInterceptor.level=HttpLoggingInterceptor.Level.BODY
            builder.addInterceptor(loggingInterceptor)            return builder.build()
        }
    }
}

编写返回的实体类

data  class MovieList(var subjects:List<Movie>){    data class Movie(var year:String,var title:String,var  images: Images){        data class Images(var small:String,var large:String)
    }
}


3. 根据模块业务编写View和Presenter及它的实现类

业务上比较简单,搜索张艺谋的电影,显示在列表上。根据这个分析,写出这个模块的View和Presenter.

interface MovieListConstract{
    interface Presenter:BasePresenter    /***
     * 抽出Presenter对View的响应行为,在View接口中定义
     */
    interface View :BaseView<Presenter>{
        fun showToast(msg:String)//Toast提示
        fun loadMovie(list: List<MovieList.Movie>)//加载电影数据
        fun showDialog()//显示dialog
        fun cancleDialog()//取消dialog
    }
}

View的实现类: 在Fragment、Activity、Adapter中怎么使用Kotlin Android Extensions

class MovieListFragment : Fragment(), MovieListConstract.View {

    lateinit var presenters: MovieListConstract.Presenter
    lateinit var dialog: ProgressDialog
    lateinit var rootView: View 

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        rootView = inflater.inflate(R.layout.fragment_movielist, container, false)
        return rootView
    }
    override fun onResume() {
        super.onResume()
        presenters.subscribe()
    }
    override fun onPause() {
        super.onPause()
        presenters.unsubscribe()
    }
    override fun setPresenter(presenter: MovieListConstract.Presenter) {
        this.presenters = presenter
    }
    override fun showToast(msg: String) {        Toast.makeText(activity.applicationContext, msg, Toast.LENGTH_SHORT).show()
    }
    /**
     * 加载数据
     */
    override fun loadMovie(list: List<MovieList.Movie>) {
        //无findViewById(),直接引用控件
        var recyclerView = rootView.movieList_recyclverView
        recyclerView.layoutManager = LinearLayoutManager(activity)
        recyclerView.adapter = MovieListAdapter(activity, list)
    }
    override fun showDialog() {
        dialog = ProgressDialog(activity)
        dialog.show()
    }
    override fun cancleDialog() {
        if (dialog != null && dialog.isShowing) {
            dialog.cancel()
        }
    }
    companion object {
        //静态对象
        val instance = MovieListFragment()
        //静态常量
        val Tag = MovieListFragment::class.java.javaClass.simpleName
    }
}

RecyclerView中Adapter

import kotlinx.android.synthetic.main.movielist_item.view.*/**
 * Created by ${新根} on 2017/6/8.
 * blog :http://blog.csdn.net/hexingen
 */internal class MovieListAdapter(var context: Context, var list: List<MovieList.Movie>) : RecyclerView.Adapter<MovieListAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = 
              ViewHolder(View.inflate(parent.context, R.layout.movielist_item, null)) 

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        GlideUtils.loadUrlImage(context,list[position].images.large,holder.imageView)
        holder.title_Tv.text=list[position].title
    } 

    override fun getItemCount() = list.size 

    internal class ViewHolder(rootView: View) : RecyclerView.ViewHolder(rootView) {        /**
         * 这里使用Kotlin Android 扩展,省略了findViewById().
         * 在最上面导入了import kotlinx.android.synthetic.main.movielist_item.view.*
         */
        var imageView = rootView.movielist_item_iv
        var title_Tv= rootView.movielist_item_tv
    }
}

Presenter的实现类

class MovieListPresenter(var view: MovieListConstract.View, var compositeSubscription: CompositeSubscription = defaultCompositeSubscription) : MovieListConstract.Presenter {
    init {//init初始化模块
        view.setPresenter(this)
    }    //开始订阅
    override fun subscribe() {
        view.showDialog()
        excuteTask()
    }    //执行任务
    fun excuteTask() {      var  disposable=RemoteDataSource.movieList(object :Subscriber<List<MovieList.Movie>>() {            override fun onNext(t: List<MovieList.Movie>) = view.loadMovie(t)            override fun onError(e: Throwable) {
                view.showToast(e.toString())
                view.cancleDialog()
            }          override fun onCompleted() {
              view.showToast("加载完成")
              view.cancleDialog()
          }
      })      this.compositeSubscription.add(disposable)
    }    //取消订阅,释放资源
    override fun unsubscribe() {
        view.cancleDialog()
        compositeSubscription.clear()
    }
    companion object {//采用伴随对象
        //默认的值
        val defaultCompositeSubscription get() = CompositeSubscription()
    }
}

Activity中创建View和Presenter:

class MovieListActvitiy : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_movielist) 

        //创建View实例
        var view=MovieListFragment.instance
        //创建Presenter实例        MovieListPresenter(view)
        //添加fragment
        supportFragmentManager.beginTransaction() 
               .add(R.id.movielist_content_layout,view,MovieListFragment.Tag).commit()
    }
}

最终项目结构目录如下:

5b76dab90001919603470554.jpg

4. 效果图如下

5b76dabb0001acb903120541.jpg

原文链接:http://www.apkbus.com/blog-847095-77589.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消