

隨著Kotlin的推廣,一些國內公司的安卓專案開發,已經從Java完全切成Kotlin了,雖然Kotlin在各類編程語言中的排名比較靠后(據TIOBE發布了 19 年 8 月份的編程語言排行榜,Kotlin竟然排名45位),但是作為安卓開發者,掌握該語言,卻已是大勢所趨了,
Kotlin的基礎用法,整體還是比較簡單的,網上已經有很多文章了,大家熟悉下即可,
案例需求
此次案例,之所以選擇分頁串列,主要是因為該功能通用性強,涵蓋的技術點也較多,對開發者熟悉Kotlin幫助性較大,
案例的主要需求如下( 參考主流電商APP實作 ):
1、串列支持手勢滑動分頁查詢(滑動到底部時,自動查詢下一頁,直到沒有更多資料)
2、可切換串列樣式和網格樣式
3、切換樣式后,資料位置保持不變(如當前在第100條位置,切換樣式后,位置不變)
4、footview根據查詢狀態,顯示不同內容:
a、資料加載中... (正在查詢資料時顯示)
b、沒有更多資料了 (查詢成功,但是已沒有可回傳的資料了)
c、出錯了,點擊重試!!(查詢時出現例外,可能是網路,也可能是其他原因)
5、當查詢出錯時,再次點擊footview,可重新發起請求(例如:網路例外了)
6、當切換網格樣式時,footview應獨占一行
設計
雖然是簡單案例,咱們開發時,也應先進行簡單的設計,讓各模塊、各類都各司其職、邏輯解耦,這樣大家學起來會更簡單一些,
此處,不畫類圖了,直接根據專案結構,簡單介紹一下吧:

1、pagedata 是指資料模塊,包含:
DataInfo 物體類,定義商品欄位屬性
DataSearch 資料訪問類,模擬子執行緒異步查詢分頁資料,可將資料結果通過lambda回呼回去
2、pageMangage 是指分頁管理模塊,將分頁的全部邏輯托管給該模塊處理,為了簡化分頁邏輯的實作,根據功能單一性進行了簡單拆分:
PagesManager 分頁管理類,用于統籌串列資料查詢、展示、樣式切換
PagesLayoutManager 分頁布局管理類,用于串列樣式和網格樣式的管理、資料位置記錄
PagesDataManager 分頁資料管理類,用于分頁串列資料、footview資料的封裝
3、adapter 是指配接器模塊,主要用于定義各類配接器
PagesAdapter 分頁配接器類,用于創建、展示 itemview、footview,處理footview回呼事件
4、utils 是指工具模塊,用于定義一些常用工具
AppUtils 工具類,用于判斷網路連接情況
代碼實作
在文章的最后,會將Demo原始碼下載地址分享給大家,以供參考,
1、pagedata 資料模塊
1.1、DataInfo.kt 物體類
kotlin類中定義了屬性,則已包含了默認的get、set
package com.qxc.kotlinpages.pagedata
/**
* 物體類
*
* @author 齊行超
* @date 19.11.30
*/
class DataInfo {
/**
* 標題
*/
var title: String = ""
/**
* 描述
*/
var desc: String = ""
/**
* 圖片
*/
var imageUrl: String = ""
/**
* 價格
*/
var price: String = ""
/**
* 鏈接
*/
var link: String = ""
}
1.2、DataSearch 資料訪問類:
package com.qxc.kotlinpages.pagedata
import com.qxc.kotlinpages.utils.AppUtils
/**
* 資料查詢類:模擬網路請求,從服務器資料庫獲取資料
*
* @author 齊行超
* @date 19.11.30
*/
class DataSearch {
//服務器資料庫中的資料總數量(模擬)
private val totalNum = 25
//宣告回呼函式(Lambda運算式引數:errorCode錯誤碼,dataInfos資料,無回傳值)
private lateinit var listener: (errorCode: Int, dataInfos: ArrayList<DataInfo>) -> Unit
/**
* 設定資料請求監聽器
*
* @param plistener 資料請求監聽器的回呼類物件
*/
fun setDataRequestListener(plistener:
(errorCode: Int, dataInfos: ArrayList<DataInfo>) -> Unit) {
this.listener = plistener
}
/**
* 查詢方法(模擬從資料庫查詢)
* positionNum: 資料起始位置
* pageNum: 查詢數量(每頁查詢數量)
*/
fun search(positionNum: Int, pageNum: Int) {
//模擬網路異步請求(子執行緒中,進行異步請求)
Thread()
{
//模擬網路查詢耗時
Thread.sleep(1000)
//獲得資料查詢結束位置
var end: Int = if (positionNum + pageNum > totalNum) totalNum
else positionNum + pageNum
//創建集合
var datas = ArrayList<DataInfo>()
//判斷網路狀態
if (!AppUtils.instance.isConnectNetWork()) {
//回呼例外結果
this.listener(1,datas)
//回呼例外結果
//this.listener.invoke(-1, datas)
return@Thread
}
//組織資料(模擬獲取到資料)
for (index in positionNum..end - 1) {
var data = https://www.cnblogs.com/qixingchao/p/DataInfo()
data.title = "Android Kotlin ${index + 1}"
data.desc = "Kotlin 是一個用于現代多平臺應用的靜態編程語言,由 JetBrains ..."
data.price = (100 * (index + 1)).toString()
data.imageUrl = ""
data.link = "跳轉至Kotlin柜臺 -> JetBrains"
datas.add(data)
}
//回呼結果
this.listener.invoke(0, datas)
}.start()
}
}
DataSearch類有兩個重點知識:
1.2.1、子執行緒異步查詢的實作
a、參考常規分頁網路請求API,資料查詢方法應包含引數:起始位置、每頁數量
b、安卓中的網路查詢,需要在子執行緒中操作,當前案例直接使用Thread{}.start()實作子執行緒
執行緒實作的寫法與Java中不太一樣,為什么這么寫呢?咱們具體展開說明一下:
-----------------------------------Thread{}.start()-------------------------------------
通常情況下,Java中實作一個執行緒,可這么寫:
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
如果完全按照Java的寫法,將其轉為Kotlin,應該這么寫:
Thread(object: Runnable{
override fun run() {
}
}).start()
但是在本案例中卻是:Thread{}.start(),并未看到Runnable物件和run方法,其實是被Lambda運算式替換了:
>> Runnable介面有且僅有1個抽象函式run(),符合“函式式介面”定義(即:一個介面僅有一個抽象方法)
>> 這樣的介面可以使用Lambda運算式來簡化代碼的撰寫 :
使用Lambda表示Runnable介面實作,因run()無引數、無回傳值,對應的Lambda實作結構應該是:
{ -> }
當Lambda運算式無任何引數時,可以省略箭頭符號:
{ }
我們將Lambda運算式替換Runnable介面實作,Kotlin代碼如下所示:
Thread({ }) .start()
如果Lambda運算式是函式是最后一個實參,則可以放在小括號后面:
Thread() { } .start()
如果Lambda是函式的唯一實參的時候,小括號可以直接省略,也就變成了咱們案例的效果了:
Thread{ } .start()
-----------------------------------Thread{}.start()-------------------------------------
以上是執行緒Lambda運算式的簡化程序!!!
使用Lambda運算式,使得我們可以在 “Thread{ }” 的大括號里直接寫子執行緒實作,代碼變簡單了
更多Lambda運算式介紹,請參考文章:https://www.cnblogs.com/Jetictors/p/8647888.html
1.2.2、資料回呼監聽
此案例通過Lambda運算式實作了資料回呼監聽(與iOS的block類似):
a、宣告Lambda運算式,用于定義回呼方法結構(引數、回傳值),如:
var listener: (errorCode: Int, dataInfos: ArrayList<DataInfo>) -> Unit
Lambda運算式可理解為一種特殊型別,即:抽象方法型別
b、由呼叫方傳遞過來Lambda運算式的實參物件(即:呼叫方已實作的Lambda運算式所表示的抽象方法)
setDataRequestListener(plistener:....)
c、當執行完資料查詢時,將結果通過呼叫Lambda運算式實參物件回傳回去,如:
this.listener(1,datas)
this.listener.invoke(0, datas)
這兩種呼叫方式都是可以的,invoke是指執行自身
當然,我們也可以在Kotlin中使用介面回呼的那種方式,與Java一樣,只是代碼會繁瑣一些而已!!!
2、pageMangage 分頁管理模塊
為了簡化分頁邏輯,讓大家更好理解,此處將分頁資料、分頁布局拆分出來,使其邏輯解耦,也便于代碼的管理維護,
2.1、PagesDataManager 分頁資料管理類
主要內容,包括:
1、配置分頁資料的查詢位置、每頁數量,每次查詢資料后重新計算下次查詢位置
2、分頁資料介面查詢
3、分頁狀態文本處理(資料查詢中、沒有更多資料了、查詢例外...)
package com.qxc.kotlinpages.pagemanage
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.qxc.kotlinpages.pagedata.DataInfo
import com.qxc.kotlinpages.pagedata.DataSearch
/**
* 分頁資料管理類:
* 1、分頁資料的查詢位置、每頁數量
* 2、分頁資料介面查詢
* 3、分頁狀態文本處理
*
* @author 齊行超
* @date 19.11.30
*/
class PagesDataManager {
var startIndex = 0 //分頁起始位置
val pageNum = 10 //每頁數量
val TYPE_PAGE_MORE = "資料加載中..." //分頁加載型別:更多資料
val TYPE_PAGE_LAST = "沒有更多資料了" //分頁加載型別:沒有資料了
val TYPE_PAGE_ERROR = "出錯了,點擊重試!!" //分頁加載型別:出錯了,當這種狀態時可點擊重試
//定義資料回呼監聽
//引數:dataInfos 當前頁資料集合, footType 分頁狀態文本
lateinit var listener: ((dataInfos: ArrayList<DataInfo>, footType: String) -> Unit)
/**
* 設定回呼
*/
fun setDataListener(pListener:
(dataInfos: ArrayList<DataInfo>, footType: String) -> Unit) {
this.listener = pListener
}
/**
* 查詢資料
*/
fun searchPagesData() {
//創建資料查詢物件(模擬資料查詢)
var dataSearch = DataSearch()
//設定資料回呼監聽
dataSearch.setDataRequestListener { errorCode, datas ->
//切回主執行緒
Handler(Looper.getMainLooper()).post {
if (errorCode == 0) {
//累計當前位置
startIndex += datas.size
//判斷后面是否還有資料
var footType = if (pageNum == datas.size) TYPE_PAGE_MORE
else TYPE_PAGE_LAST
//回呼結果
listener.invoke(datas, footType)
} else {
//回呼錯誤結果
listener.invoke(datas, TYPE_PAGE_ERROR)
}
}
}
//查詢資料
dataSearch.search(startIndex, pageNum)
}
/**
* 重置查詢
*/
fun reset() {
startIndex = 0;
}
}
2.2、PagesLayoutManager 分頁布局管理類
主要內容,包括:
1、創建串列布局、網格布局(只創建一次即可)
2、記錄資料位置(用于切換串列布局、網格布局時,保持位置不變)
package com.qxc.kotlinpages.pagemanage
import android.content.Context
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
/**
* 分頁布局管理類:
* 1、創建串列布局、網格布局
* 2、記錄資料位置(用于切換串列布局、網格布局時,保持位置不變)
*
* @author 齊行超
* @date 19.11.30
*/
class PagesLayoutManager(
pcontext: Context
) {
val STYLE_LIST = 1 //串列樣式(常量標識)
val STYLE_GRID = 2 //網格樣式(常量標識)
var llManager: LinearLayoutManager //串列布局管理器物件
var glManager: GridLayoutManager //網格布局管理器物件
var position: Int = 0 //資料位置(當切換樣式時,需記錄串列資料的位置,用以保持資料位置不變)
var context = pcontext //背景關系物件
init {
llManager = LinearLayoutManager(context)
glManager = GridLayoutManager(context, 2)
}
/**
* 獲得布局管理器物件
*/
fun getLayoutManager(pagesStyle: Int): LinearLayoutManager {
//記錄當前資料位置
recordDataPosition(pagesStyle)
//根據樣式回傳布局管理器物件
if (pagesStyle == STYLE_LIST) {
return llManager
}
return glManager
}
/**
* 獲得資料位置
*/
fun getDataPosition(): Int {
return position
}
/**
* 記錄資料位置
*/
private fun recordDataPosition(pagesStyle: Int) {
//pagesStyle表示目標樣式,此處需要記錄的是原樣式時的資料位置
if (pagesStyle == STYLE_LIST) {
position = glManager?.findFirstVisibleItemPosition()
} else if (pagesStyle == STYLE_GRID) {
position = llManager?.findFirstVisibleItemPosition()
}
}
}
2.3、PagesManager 分頁管理類
主要內容,包含:
1、創建、重繪配接器
2、查詢、系結分頁資料
3、切換分頁布局(串列布局、網格布局)
4、當切換至網格布局時,設定footview獨占一行(即使網格布局每行顯示多個item,footview也獨占一行)
主要技術點,包括:
1、設定grid footview獨占一行
2、RecyclerView控制元件的使用(資料系結,重繪,樣式切換等)
package com.qxc.kotlinpages.pagemanage
import android.content.Context
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.qxc.kotlinpages.adapter.PagesAdapter
import com.qxc.kotlinpages.pagedata.DataInfo
/**
* 分頁管理類:
* 1、創建配接器
* 2、查詢、系結分頁資料
* 3、切換分頁布局
* 4、當切換至網格布局時,設定footview獨占一行
*
* @author 齊行超
* @date 19.11.30
*/
class PagesManager(pContext: Context, pRecyclerView: RecyclerView) {
private var context = pContext //背景關系物件
private var recyclerView = pRecyclerView //串列控制元件物件
private var adapter:PagesAdapter? = null //配接器物件
private var pagesLayoutManager = PagesLayoutManager(context) //分頁布局管理物件
private var pagesDataManager = PagesDataManager() //分頁資料管理物件
private var datas = ArrayList<DataInfo>() //資料集合
/**
* 設定分頁樣式(串列、網格)
*
* @param isGrid 是否網格樣式
*/
fun setPagesStyle(isGrid: Boolean): PagesManager {
//通過樣式獲得對應的布局型別
var style = if (isGrid) pagesLayoutManager.STYLE_GRID
else pagesLayoutManager.STYLE_LIST
var layoutManager = pagesLayoutManager.getLayoutManager(style)
//獲得當前資料位置(切換樣式后,滑動到記錄的資料位置)
var position = pagesLayoutManager.getDataPosition()
//創建配接器物件
adapter = PagesAdapter(context, datas, pagesDataManager.TYPE_PAGE_MORE)
//通知配接器,itemview當前使用哪種分頁布局樣式(串列、網格)
adapter?.setItemStyle(style)
//串列控制元件設定配接器
recyclerView.adapter = adapter
//串列控制元件設定布局管理器
recyclerView.layoutManager = layoutManager
//串列控制元件滑動到指定位置
recyclerView.scrollToPosition(position)
//當layoutManager是網格布局時,設定footview獨占一行
if(layoutManager is GridLayoutManager){
setSpanSizeLookup(layoutManager)
}
//設定監聽器
setListener()
return this
}
/**
* 設定監聽器:
*
* 1、當滑動到串列底部時,查詢下一頁資料
* 2、當點擊了footview的"出錯了,點擊重試!!"時,重新查詢資料
*/
fun setListener() {
//1、當滑動到串列底部時,查詢下一頁資料
adapter?.setOnFootViewAttachedToWindowListener {
//查詢資料
searchData()
}
//2、當點擊了footview的"出錯了,點擊重試!!"時,重新查詢資料
adapter?.setOnFootViewClickListener {
if (it.equals(pagesDataManager.TYPE_PAGE_ERROR)) {
//點擊查詢,更改footview狀態
adapter?.footMessage = pagesDataManager.TYPE_PAGE_MORE
adapter?.notifyDataSetChanged()
//"出錯了,點擊重試!!
searchData()
}
}
}
/**
* 設定grid footview獨占一行
*/
fun setSpanSizeLookup(layoutManager: GridLayoutManager) {
layoutManager.setSpanSizeLookup(object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (adapter?.TYPE_FOOTVIEW == adapter?.getItemViewType(position))
layoutManager.getSpanCount()
else 1
}
})
}
/**
* 獲得查詢結果,重繪串列
*/
fun searchData() {
pagesDataManager.setDataListener { pdatas, footMessage ->
if (pdatas != null) {
datas.addAll(pdatas)
adapter?.footMessage = footMessage
adapter?.notifyDataSetChanged()
}
}
pagesDataManager.searchPagesData()
}
}
3、adapter 配接器模塊
3.1、PagesAdapter 分頁配接器類
主要內容,包括:
1、創建、系結itemview(串列item、網格item)、footview
2、判斷是否滑動到串列底部(更簡單的方式實作串列滑動到底部的監聽)
3、footview點擊事件回呼(如果是footview顯示為“出錯了,點擊重試”,需要獲取點擊事件,重新查詢資料)
4、滑動到串列底部事件回呼(當串列滑動到底部時,則需要查詢下一頁資料了)
主要技術點,包括:
1、多item項的應用
2、滑動到串列底部的判斷(比“監聽RecyclerView的Scroll坐標”這種常規做法要簡化很多,且精準)
package com.qxc.kotlinpages.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.qxc.kotlinpages.R
import com.qxc.kotlinpages.pagedata.DataInfo
/**
* 分頁配接器類
* 1、創建、系結itemview(串列item、網格item)、footview
* 2、判斷是否滑動到串列底部
* 3、footview點擊事件回呼
* 4、滑動到串列底部事件回呼
*
* @author 齊行超
* @date 19.11.30
*/
class PagesAdapter(
pContext: Context,
pDataInfos: ArrayList<DataInfo>,
pFootMessage : String
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var context = pContext //背景關系物件,通過構造傳函式遞過來
private var datas = pDataInfos //資料集合,通過建構式傳遞過來
var footMessage = pFootMessage //footview文本資訊,可通過建構式傳遞過來,也可再次修改
val TYPE_FOOTVIEW: Int = 1 //item型別:footview
val TYPE_ITEMVIEW: Int = 2 //item型別:itemview
var typeItem = TYPE_ITEMVIEW
val STYLE_LIST_ITEM = 1 //樣式型別:串列
val STYLE_GRID_ITEM = 2 //樣式型別:網格
var styleItem = STYLE_LIST_ITEM
/**
* 重寫創建ViewHolder的函式
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
: RecyclerView.ViewHolder {
//如果是itemview
if (typeItem == TYPE_ITEMVIEW) {
//判斷樣式型別(串列布局、網格布局)
var layoutId =
if (styleItem == STYLE_LIST_ITEM) R.layout.item_page_list
else R.layout.item_page_grid
var view = LayoutInflater.from(context).inflate(layoutId, parent, false)
return ItemViewHolder(view)
}
//如果是footview
else {
var view = LayoutInflater.from(context)
.inflate(R.layout.item_page_foot, parent, false)
return FootViewHolder(view)
}
}
/**
* 重寫獲得項數量的函式
*/
override fun getItemCount(): Int {
//因串列中增加了footview(顯示分頁狀態資訊),所以item總數量 = 資料數量 + 1
return datas.size + 1
}
/**
* 重寫系結ViewHolder的函式
*/
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is ItemViewHolder) {
if (datas.size <= position) {
return
}
var data = https://www.cnblogs.com/qixingchao/p/datas.get(position)
holder.tv_title.text = data.title
holder.tv_desc.text = data.desc
holder.tv_price.text = data.price
holder.tv_link.text = data.link
} else if (holder is FootViewHolder) {
holder.tv_msg.text = footMessage
//當點擊footview時,將該事件回呼出去
holder.tv_msg.setOnClickListener {
footViewClickListener.invoke(footMessage)
}
}
}
/**
* 重新獲得項型別的函式(項型別包括:itemview、footview)
*/
override fun getItemViewType(position: Int): Int {
//設定在資料最底部顯示footview
typeItem = if (position == datas.size) TYPE_FOOTVIEW else TYPE_ITEMVIEW
return typeItem
}
/**
* 當footview第二次出現在串列時,回呼該事件
* (此處用于模擬用戶上滑手勢,當滑到底部時,重新請求資料)
*/
var footviewPosition = 0
override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {
super.onViewAttachedToWindow(holder)
if (footviewPosition == holder.adapterPosition) {
return
}
if (holder is FootViewHolder) {
footviewPosition = holder.adapterPosition
//回呼查詢事件
footViewAttachedToWindowListener.invoke()
}
}
/**
* ItemViewHolder
*/
class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var tv_title = itemView.findViewById(R.id.tv_title)
var tv_desc = itemView.findViewById(R.id.tv_desc)
var tv_price = itemView.findViewById(R.id.tv_price)
var tv_link = itemView.findViewById(R.id.tv_link)
}
/**
* FootViewHolder
*/
class FootViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var tv_msg = itemView.findViewById(R.id.tv_msg)
}
/**
* 設定Item樣式(串列、網格)
*/
fun setItemStyle(pstyle: Int) {
styleItem = pstyle
}
//定義footview附加到Window上時的回呼
lateinit var footViewAttachedToWindowListener: () -> Unit
fun setOnFootViewAttachedToWindowListener(pListener: () -> Unit) {
this.footViewAttachedToWindowListener = pListener
}
//定義footview點擊時的回呼
lateinit var footViewClickListener:(String)->Unit
fun setOnFootViewClickListener(pListner:(String)->Unit){
this.footViewClickListener = pListner
}
}
4、utils 工具模塊
4.1、AppUtils 專案工具類
此案例中主要用于判斷網路連接情況,
該類的主要技術點:Kotlin的共生物件、執行緒安全單例,詳見原始碼:
package com.qxc.kotlinpages.utils
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Build
/**
* 工具類
*
* @author 齊行超
* @date 19.11.30
*/
class AppUtils {
//使用共生物件,表示靜態static
companion object{
/**
* 執行緒安全的單例(懶漢式單例)
*/
val instance : AppUtils by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
AppUtils()
}
private lateinit var context:Context
/**
* 注冊
*
* @param pContext 背景關系
*/
fun register(pContext: Context){
context = pContext
}
}
/**
* 判斷是否連接了網路
*/
fun isConnectNetWork():Boolean{
var result = false
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE)
as ConnectivityManager?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
cm?.run {
this.getNetworkCapabilities(cm.activeNetwork)?.run {
result = when {
this.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
this.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
this.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
else -> false
}
}
}
} else {
cm?.run {
cm.activeNetworkInfo?.run {
if (type == ConnectivityManager.TYPE_WIFI) {
result = true
} else if (type == ConnectivityManager.TYPE_MOBILE) {
result = true
}
}
}
}
return result
}
}
5、UI模塊
5.1、MainActivity 主頁面,用于顯示分頁串列、切換分頁樣式(串列樣式、網格樣式)
package com.qxc.kotlinpages
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.qxc.kotlinpages.pagemanage.PagesManager
import com.qxc.kotlinpages.utils.AppUtils
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
var isGrid = false
var pagesManager: PagesManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
AppUtils.register(this)
initEvent()
initData()
}
fun initEvent() {
//切換串列樣式按鈕的點擊事件
iv_style.setOnClickListener {
//切換圖示(串列與網格)
var id: Int =
if (isGrid) R.mipmap.product_search_list_style_grid
else R.mipmap.product_search_list_style_list
iv_style.setImageResource(id)
//記錄當前圖示型別
isGrid = !isGrid
//更改樣式(串列與網格)
pagesManager!!.setPagesStyle(isGrid)
}
}
fun initData() {
//初始化PagesManager,默認查詢串列
pagesManager = PagesManager(this, rv_data)
pagesManager!!.setPagesStyle(isGrid).searchData()
}
}
注意:頁面中參考了 kotlinx.android.synthetic.main.activity_main.*
》》這表示無需再寫findViewById()了,直接使用xml中控制元件id即可
MainActivity的布局頁面,使用了約束布局,層級嵌套少,且更簡單一些:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="@+id/v_top"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#FD4D4D"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="分頁demo"
android:textColor="#ffffff"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@id/v_top"
app:layout_constraintLeft_toLeftOf="@id/v_top"
app:layout_constraintRight_toRightOf="@id/v_top"
app:layout_constraintTop_toTopOf="@id/v_top" />
<ImageView
android:id="@+id/iv_style"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginRight="5dp"
android:scaleType="center"
android:src=https://www.cnblogs.com/qixingchao/p/"@mipmap/product_search_list_style_grid"
app:layout_constraintBottom_toBottomOf="@id/v_top"
app:layout_constraintRight_toRightOf="@id/v_top"
app:layout_constraintTop_toTopOf="@id/v_top" />
5.2、item布局(串列樣式),也是使用了約束布局:

<?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="150dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:background="#eeeeee">
<ImageView
android:id="@+id/iv_image"
android:layout_width="80dp"
android:layout_height="110dp"
android:layout_marginLeft="10dp"
android:scaleType="fitXY"
android:src=https://www.cnblogs.com/qixingchao/p/"@mipmap/kotlin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
5.3、item布局(網格樣式),仍然使用了約束布局:

<?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="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:paddingBottom="10dp"
android:background="#eeeeee">
<ImageView
android:id="@+id/iv_image"
android:layout_width="80dp"
android:layout_height="110dp"
android:layout_marginTop="10dp"
android:scaleType="fitXY"
android:src=https://www.cnblogs.com/qixingchao/p/"@mipmap/kotlin"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
5.4、footview布局
比較簡單,僅有一個文本控制元件:

<?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="50dp"
android:layout_margin="10dp"
android:background="#eeeeee">
<TextView
android:id="@+id/tv_msg"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="加載中..."
android:textColor="#777777"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
總結
分頁實作難點匯總:
1、切換RecyclerView展示樣式(串列樣式、網格樣式),保持資料位置不變
2、網格樣式時,footview獨占一行
3、直接在adapter中判斷是否滑動到了底部,比常規做法(監聽滑動坐標)更簡單一些
4、分頁狀態管控(資料加載中、沒有更多資料了、出錯了點擊重試)
Kotlin主要技術點匯總:
1、多執行緒實作(Lambda運算式的應用)
2、異步回呼(Lambda運算式的應用、高階函式)
3、共生物件
4、執行緒安全單例
5、其他略(都比較基礎了,大家熟悉下即可)
此篇文章主要是為了講解常規分頁的實作,所以只是做了一些基礎的拆分解耦,如果想在專案中使用,建議還是抽象一下,擴展性會更好一些(如:footview介面化擴展、資料查詢介面化擴展等),
如果有疑問,也歡迎留言咨詢O(∩_∩)O~
Demo下載地址:
https://pan.baidu.com/s/1gH0Zcd0QXdm4mRNMqJgS8Q
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/47850.html
標籤:Android
下一篇:Android動態添加碎片
