Android简单mvvm,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" />

name="index"

type="int" />

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

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

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

图(2)才是正确的写法

6.实现的效果图如下:

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

你可能感兴趣的:(Android简单mvvm)