Skip to content

教你构建瀑布流 Step4

zhangxin-it edited this page Dec 11, 2019 · 3 revisions

完整Demo:WaterfallViewDemo.lua

WaterfallView是MomoLua中对瀑布流布局的封装,继承自CollectionView,相关adapter和layout也都继承自CollectionView相关的adapter和layout。他的使用包括WaterfallView、WaterfallLayout和WaterfallAdapter三个部分。其中WaterfallView决定整个瀑布流布局的显示位置,大小。WaterfallLayout指定adapter中每个视图之间的间隔,大小。Adapter说明数据(Data)和单元视图(ItemView)的绑定关系,以及每个单元视图(ItemView)的渲染方式。整个实现流程可参考TableView、CollectionView的相关思路。

一、WaterfallView

WaterfallView决定整个瀑布流布局的显示位置,大小。此处将介绍其初始化及常用事件和方法。

-------------------------初始化WaterfallView-----------------------------
W = System:screenSize():width()
collectionView = WaterfallView(true, true)
        :width(MeasurementType.MATCH_PARENT)
        :height(MeasurementType.MATCH_PARENT)
        :bgColor(Color(255, 255, 0, 0.5))
        :useAllSpanForLoading(true)--加载动画是否占用一行,默认不占用
        :showScrollIndicator(true)--显示滑动指示器

--下拉刷新事件回调
collectionView:setRefreshingCallback(
        function()
            print("开始刷新")
            System:setTimeOut(function()
                --2秒后结束刷新
                print("结束刷新了")
                collectionView:stopRefreshing()
            end, 2)
        end)
--上拉加载事件回调
collectionView:setLoadingCallback(function()
    print("开始加载")
    System:setTimeOut(function()
        --2秒后结束加载
        print("结束加载")
        collectionView:stopLoading()
        --已加载全部
        collectionView:noMoreData()
    end, 2)

end)
--开始滑动的回调事件
collectionView:setScrollBeginCallback(function()
    print("开始滑动")
end)
--滑动中的回调事件
collectionView:setScrollingCallback(function()
    --print("滑动中")
end)
--结束滑动的回调事件
collectionView:setScrollEndCallback(function()
    print("结束滑动")
end)
--初始化WaterfallLayout,具体实现稍后会介绍
collectionLayout = initWaterfallLayout()
collectionView:layout(collectionLayout)
--adapter初始化方法,具体实现稍后会介绍
adapter = initAdapter()
collectionView:adapter(adapter)

在以上代码中,我们新建一个WaterfallView,其构造方法中2个参数控制是否刷新或加载,并设置其指示器显示以及滑动监听等属性和方法。 相关属性及方法如下:

addHeaderView(View view) 添加头布局
removeHeaderView() 移除头布局
showScrollIndicator(boolean b) 是否显示滑动指示器
useAllSpanForLoading(boolean b) 加载动画是否占用一行,默认不占用
setRefreshingCallback(func callback) 设置下拉刷新回调
setLoadingCallback(func callback) 设置上拉加载回调
setScrollBeginCallback(func callback) 设置开始滑动回调
setScrollingCallback(func callback) 设置滑动中回调
setScrollEndCallback(func callback) 设置结束滑动回调
adapter(Adapter adapter) 绑定适配器
至此WaterfallView的初始化操作基本完成。

二、WaterfallLayout

WaterfallLayout指定adapter中每个视图(ItemView)之间的间隔大小。

---------------------------初始化WaterfallLayout----------------------------
collectionLayout = WaterfallLayout()
collectionLayout:itemSpacing(5)--间隔大小
                :lineSpacing(5)
                :spanCount(2)
collectionView:layout(collectionLayout)

三、WaterfallAdapter

adapter适配器的说明及其关键方法使用类似于TableView的adapter,它作为一个中间类来协助WaterfallView完成对于子View的创建和赋值操作。可前往TableView了解更多详细。 用法如下:

---------------------------初始化WaterfallAdapter----------------------------
local function initAdapter()

    adapter = WaterfallAdapter()

    adapter:sectionCount(function()
        return 1
    end)
    
    -----------------------------设置header----------------------------------
        adapter:initHeader(function(header)
            header.contentView:bgColor(Color(0, 0, 255, 0.3))
            header.textLabel = Label():setGravity(Gravity.CENTER):width(MeasurementType.MATCH_PARENT):height(MeasurementType.MATCH_PARENT):lines(0):textAlign(TextAlign.CENTER)
            header.contentView:addView(header.textLabel)
        end)
    
        adapter:fillHeaderData(function(header, section, row)
            header.textLabel:text('我是头布局')
        end)
    
        adapter:headerValid(function(section)
            return true
        end)
        adapter:heightForHeader(function(section)
            return 100
        end)
    -----------------------------设置子view个数-------------------------------
    adapter:rowCount(function(section)
        if datas == nill or #datas == 0 then
            return 0;
        else
            return #datas;
        end
    end)
    --------------------------------设置cell高-------------------------------
    --设置对应子view的高
    adapter:heightForCell(function(section, row)
        return datas[row].height
    end)
    -------------------------------子view类型--------------------------------
    --返回当前位置子view的类型标识
    adapter:reuseId(function(section, row)
        return "CellId"
    end)
    -------------------------------创建子view--------------------------------
    adapter:initCellByReuseId("CellId", function(cell)

        cell.userView = LinearLayout(LinearType.VERTICAL)
                :setGravity(Gravity.CENTER)
        --头像
        cell.imageView = ImageView():width(50):height(50):cornerRadius(45)
                                    :priority(1):bgColor(Color(255, 0, 0, 0.5))
                                    :setGravity(Gravity.CENTER)
        cell.userView:addView(cell.imageView)
        --昵称
        cell.nameLabel = Label():fontSize(14):textColor(Color(0, 0, 0, 1))
                                :text("昵称"):setGravity(Gravity.CENTER)
                                :marginTop(5)
        cell.contentView:bgColor(Color(255, 255, 255, 1))
            :cornerRadius(5)
        cell.userView:addView(cell.nameLabel)
        cell.contentView:addView(cell.userView)
    end)
    --------------------------将子view与数据进行绑定赋值-------------------------
    adapter:fillCellDataByReuseId("CellId", function(cell, section, row)
        cell.nameLabel:text(datas[row].name)
    end)
    --cell点击事件
    adapter:selectedRowByReuseId("CellId", function(cell, section, row)
        print("点击了cell", row .. datas[row])
    end)
    --cell被滑出屏幕可见区域的回调
    adapter:cellDidDisappear(function(cell, section, row)
        print("cell不见了", row)
    end)
    --cell出现在屏幕可见区域的回调
    adapter:cellWillAppear(function(cell, section, row)
        print("cell出现了", row)
    end)
    --头布局header被滑出屏幕可见区域的回调
    adapter:headerDidDisappear(function()
        print("头布局header不见了")
    end)
    --头布局header出现在屏幕可见区域的回调
    adapter:headerWillAppear(function()
        print("头布局header出现了")
    end)
    return adapter
end

上述代码我们可以看到基本与TableView的adapter使用一样。相关方法可参考TableView使用教程。对于头布局header,我们以下几个常用方法:

headerDidDisappear(function()) 头布局header被滑出屏幕可见区域的回调
headerWillAppear(function()) 头布局header出现在屏幕可见区域的回调
在和reuseId相关的方法中,每个方法都可以有多个同名的"重载"方法,当我们在reuseId(func)方法中返回多少种reuseId,与reuseId相关的方法就各自要多少种"重载",例如代码中返回两种reuseId,那么用来创建子view的方法initCellByReuseId就有两个"重载"(其实不是重载,sdk的底层用委托的方式实现调用)。类似的fillCellDataByReuseId等方法也是如此。 当然当我们reuseId只有一个时,完全可以使用不区分reuseId的方法去初始化adapter。简化如下:
--初始化适配器
    --创建子view
    adapter:initCell(function(cell)

    end)
    --将子view与数据进行绑定赋值
    adapter:fillCellData(function(cell, section, row)
    
    end)
    --cell点击事件
    adapter:selectedRow(function(cell, section, row)
    
    end)
    return adapter

【注】 区分reuseId的方法不能与不区分reuseId的方法同时使用,如使用initCellByReuseId方法初始化单元视图时就不可再使用initCell方法。

以上代码完成了adapter初始化,之后调用adapter方法实现适配器绑定即可完成瀑布流布局的一个简单实现。 总结:TableView(列表布局)、CollectionView(网格布局)、WaterfallView(瀑布流布局)整体实现思路上基本一致,包括其adapter的实现方法。

附录

上述例子datas的数据格式:

datas = {
    {
        name = "苹果",
        height = 140
    },
    {
        name = "葡萄",
        height = 100
    },
    {
        name = "西瓜",
        height = 130
    },
    {
        name = "草莓",
        height = 118
    },
    {
        name = "菠萝",
        height = 190
    },
    {
        name = "香蕉",
        height = 100
    },
    {
        name = "芒果",
        height = 120
    },
    {
        name = "猕猴桃",
        height = 150
    },
    {
        name = "橘子",
        height = 180
    },
    {
        name = "哈密瓜",
        height = 200
    },
}
Clone this wiki locally