? 最近在學(xué)習(xí)mvvm,利用空閑時間寫了一個簡單版的wanAndroid項目.介紹就不說了欲虚,直接上代碼.
? 1.引入:在App——build.gradle目錄引入以下代碼即可:
dataBinding{
? ? enabled true
}
?
2.配置gradle.properties
android.useAndroidX=true
android.enableJetifier=true
3.WanAndroid項目主要有首頁、知識幢竹、導(dǎo)航莫换、項目四個模塊,采用MvvM+Kotlin方式
主MainActivity代碼如下:
/**
* @作者: njb
* @時間: 2020/1/13 12:51
* @描述: 主界面
*/
open class MainActivity : BaseActivity() {
? ? private val fragmentList: MutableList<Fragment> =
? ? ? ? ArrayList()
? ? private val strings =
? ? ? ? arrayOf("首頁", "知識", "導(dǎo)航", "項目")
? ? 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<BannerModel>? = 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)
? ? ? ? //設(shè)置banner樣式
? ? ? ? banner!!.setBannerStyle(BannerConfig.CIRCLE_INDICATOR)
? ? ? ? //設(shè)置圖片加載器
? ? ? ? banner!!.setImageLoader(GlideImageLoader())
? ? ? ? //設(shè)置banner動畫效果
? ? ? ? banner!!.setBannerAnimation(Transformer.Default)
? ? ? ? //設(shè)置自動輪播响蕴,默認(rèn)為true
? ? ? ? banner!!.isAutoPlay(true)
? ? ? ? //設(shè)置輪播時間
? ? ? ? banner!!.setDelayTime(3000)
? ? ? ? //設(shè)置指示器位置(當(dāng)banner模式中有指示器時)
? ? ? ? banner!!.setIndicatorGravity(BannerConfig.CENTER)
? ? ? ? //banner設(shè)置方法全部調(diào)用完畢時最后調(diào)用
? ? ? ? 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<BaseModel<ArticleListModel>>? = null
? ? private var repository = HomeRepository()
? ? fun getArtCircleList(page:Int) {
? ? ? ? observer = object : Observer<BaseModel<ArticleListModel>> {
? ? ? ? ? ? override fun onComplete() {
? ? ? ? ? ? ? ? completedListener.onCompleted()
? ? ? ? ? ? }
? ? ? ? ? ? override fun onSubscribe(d: Disposable) {
? ? ? ? ? ? }
? ? ? ? ? ? override fun onNext(t: BaseModel<ArticleListModel>) {
? ? ? ? ? ? ? ? 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<BaseModel<ArticleListModel>>
? ? ? ? )
? ? }
}
首頁請求HomeRespository
/**
*@作者: njb
*@時間: 2020/1/10 18:00
*@描述:
*/
class HomeRepository :BaseRepository(){
? ? fun getArcticList(
? ? ? ? page: Int,
? ? ? ? observer: Observer<BaseModel<ArticleListModel>>) {
? ? ? ? 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<ArticleListModel.DatasBean>?):
? ? BaseQuickAdapter<ArticleListModel.DatasBean,BaseViewHolder>(R.layout.item_home,data){
? ? override fun convert(helper: BaseViewHolder, item: ArticleListModel.DatasBean) {
? ? ? ? val itemBinding = DataBindingUtil.bind<ItemHomeBinding>(helper.itemView)!!
? ? ? ? itemBinding.homebean = item
? ? ? ? itemBinding.executePendingBindings()
? ? }
}
首頁布局:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
? ? <LinearLayout
? ? ? ? android:layout_width="match_parent"
? ? ? ? android:layout_height="match_parent"
? ? ? ? android:orientation="vertical">
? ? ? ? <com.ning.mvvmplayandroid.weight.MyRefreshLayout
? ? ? ? ? ? android:id="@+id/rf_home"
? ? ? ? ? ? android:layout_width="match_parent"
? ? ? ? ? ? android:layout_height="match_parent">
? ? ? ? ? ? <androidx.recyclerview.widget.RecyclerView
? ? ? ? ? ? ? ? android:id="@+id/rv_home"
? ? ? ? ? ? ? ? android:layout_width="match_parent"
? ? ? ? ? ? ? ? android:layout_height="match_parent" />
? ? ? ? </com.ning.mvvmplayandroid.weight.MyRefreshLayout>
? ? </LinearLayout>
</layout>
首頁Banner布局加在文章列表頭部谆焊,Banner廣告布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto"
? ? android:layout_width="match_parent"
? ? android:layout_height="match_parent">
? ? <com.youth.banner.Banner
? ? ? ? android:id="@+id/home_banner"
? ? ? ? android:layout_width="match_parent"
? ? ? ? android:layout_height="200dp"
? ? ? ? app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
首頁文章列表item布局:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
? ? xmlns:android="http://schemas.android.com/apk/res/android"
? ? xmlns:app="http://schemas.android.com/apk/res-auto">
? ? <data>
? ? ? ? <variable
? ? ? ? ? ? name="homebean"
? ? ? ? ? ? type="fule.com.playandroidkotlin.ui.model.ArticleListModel.DatasBean" />
? ? </data>
? ? <androidx.constraintlayout.widget.ConstraintLayout
? ? ? ? android:layout_width="match_parent"
? ? ? ? android:layout_height="wrap_content"
? ? ? ? android:padding="10dp"
? ? ? ? tools:ignore="MissingConstraints">
? ? ? ? <TextView
? ? ? ? ? ? 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" />
? ? ? ? <TextView
? ? ? ? ? ? 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" />
? ? ? ? <androidx.cardview.widget.CardView
? ? ? ? ? ? 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">
? ? ? ? ? ? <ImageView
? ? ? ? ? ? ? ? android:id="@+id/iv_content"
? ? ? ? ? ? ? ? android:layout_width="100dp"
? ? ? ? ? ? ? ? android:layout_height="80dp"
? ? ? ? ? ? ? ? android:scaleType="centerCrop"
? ? ? ? ? ? ? ? app:home_image="@{homebean.envelopePic}"/>
? ? ? ? </androidx.cardview.widget.CardView>
? ? ? ? <TextView
? ? ? ? ? ? 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" />
? ? ? ? <TextView
? ? ? ? ? ? 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" />
? ? ? ? <ImageView
? ? ? ? ? ? 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" />
? ? </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
?從下圖可以看到數(shù)據(jù)是和視圖綁定到一起的,數(shù)據(jù)發(fā)生變化浦夷,視圖也會改變.其他三個模塊的代碼都是和首頁一樣辖试,創(chuàng)建view,綁定視圖劈狐,請求數(shù)據(jù)罐孝,數(shù)據(jù)請求成功后直接顯示.這里就不寫重復(fù)贅述了。
?
5.遇到的問題:
(1)databinding綁定圖片和一般的用法不一樣肥缔,一般的直接設(shè)置背景圖片就可以莲兢,拿到數(shù)據(jù)后直接用glide顯示,這里是和視圖綁定的,所以我們得新建一個圖片顯示的工具類本文暫定ImageUtil,方法必須和布局中的名稱一致续膳,否則會報錯.
我們在ImageUitl中新建一個顯示首頁文章列表圖片的方法:
?
??????
@BindingAdapter("image_view")
public static void setImageView(ImageView imageView, String urlString) {
? ? Glide.with(imageView.getContext()).load(urlString).into(imageView);
}
?
(2)當(dāng)實體類有一個list我們好處理改艇,但是如果又嵌套一個的話比較麻煩,本博主也是參考網(wǎng)上的方法坟岔,找了很多資料才找到這個辦法谒兄,剛開始一直獲取到的第一個數(shù)據(jù).
我們這里就以知識模塊為例.返回的數(shù)據(jù)是一個知識體系列表,然后這個體系列表里面又有一個體系文章標(biāo)題列表
?<data>
? ? <variable
? ? ? ? name="knowledge"
? ? ? ? type="fule.com.playandroidkotlin.ui.model.KnowledgeModel.DataBean" />
? ? <!導(dǎo)入一個list!>
? ? <import type="java.util.List" />
? ? <variable
? ? ? ? name="knowList"
? ? ? ? type="List<fule.com.playandroidkotlin.ui.model.ChildrenModel>" />
? ? <!表示list的位置!>
? ? <variable
? ? ? ? name="index"
? ? ? ? type="int" />
</data>
下圖中一個表示知識體系列表社付,一個表示體系列表下的欄目列表?
?
(3)剛開始在fragment布局中l(wèi)ayout設(shè)置了寬和高會一直報錯
圖(1)是錯誤的寫法承疲,會一直報錯,這是我作為一個初學(xué)者啥也不懂踩的坑
?
圖(2)才是正確的寫法
?
6.實現(xiàn)的效果圖如下:
?
7.最后鸥咖,項目的完整源碼地址:https://gitee.com/jackning_admin/MvvmPlayAndroid
當(dāng)然纪隙,這個小demo還有很多問題需要處理,比如登錄注冊功能扛或,點贊收藏等等绵咱,后面有時間會慢慢補(bǔ)上,歡迎各位大佬前來討論,指出小弟的問題悲伶,如有問題艾恼,及時提示,我會及時改正.