Android使用MvvM+kotlin实现简单WanAndroid

  最近在学习mvvm,利用空闲时间写了一个简单版的wanAndroid项目.介绍就不说了,直接上代码.

  1.引入:在App——build.gradle目录引入以下代码即可:

dataBinding{

    enabled true

}

2.配置gradle.properties

android.useAndroidX=true

android.enableJetifier=true

3.WanAndroid项目主要有首页、知识、导航、项目四个模块,采用MvvM+Kotlin方式

主MainActivity代码如下:

/**

* @作者: njb

* @时间: 2020/1/13 12:51

* @描述: 主界面

*/

open class MainActivity : BaseActivity() {

    private val fragmentList: MutableList =

        ArrayList()

    private val strings =

        arrayOf("首页", "知识", "导航", "项目")

    override val layoutId: Int

        get() = R.layout.activity_main

    override fun initView() {

        fragmentList.add(HomeFragment())

        fragmentList.add(KnowledgeFragment())

        fragmentList.add(NavigationFragment())

        fragmentList.add(ProjectFragment())

        viewPager!!.adapter = object : FragmentStateAdapter(this) {

            override fun createFragment(position: Int): Fragment {

                return fragmentList[position]

            }

            override fun getItemCount(): Int {

                return fragmentList.size

            }

        }

        viewPager.offscreenPageLimit = 3

        val tabLayoutMediator = TabLayoutMediator(

            tab_layout,

            viewPager,

            TabConfigurationStrategy { tab: TabLayout.Tab, position: Int ->

                tab.text = strings[position]

            }

        )

        tabLayoutMediator.attach()

        initToolBar()

    }

    private fun initToolBar() {

        setSupportActionBar(toolbar)

        supportActionBar!!.setDisplayHomeAsUpEnabled(false)

        supportActionBar!!.setHomeButtonEnabled(false)

        iv_search.setImageResource(R.drawable.ic_baseline_search_24)

    }

    override fun addListener() {

        iv_search.setOnClickListener(View.OnClickListener {

            startActivity(

                Intent(

                    this@MainActivity,

                    SearchActivity::class.java

                )

            )

        })

    }

}

4.首页Fragment代码:

/**

* @作者: njb

* @时间: 2020/1/10 18:19

* @描述: 首页

*/

class HomeFragment : Fragment(), CompletedListener {

    private var mBinding: ViewDataBinding? = null

    private var artListAdapter: ArtListAdapter? = null

    private var viewModel: HomeViewModel? = null

    private var layoutManager: LinearLayoutManager? = null

    private var page: Int = 1

    private var viewHead: View? = null

    private var banner: Banner? = null

    private var bannerModel: List? = null

    private var viewModels:BannerViewModel ? = null

    override fun onCreateView(

        inflater: LayoutInflater,

        container: ViewGroup?,

        savedInstanceState: Bundle?

    ): View? {

        mBinding = DataBindingUtil.inflate(inflater, R.layout.fm_home, container, false)

        val view = mBinding!!.root

        viewHead = LayoutInflater.from(context).inflate(R.layout.item_home_banner, null)

        initBanner()

        initView(view)

        initListener(view)

        return mBinding!!.root

    }

    /**

    * 初始化view

    */

    private fun initView(view: View) {

        artListAdapter = ArtListAdapter(null)

        layoutManager = LinearLayoutManager(activity!!)

        layoutManager!!.orientation = LinearLayoutManager.VERTICAL

        view.rv_home.layoutManager = layoutManager

        artListAdapter!!.addHeaderView(viewHead)

        view.rv_home.addItemDecoration(DividerDecoration(ContextCompat.getColor(activity!!,R.color.ce7e7e7),2))

        view.rv_home.adapter = artListAdapter

        viewModel = HomeViewModel(artListAdapter!!, this)

        //显示加载动画

        viewModel!!.getArtCircleList(page)

    }

    private fun initBanner() {

        banner = viewHead!!.findViewById(R.id.home_banner)

        //设置banner样式

        banner!!.setBannerStyle(BannerConfig.CIRCLE_INDICATOR)

        //设置图片加载器

        banner!!.setImageLoader(GlideImageLoader())

        //设置banner动画效果

        banner!!.setBannerAnimation(Transformer.Default)

        //设置自动轮播,默认为true

        banner!!.isAutoPlay(true)

        //设置轮播时间

        banner!!.setDelayTime(3000)

        //设置指示器位置(当banner模式中有指示器时)

        banner!!.setIndicatorGravity(BannerConfig.CENTER)

        //banner设置方法全部调用完毕时最后调用

        banner!!.start()

        viewModels = BannerViewModel(banner!!,this)

        viewModels!!.getBannerList()

    }

    /**

    * 初始化事件

    */

    private fun initListener(view: View) {

        artListAdapter!!.setOnItemClickListener{

            listener,View,position ->

            Intent(context, WebViewActivity::class.java).run {

                putExtra(AppConstant.WEBVIEW_TITLE_KEY, artListAdapter!!.data[position].title)

                putExtra(AppConstant.WEBVIEW_ID_KEY, artListAdapter!!.data[position].id)

                putExtra(AppConstant.WEBVIEW_URL_KEY, artListAdapter!!.data[position].link)

                startActivity(this, null)

            }

        }

        view.rf_home.setOnRefreshListener {

            page = 1

            viewModel!!.getArtCircleList(page)

            view.rf_home.finishRefresh(200)

        }

        view.rf_home.setOnLoadMoreListener {

            page++

            viewModel!!.getArtCircleList(page)

            view.rf_home.finishLoadMore(200)

        }

    }

    override fun onCompleted() {

    }

}

HomeViewModel

/**

*@作者: njb

*@时间: 2020/1/10 17:55

*@描述:

*/

class HomeViewModel(

    private val adapter: ArtListAdapter,

    private val completedListener: CompletedListener

) {

    private var observer: Observer>? = null

    private var repository = HomeRepository()

    fun getArtCircleList(page:Int) {

        observer = object : Observer> {

            override fun onComplete() {

                completedListener.onCompleted()

            }

            override fun onSubscribe(d: Disposable) {

            }

            override fun onNext(t: BaseModel) {

                if (t.errorCode == 0) {

                    t.data?.let {

                        if(page == 1){

                            adapter.setNewData(it.datas)

                        }else{

                            adapter.addData(it.datas!!)

                        }

                    }

                }

            }

            override fun onError(e: Throwable) {

                completedListener.onCompleted()

            }

        }

        repository.getArcticList(page,

            observer as Observer>

        )

    }

}

首页请求HomeRespository

/**

*@作者: njb

*@时间: 2020/1/10 18:00

*@描述:

*/

class HomeRepository :BaseRepository(){

    fun getArcticList(

        page: Int,

        observer: Observer>) {

        apiServer.articleList(page).subscribeOn(Schedulers.io())

            .observeOn(AndroidSchedulers.mainThread())

            .subscribe(observer)

    }

}

首页文章列表适配器

/**

*@author: njb

*@date:  2020/2/11 0011 16:47

*@desc:  文章列表适配器

*/

class ArtListAdapter (data:List?):

    BaseQuickAdapter(R.layout.item_home,data){

    override fun convert(helper: BaseViewHolder, item: ArticleListModel.DatasBean) {

        val itemBinding = DataBindingUtil.bind(helper.itemView)!!

        itemBinding.homebean = item

        itemBinding.executePendingBindings()

    }

}

首页布局:

   

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:orientation="vertical">

       

            android:id="@+id/rf_home"

            android:layout_width="match_parent"

            android:layout_height="match_parent">

           

                android:id="@+id/rv_home"

                android:layout_width="match_parent"

                android:layout_height="match_parent" />

       

   

首页Banner布局加在文章列表头部,Banner广告布局:

    xmlns:app="http://schemas.android.com/apk/res-auto"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

   

        android:id="@+id/home_banner"

        android:layout_width="match_parent"

        android:layout_height="200dp"

        app:layout_constraintTop_toTopOf="parent" />

首页文章列表item布局:

    xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:app="http://schemas.android.com/apk/res-auto">

   

       

            name="homebean"

            type="fule.com.playandroidkotlin.ui.model.ArticleListModel.DatasBean" />

   

   

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:padding="10dp"

        tools:ignore="MissingConstraints">

       

            android:id="@+id/tv_username"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            app:layout_constraintLeft_toLeftOf="parent"

            android:layout_marginRight="20dp"

            android:text="@{homebean.author}"

            android:textColor="@color/colorPrimary"

            android:textSize="12sp" />

       

            android:id="@+id/tv_time"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            app:layout_constraintEnd_toEndOf="parent"

            android:text="@{String.valueOf(homebean.niceDate)}"

            android:textColor="@color/colorPrimary"

            android:textSize="12sp" />

       

            android:id="@+id/cv_content"

            android:layout_width="100dp"

            android:layout_height="80dp"

            app:layout_constraintTop_toBottomOf="@+id/tv_username"

            android:layout_marginTop="10dp"

            android:elevation="0dp"

            app:cardCornerRadius="4dp"

            app:cardElevation="0dp">

           

                android:id="@+id/iv_content"

                android:layout_width="100dp"

                android:layout_height="80dp"

                android:scaleType="centerCrop"

                app:home_image="@{homebean.envelopePic}"/>

       

       

            android:id="@+id/tv_content"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_marginStart="12dp"

            android:layout_marginTop="10dp"

            android:ellipsize="end"

            android:gravity="top|start"

            android:lineSpacingExtra="2dp"

            android:maxLines="2"

            android:text="@{homebean.title}"

            android:textColor="@color/black"

            android:textSize="14sp"

            app:layout_constraintLeft_toRightOf="@+id/cv_content"

            app:layout_constraintRight_toRightOf="parent"

            app:layout_constraintTop_toBottomOf="@+id/tv_username" />

       

            android:id="@+id/tv_type"

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:layout_marginStart="10dp"

            android:layout_marginTop="10dp"

            android:layout_toRightOf="@+id/cv_content"

            android:gravity="center"

            android:padding="4dp"

            android:text="@{homebean.chapterName}"

            android:background="@drawable/shape_article_type_bg"

            android:textColor="@color/white"

            android:textSize="12sp"

            app:layout_constraintLeft_toRightOf="@+id/cv_content"

            app:layout_constraintTop_toBottomOf="@+id/tv_content" />

       

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            app:layout_constraintTop_toBottomOf="@+id/tv_content"

            app:layout_constraintEnd_toEndOf="parent"

            android:layout_marginTop="10dp"

            app:srcCompat="@drawable/ic_favorite_black_24dp" />

   

 从下图可以看到数据是和视图绑定到一起的,数据发生变化,视图也会改变.其他三个模块的代码都是和首页一样,创建view,绑定视图,请求数据,数据请求成功后直接显示.这里就不写重复赘述了。

5.遇到的问题:

(1)databinding绑定图片和一般的用法不一样,一般的直接设置背景图片就可以,拿到数据后直接用glide显示,这里是和视图绑定的,所以我们得新建一个图片显示的工具类本文暂定ImageUtil,方法必须和布局中的名称一致,否则会报错.

我们在ImageUitl中新建一个显示首页文章列表图片的方法:

​​​​​​

@BindingAdapter("image_view")

public static void setImageView(ImageView imageView, String urlString) {

    Glide.with(imageView.getContext()).load(urlString).into(imageView);

}


(2)当实体类有一个list我们好处理,但是如果又嵌套一个的话比较麻烦,本博主也是参考网上的方法,找了很多资料才找到这个办法,刚开始一直获取到的第一个数据.

我们这里就以知识模块为例.返回的数据是一个知识体系列表,然后这个体系列表里面又有一个体系文章标题列表

   

        name="knowledge"

        type="fule.com.playandroidkotlin.ui.model.KnowledgeModel.DataBean" />

   

   

   

        name="knowList"

        type="List<fule.com.playandroidkotlin.ui.model.ChildrenModel>" />

   

   

        name="index"

        type="int" />

下图中一个表示知识体系列表,一个表示体系列表下的栏目列表 

(3)刚开始在fragment布局中layout设置了宽和高会一直报错

图(1)是错误的写法,会一直报错,这是我作为一个初学者啥也不懂踩的坑

图(2)才是正确的写法

6.实现的效果图如下:

7.最后,项目的完整源码地址:https://gitee.com/jackning_admin/MvvmPlayAndroid

当然,这个小demo还有很多问题需要处理,比如登录注册功能,点赞收藏等等,后面有时间会慢慢补上,欢迎各位大佬前来讨论,指出小弟的问题,如有问题,及时提示,我会及时改正.

你可能感兴趣的:(Android使用MvvM+kotlin实现简单WanAndroid)