VLayout中LayoutHelper分类(辅助Adapter实现RecyclerView的Item各种类型的布局方式)
LinearLayoutHelper: 线性布局
GridLayoutHelper: Grid布局, 支持横向的colspan
FixLayoutHelper: 固定布局,始终在屏幕固定位置显示
ScrollFixLayoutHelper: 固定布局,但之后当页面滑动到该图片区域才显示, 可以用来做返回顶部或其他书签等
FloatLayoutHelper: 浮动布局,可以固定显示在屏幕上,但用户可以拖拽其位置
ColumnLayoutHelper: 栏格布局,都布局在一排,可以配置不同列之间的宽度比值
SingleLayoutHelper: 通栏布局,只会显示一个组件View
OnePlusNLayoutHelper: 一拖N布局,可以配置1-5个子元素
StickyLayoutHelper: stikcy布局, 可以配置吸顶或者吸底
StaggeredGridLayoutHelper: 瀑布流布局,可配置间隔高度/宽度
新建Helper的方法详见VLayout
在项目app->build.gradle->dependencies中添加如下依赖
注意:使用过程中可能存在AnimatorCompatHelper.java找不到的情况,原因可能是当前编译使用的support-v4包中不存在该类(例如26.0.1)
解决方案一:在项目中新建android.support.v4.animation包,并将…\sdk\sources\android-25\android\support\v4\animation路径(在SDK安装路径下搜索AnimatorCompatHelper.java得到的路径)下的AnimatorCompatHelper.java复制到新建的包中
解决方案二:使用com.android.support:recyclerview-v7和com.android.support:support-v4最新的依赖包
compile ('com.alibaba.android:vlayout:1.2.8@aar') {
transitive = true
}
如果一屏内相同类型的 ItemView 个数比较多,需要设置一个合适的大小,防止来回滚动时重新创建 ItemView
val viewPool = RecyclerView.RecycledViewPool()
rv.setRecycledViewPool(viewPool)
//参数一:itemView类型ID 参数二:最大回收复用ItemView数(设置1:全部重建,设置10:屏幕最多保留10个相同ItemView不重建)
viewPool.setMaxRecycledViews(0, 10)
方法一:通过DelegateAdapter初始化RecyclerView
//参数一:上下文 参数二:布局方向
val layoutManager = VirtualLayoutManager(mContext, VirtualLayoutManager.VERTICAL)
rv.setLayoutManager(layoutManager)
//参数一:布局管理器 参数二:是否所有子adapter共享Item布局(当hasConsistItemType=true的时候,不论是不是属于同一个子adapter,相同类型的item都能复用。表示它们共享一个类型。 当hasConsistItemType=false的时候,不同子adapter之间的类型不共享)
val delegateAdapter = DelegateAdapter(layoutManager, true)
val adapters = ArrayList<DelegateAdapter.Adapter<RecyclerView.ViewHolder>>
//MyAdapter是继承DelegateAdapter.Adapter的实例适配器,详见BaseVLayoutDelegateAdapter
adapters.add(MyAdapter(floatLayoutHelper))
...
adapters.add(MyAdapter(stickyLayoutHelper))
//设置子adapter集合
delegateAdapter.setAdapters(adapters)
//为RecyclerView设置adapter
rv.adapter = delegateAdapter
方法二:通过VirtualLayoutAdapter初始化RecyclerView
//参数一:上下文 参数二:布局方向
val layoutManager = VirtualLayoutManager(mContext, VirtualLayoutManager.VERTICAL) rv.setLayoutManager(layoutManager)
val helpers = LinkedList<LayoutHelper>()
helpers.add(stickyLayoutHelper)
...
helpers.add(linearLayoutHelper)
//Adapter是继承VirtualLayoutAdapter的实例适配器 详见 BaseVLayoutVirtualLayoutAdapter
val adapter = Adapter(layoutManager, helpers)
rv.adapter = adapter
-keepattributes InnerClasses
-keep class com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx { *; }
-keep class android.support.v7.widget.RecyclerView$LayoutParams { *; }
-keep class android.support.v7.widget.RecyclerView$ViewHolder { *; }
-keep class android.support.v7.widget.ChildHelper { *; }
-keep class android.support.v7.widget.ChildHelper$Bucket { *; }
-keep class android.support.v7.widget.RecyclerView$LayoutManager { *; }
object VLayoutHelper {
fun initRecyclerView(rv: RecyclerView,
@OrientationMode orientation: Int,
hasConsistItemType: Boolean,
adapters: List<DelegateAdapter.Adapter<RecyclerView.ViewHolder>>) {
val layoutManager = VirtualLayoutManager(MyAPP.getInstance(), orientation)
rv.setLayoutManager(layoutManager)
val delegateAdapter = DelegateAdapter(layoutManager, hasConsistItemType)
delegateAdapter.setAdapters(adapters as List<DelegateAdapter.Adapter<RecyclerView.ViewHolder>>?)
rv.adapter = delegateAdapter
}
fun initRecyclerView(rv: RecyclerView, @OrientationMode orientation: Int, hasConsistItemType: Boolean, adapter: VirtualLayoutAdapter<RecyclerView.ViewHolder>) {
val layoutManager = VirtualLayoutManager(MyAPP.getInstance(), orientation)
rv.setLayoutManager(layoutManager)
rv.adapter = adapter
}
fun setViewPool(rv: RecyclerView, viewType: Int, max: Int) {
val viewPool = RecyclerView.RecycledViewPool()
rv.setRecycledViewPool(viewPool)
viewPool.setMaxRecycledViews(viewType, max)
}
/**
* 获取线性布局Helper
*/
fun getLinearLayoutHelper(count: Int, dividerHeight: Int): LinearLayoutHelper {
val linearLayoutHelper = LinearLayoutHelper()
//线性布局的Item条目数
linearLayoutHelper.itemCount = count
linearLayoutHelper.setDividerHeight(dividerHeight)
return linearLayoutHelper
}
fun getLinearLayoutHelper(@DimenRes dividerHeightID: Int): LinearLayoutHelper {
return getLinearLayoutHelper(0, Resources.getSystem().getDimensionPixelOffset(dividerHeightID))
}
/**
* 获取网格布局
*/
fun getGridLayoutHelper(spanCount: Int, isAutoExpand: Boolean): GridLayoutHelper {
return getGridLayoutHelper(spanCount, isAutoExpand, 0, null, null)
}
fun getGridLayoutHelper(spanCount: Int, position: ((Int) -> Int)?): GridLayoutHelper {
return getGridLayoutHelper(spanCount, false, 0, null, position)
}
fun getGridLayoutHelper(spanCount: Int, itemCount: Int, position: ((Int) -> Int)?): GridLayoutHelper {
return VLayoutHelper.getGridLayoutHelper(spanCount, false, itemCount, null, 0, 0, position)
}
fun getGridLayoutHelper(spanCount: Int, isAutoExpand: Boolean, itemCount: Int, weights: FloatArray?,
position: ((Int) -> Int)?): GridLayoutHelper {
return VLayoutHelper.getGridLayoutHelper(spanCount, isAutoExpand, itemCount, weights, 0, 0, position)
}
fun getGridLayoutHelper(spanCount: Int, isAutoExpand: Boolean, itemCount: Int, weights: FloatArray?, gap: Int,
position: ((Int) -> Int)?): GridLayoutHelper {
return VLayoutHelper.getGridLayoutHelper(spanCount, isAutoExpand, itemCount, weights, gap, gap, position)
}
fun getGridLayoutHelper(spanCount: Int, isAutoExpand: Boolean, itemCount: Int, weights: FloatArray?,
vGap: Int, hGap: Int, position: ((Int) -> Int)?): GridLayoutHelper {
var spanSL: GridLayoutHelper.SpanSizeLookup? = null
if (null != position) {
spanSL = object : GridLayoutHelper.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return position(position)
}
}
}
//网格布局列数
val gridLayoutHelper = GridLayoutHelper(spanCount)
//是否自动补全布局(当其设置为true时,如果一行的Item总占用列数小于网格列数,那么Item两头的间距将均匀拉伸使之填满一行)
gridLayoutHelper.setAutoExpand(isAutoExpand)
//网格布局的Item条目数
gridLayoutHelper.itemCount = itemCount
//设置每列的权重(表示每列占一行的多大宽度,最大100f,weights的size不能大于网格列数,其值得和不能大于100f)
gridLayoutHelper.setWeights(weights)
//设置Item的水平间距
gridLayoutHelper.hGap = hGap
//设置Item的垂直间距
gridLayoutHelper.vGap = vGap
//指定条目占用一行的指定列数的规则(指定占用列数不能大于网格的列数)
gridLayoutHelper.setSpanSizeLookup(spanSL)
return gridLayoutHelper
}
const val TOP_LEFT = 0 //以RecyclerView内左上角为原点
const val TOP_RIGHT = 1 //以RecyclerView内右上角为原点
const val BOTTOM_LEFT = 2 //以RecyclerView内左下角为原点
const val BOTTOM_RIGHT = 3 //以RecyclerView内右下角为原点
/**
* 以RecyclerView控件内部为基准
*/
@IntDef(TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT)
@Retention(AnnotationRetention.SOURCE)
private annotation class LayoutHelperAlignType
/**
* 获取FixLayout固定布局
*/
fun getFixLayoutHelper(@LayoutHelperAlignType alignType: Int): FixLayoutHelper {
return getFixLayoutHelper(alignType, 0, 0)
}
fun getFixLayoutHelper(@LayoutHelperAlignType alignType: Int, offsetX: Int, offsetY: Int): FixLayoutHelper {
//参数一:设置对齐方式(原点位置) 参数二:相对原点X轴偏移量 参数三:相对原点Y轴偏移量
val fixLayoutHelper = FixLayoutHelper(alignType,offsetX, offsetY)
fixLayoutHelper.itemCount = 1
return fixLayoutHelper
}
const val SHOW_ALWAYS = 0
const val SHOW_ON_ENTER = 1
const val SHOW_ON_LEAVE = 2
@IntDef(SHOW_ALWAYS, SHOW_ON_ENTER, SHOW_ON_LEAVE)
@Retention(AnnotationRetention.SOURCE)
private annotation class ScrollFixLayoutHelperShowType
/**
* 这个也是固定布局,而且使继承自FixLayoutHelper的,这个Helper存在一定bug,有可能设置showType无效
*- SHOW_ALWAYS:与FixLayoutHelper的行为一致,固定在某个位置;
*- SHOW_ON_ENTER:默认不显示视图,当页面滚动到这个视图的位置的时候,才显示;
*- SHOW_ON_LEAVE:默认不显示视图,当页面滚出这个视图的位置的时候显示;
*/
fun getScrollFixLayoutHelper(@LayoutHelperAlignType alignType: Int, @ScrollFixLayoutHelperShowType showType: Int): ScrollFixLayoutHelper {
return getScrollFixLayoutHelper(alignType, showType, 0, 0)
}
fun getScrollFixLayoutHelper(@LayoutHelperAlignType alignType: Int, @ScrollFixLayoutHelperShowType showType: Int, offsetX: Int, offsetY: Int): ScrollFixLayoutHelper {
val scrollFixLayoutHelper = ScrollFixLayoutHelper(alignType, offsetX, offsetY)
scrollFixLayoutHelper.setShowType(showType)
scrollFixLayoutHelper.itemCount = 1
return scrollFixLayoutHelper
}
/**
* 获取可拖动布局
* 设置dragEnable = true可拖动时
* 当其为adapter的第一个helper时可以直接拖动
* 否则需要其上一个helper显示后才可以拖动
*/
fun getFloatLayoutHelper(@LayoutHelperAlignType alignType: Int,dragEnable: Boolean): FloatLayoutHelper {
return getFloatLayoutHelper(TOP_LEFT,0,0,dragEnable)
}
fun getFloatLayoutHelper(posX: Int, posY: Int, dragEnable: Boolean): FloatLayoutHelper {
return getFloatLayoutHelper(TOP_LEFT,posX,posY,dragEnable)
}
fun getFloatLayoutHelper(@LayoutHelperAlignType alignType: Int,posX: Int, posY: Int, dragEnable: Boolean): FloatLayoutHelper {
val floatLayoutHelper = FloatLayoutHelper()
floatLayoutHelper.setAlignType(alignType)
//参数一:相对原点X轴偏移量 参数二:相对原点Y轴偏移量
floatLayoutHelper.setDefaultLocation(posX, posY)
floatLayoutHelper.setDragEnable(dragEnable)
floatLayoutHelper.itemCount = 1
return floatLayoutHelper
}
/**
* 获取栏格布局
* 只有一行,可通过itemCount设置多列
*/
fun getColumnLayoutHelper(count: Int, weights: FloatArray?): ColumnLayoutHelper {
val columnLayoutHelper = ColumnLayoutHelper()
columnLayoutHelper.setWeights(weights)
columnLayoutHelper.itemCount = count
return columnLayoutHelper
}
/**
* 获取通栏布局
* 只有一个Item
*/
fun getSingleLayoutHelper(): SingleLayoutHelper {
val singleLayoutHelper = SingleLayoutHelper()
return singleLayoutHelper
}
/**
* 获取一拖N布局(最多5个Item)
* 布局方式详见[GitHub->OnePlusNLayoutHelper.java](https://github.com/alibaba/vlayout/blob/master/vlayout/src/main/java/com/alibaba/android/vlayout/layout/OnePlusNLayoutHelper.java)
*/
fun getOnePlusNLayoutHelper(count: Int, colWeights: FloatArray?, rowWeight: Float): OnePlusNLayoutHelper {
val onePlusNLayoutHelper = OnePlusNLayoutHelper()
onePlusNLayoutHelper.itemCount = if (count > 5) 5 else count
onePlusNLayoutHelper.setColWeights(colWeights)
onePlusNLayoutHelper.setRowWeight(rowWeight)
return onePlusNLayoutHelper
}
fun getOnePlusNLayoutHelper(count: Int): OnePlusNLayoutHelper {
return getOnePlusNLayoutHelper(count, null, Float.NaN)
}
/**
* 获取一拖N布局(最多7个Item)
* 布局方式详见[GitHub->OnePlusNLayoutHelperEx.java](https://github.com/alibaba/vlayout/blob/master/vlayout/src/main/java/com/alibaba/android/vlayout/layout/OnePlusNLayoutHelperEx.java)
*/
fun getOnePlusNLayoutHelperEx(count: Int): OnePlusNLayoutHelperEx {
return getOnePlusNLayoutHelperEx(count,null, Float.NaN)
}
fun getOnePlusNLayoutHelperEx(count: Int, colWeights: FloatArray?, rowWeight: Float): OnePlusNLayoutHelperEx {
val onePlusNLayoutHelperEx = OnePlusNLayoutHelperEx ()
onePlusNLayoutHelperEx.itemCount = if (count > 7) 7 else count
onePlusNLayoutHelperEx.setColWeights(colWeights)
onePlusNLayoutHelperEx.setRowWeight(rowWeight)
return onePlusNLayoutHelperEx
}
/**
* 获取吸边布局
* stickyStart是否吸顶 true = 组件吸在顶部 false = 组件吸在底部
*/
fun getStickyLayoutHelper(stickyStart: Boolean, offset: Int): StickyLayoutHelper {
val stickyLayoutHelper = StickyLayoutHelper()
stickyLayoutHelper.setStickyStart(stickyStart)
stickyLayoutHelper.setOffset(offset)
return stickyLayoutHelper
}
/**
* 获取瀑布流布局
* count item数
* lane 列数(每行条目数)
*/
fun getStaggeredGridLayoutHelper(count: Int, lane: Int, gap: Int): StaggeredGridLayoutHelper {
return getStaggeredGridLayoutHelper(count, lane, gap, 0, 0)
}
fun getStaggeredGridLayoutHelper(count: Int, lane: Int, gap: Int, vGap: Int, hGap: Int): StaggeredGridLayoutHelper {
val staggeredGridLayoutHelper = StaggeredGridLayoutHelper(lane)
staggeredGridLayoutHelper.itemCount = count
if (gap > 0) {
//设置Item的间距
staggeredGridLayoutHelper.setGap(gap)
} else {
//设置Item水平间距
staggeredGridLayoutHelper.hGap = hGap
//设置Item垂直间距
staggeredGridLayoutHelper.vGap = vGap
}
return staggeredGridLayoutHelper
}
}
abstract class BaseVLayoutVirtualLayoutAdapter(private val layoutManager: VirtualLayoutManager, helpers: List<LayoutHelper>) : VirtualLayoutAdapter<BaseVLayoutVirtualLayoutAdapter.BaseViewHolder>(layoutManager) {
init {
layoutHelpers = helpers
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
return BaseViewHolder(LayoutInflater.from(parent.context).inflate(getCustomHelperLayout(viewType), parent, false))
}
abstract fun getCustomHelperLayout(viewType: Int): Int
/**
*获取指定布局位置的LayoutHelper
*/
fun getLayoutHelper(position: Int): LayoutHelper? {
return layoutManager.findLayoutHelperByPosition(position)
}
/**
*获取指定位置的ItemView
*/
fun getItemView(position: Int): View? {
return layoutManager.findViewByPosition(position)
}
override fun getItemCount(): Int {
var count = 0
for (layoutHelper in layoutHelpers){
count += layoutHelper.itemCount
}
return count
}
override fun getItemViewType(position: Int): Int {
return getItemViewType(getLayoutHelper(position)!!, position)
}
fun getItemViewType(@Nullable layoutHelper: LayoutHelper, position: Int): Int {
return super.getItemViewType(position)
}
inner class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}
abstract class BaseVLayoutDelegateAdapter(private val layoutHelper: LayoutHelper) : DelegateAdapter.Adapter<BaseVLayoutDelegateAdapter.BaseViewHolder>() {
override fun onCreateLayoutHelper(): LayoutHelper {
return layoutHelper
}
fun getLayoutHelper(): LayoutHelper {
return layoutHelper
}
override fun getItemCount(): Int {
return layoutHelper.itemCount
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
@LayoutRes var layoutID = getCustomHelperLayout(layoutHelper, viewType)
return BaseViewHolder(LayoutInflater.from(parent.context).inflate(layoutID, parent, false))
}
fun getCustomHelperLayout(layoutHelper: LayoutHelper, viewType: Int): Int {
return 0
}
override fun getItemViewType(position: Int): Int {
return getViewType(position, layoutHelper)
}
fun getViewType(position: Int, layoutHelper: LayoutHelper): Int {
return super.getItemViewType(position)
}
inner class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}
alibaba/vlayout:https://github.com/alibaba/vlayout