49.自定义控件-Banner

1.gitbub地址

  implementation 'io.github.youth5201314:banner:2.2.3'

2.核心功能

  1. Banner(com.youth.banner.Banner)
    • 轮播控件容器
    • 内部持有一个 RecyclerView + ViewPager2 实现循环滑动
    • 负责轮播控制(自动播放、手势滑动、翻页)
  2. BannerAdapter
    • 用于给 Banner 提供数据和创建 View
    • 类似 RecyclerView.Adapter
  3. Indicator
    • 指示器(圆点、数字、方块)
    • 可自定义或使用内置的 CircleIndicator
  4. BannerViewHolder
    • 单个轮播页的 View 容器
    • 支持任意自定义布局

3.自定义 Item View

步骤 1:创建数据类

data class BannerData(
    val imageUrl: String,
    val title: String
)

步骤 2:创建自定义 Adapter

继承 BannerAdapter<T, VH>:

class MyBannerAdapter(data: List<BannerData>) :
    BannerAdapter<BannerData, MyBannerAdapter.BannerViewHolder>(data) {

    // 创建自定义 ViewHolder
    class BannerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val imageView: ImageView = view.findViewById(R.id.image)
        val titleView: TextView = view.findViewById(R.id.title)
    }

    override fun onCreateHolder(parent: ViewGroup, viewType: Int): BannerViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_banner, parent, false)
        return BannerViewHolder(view)
    }

    override fun onBindView(holder: BannerViewHolder, data: BannerData, position: Int, size: Int) {
        // 绑定数据
        Glide.with(holder.itemView)
            .load(data.imageUrl)
            .into(holder.imageView)
        holder.titleView.text = data.title
    }
}

🔹 item_banner.xml 可以完全自定义(例如图片 + 文本 + 按钮)

步骤 3:在 Activity/Fragment 中使用

val list = listOf(
    BannerData("https://xxx/image1.jpg", "标题1"),
    BannerData("https://xxx/image2.jpg", "标题2")
)

binding.banner.apply {
    adapter = MyBannerAdapter(list)      // 设置自定义Adapter
    indicator = CircleIndicator(context) // 设置指示器
    isAutoLoop(true)                     // 自动轮播
    setLoopTime(3000)                    // 3秒切换
    start()
}

4.自定义 Indicator(指示器)

com.youth.banner 提供了 Indicator 接口,可以自定义任何形式的指示器。
示例:数字指示器

class NumberIndicator(context: Context) : FrameLayout(context), Indicator {
    private val textView = TextView(context).apply {
        setTextColor(Color.WHITE)
        textSize = 16f
    }

    init {
        addView(textView, LayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity.BOTTOM or Gravity.END))
    }

    override fun onPageChanged(count: Int, current: Int) {
        textView.text = "${current + 1}/$count"
    }
}

使用

binding.banner.indicator = NumberIndicator(this)

5.自定义轮播 View(扩展 Banner)

如果你要对整个 Banner 行为进行自定义,例如:
• 改变滑动速度
• 自定义过渡动画
• 改变布局结构(例如添加渐变蒙层)

可以继承 Banner<T, Adapter>:

class MyCustomBanner @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : Banner<BannerData, MyBannerAdapter>(context, attrs) {

    init {
        // 可以自定义ViewPager2的各种属性
        (viewPager2.getChildAt(0) as RecyclerView).apply {
            // 自定义切换动画或者间距
            setPadding(40, 0, 40, 0)
            clipToPadding = false
        }
    }
}

6.常见扩展点

6.1. 自定义过渡动画

banner.setPageTransformer(ZoomOutPageTransformer())

6.2. 监听点击事件

banner.adapter.setOnBannerListener { data, position ->
    Toast.makeText(context, "点击了第$position个", Toast.LENGTH_SHORT).show()
}

6.3. 自定义轮播时长

banner.setLoopTime(5000) // 5秒

6.4. 手动开始/停止轮播

banner.start()
banner.stop()

7.源码内部原理简析

• 核心是 ViewPager2 + RecyclerView
• 数据由 BannerAdapter 驱动
• 无限循环是通过 虚拟 position 映射 实现(如 1000 * size)
• 生命周期感知
• Banner 内部实现了 LifecycleObserver:

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun start()

@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun stop()

• 所以放在 Fragment/Activity 内无需手动管理轮播暂停。

8.实例

 mBinding.bannerBackpack.setAdapter(
                    BackpackBannerAdapter().apply { this.setDatas(it) })
                    .addBannerLifecycleObserver(this) //添加生命周期观察者
                    .setIndicator(RectangleIndicator(mContext))
                    .setIndicatorGravity(IndicatorConfig.Direction.RIGHT)
                    .setIndicatorSelectedColor("#FFFFFFFF".toColorInt())
                    .setIndicatorNormalColor("#80FFFFFF".toColorInt())
                    .setIndicatorRadius(UiUtil.dp2Px(3, mContext))
                    .setIndicatorHeight(UiUtil.dp2Px(6, mContext))
                    .setIndicatorWidth(UiUtil.dp2Px(6, mContext), UiUtil.dp2Px(15, mContext))
                    .setOnBannerListener { data, position ->
                        if (data is BannerResult) {
                            if (data.target == 0) {
                                return@setOnBannerListener
                            }
                            if (data.jump?.startsWith("http") == true) {
                                WebViewActivity.actionStart(this@BackpackActivityNew, data.title ?: "", data.jump ?: "", hideHead = false, finish = false)
                            } else {
                                SchemeFilterActivity.openSchemeFilterActivity(mContext, data.jump ?: return@setOnBannerListener)
                            }
                        }
                    }

总结

在 com.youth.banner 中,自定义 View 的方式主要有:
1. 自定义 Item Layout(通过 Adapter + ViewHolder)
2. 自定义 Indicator(实现 Indicator 接口)
3. 自定义 Banner 容器行为(继承 Banner 类,修改 ViewPager2 行为)

大多数需求只需要 自定义 Adapter + 自定义 item_layout 就可以实现复杂效果,比如卡片式、带按钮的轮播页。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容