You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在BaseExpandableListAdapter中集成可配置的独立Google Ads?

解决BaseExpandableListAdapter中插入独立Google Ads组项的问题

问题背景

  • 需求:在BaseExpandableListAdapter中实现独立的广告视图作为组项,支持在任意偶数位置插入
  • 已遇问题:此前在父视图XML中嵌入广告视图后,广告所在组项仍可展开,不符合需求

解决方案步骤

核心思路是将广告项与普通记录项拆分为独立类型,通过合并数据源区分处理,让广告组项无对应子项,从根源避免展开问题。

1. 定义数据类型密封类

创建密封类区分普通记录和广告两种组项:

sealed class ListItemCllRcc {
    data class RecordItem(val record: RecordCllRcc) : ListItemCllRcc()
    object AdItem : ListItemCllRcc()
}

2. 修改适配器数据源

替换原单一记录列表为包含两种类型的合并列表,并实现数据合并逻辑:

var combinedList: MutableList<ListItemCllRcc> = mutableListOf()
    set(value) {
        field = value
        notifyDataSetChanged()
    }

// 合并记录与广告项的方法,可自定义插入位置规则
fun updateRecords(newRecords: List<RecordCllRcc>) {
    combinedList.clear()
    newRecords.forEachIndexed { index, record ->
        combinedList.add(ListItemCllRcc.RecordItem(record))
        // 示例:每4条记录后插入广告,如需偶数位置可改为 (index + 1) % 2 == 0
        if ((index + 1) % 4 == 0) {
            combinedList.add(ListItemCllRcc.AdItem)
        }
    }
    notifyDataSetChanged()
}

3. 重写Adapter核心方法

根据组项类型返回对应参数:

override fun getGroupCount(): Int {
    return combinedList.size
}

override fun getChildrenCount(groupPosition: Int): Int {
    // 广告项无对应子项,返回0;普通记录项返回1
    return when (combinedList[groupPosition]) {
        is ListItemCllRcc.RecordItem -> 1
        is ListItemCllRcc.AdItem -> 0
    }
}

override fun getGroup(groupPosition: Int): ListItemCllRcc {
    return combinedList[groupPosition]
}

override fun getChild(groupPosition: Int, childPosition: Int): RecordCllRcc {
    val recordItem = combinedList[groupPosition] as ListItemCllRcc.RecordItem
    return recordItem.record
}

override fun getGroupId(groupPosition: Int): Long {
    return groupPosition.toLong()
}

override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
    // 仅普通记录项的子项可选中
    return combinedList[groupPosition] is ListItemCllRcc.RecordItem
}

4. 区分加载组项视图

根据组项类型加载对应布局,广告项使用独立的广告布局:

override fun getGroupView(
    groupPosition: Int,
    isExpanded: Boolean,
    convertView: View?,
    parent: ViewGroup?
): View {
    return when (val item = combinedList[groupPosition]) {
        is ListItemCllRcc.RecordItem -> {
            // 加载普通记录项布局
            val binding: ListItemRecordCllRccBinding = if (convertView == null || convertView.tag != "record") {
                DataBindingUtil.inflate(
                    LayoutInflater.from(parent!!.context),
                    R.layout.list_item_record_cll_rcc,
                    parent,
                    false
                ).apply { tag = "record" }
            } else {
                DataBindingUtil.getBinding(convertView)!!
            }
            bindRecordItem(binding, item.record, groupPosition)
            binding.root
        }
        is ListItemCllRcc.AdItem -> {
            // 加载独立广告布局(需提前创建包含NativeAd模板的XML)
            val adBinding: ListItemAdCllRccBinding = if (convertView == null || convertView.tag != "ad") {
                DataBindingUtil.inflate(
                    LayoutInflater.from(parent!!.context),
                    R.layout.list_item_ad_cll_rcc,
                    parent,
                    false
                ).apply { tag = "ad" }
            } else {
                DataBindingUtil.getBinding(convertView)!!
            }
            loadNativeAd(adBinding)
            adBinding.root
        }
    }
}

5. 实现视图绑定与广告加载

  • 普通记录项绑定逻辑(剥离原广告代码):
private fun bindRecordItem(
    binding: ListItemRecordCllRccBinding,
    record: RecordCllRcc,
    groupPosition: Int
) {
    binding.record = record
    binding.isFirstOrLastItem = groupPosition == 0
}
  • 独立广告加载逻辑:
private fun loadNativeAd(binding: ListItemAdCllRccBinding) {
    val adLoader = AdLoader.Builder(mContext, mContext.getString(R.string.native_ad_id))
        .forNativeAd { ad: NativeAd ->
            binding.templateID.setNativeAd(ad)
        }
        .withAdListener(object : AdListener() {
            override fun onAdFailedToLoad(adError: LoadAdError) {
                Log.e("AdError", adError.message)
            }

            override fun onAdLoaded() {
                Log.e("AdStatus", "广告加载成功")
                super.onAdLoaded()
            }
        })
        .withNativeAdOptions(NativeAdOptions.Builder().build())
        .build()
    adLoader.loadAd(AdRequest.Builder().build())
}

6. 调整子视图加载逻辑

仅处理普通记录项的子视图加载:

override fun getChildView(
    groupPosition: Int,
    childPosition: Int,
    isLastChild: Boolean,
    convertView: View?,
    parent: ViewGroup
): View {
    val recordItem = combinedList[groupPosition] as ListItemCllRcc.RecordItem
    val binding: ListItemExpandedRecordBinding = if (convertView == null) {
        DataBindingUtil.inflate(
            LayoutInflater.from(parent.context),
            R.layout.list_item_expanded_record,
            parent,
            false
        )
    } else {
        DataBindingUtil.getBinding(convertView)!!
    }
    bindExpandedViewCllRcc(binding, recordItem.record)
    return binding.root
}

// 修改子视图绑定方法,直接接收Record参数
private fun bindExpandedViewCllRcc(
    binding: ListItemExpandedRecordBinding,
    record: RecordCllRcc
) {
    try {
        val file = Environment.getExternalStorageDirectory()
            .toString() + "/Call/" + record.nameCllRcc

        binding.viewModel = viewModelCllRcc
        if (viewModelCllRcc.currentRecordCllRcc.value?.nameCllRcc != record.nameCllRcc) {
            binding.record = record
            viewModelCllRcc.currentRecordCllRcc.value = record
        }

        binding.wave.setRawDataCllRcc(File(file).readBytes()) { }
        binding.wave.onProgressListenerCllRcc = object : CustomProgressListenerCllRcc() {
            override fun onProgressChanged(progress: Float, byUser: Boolean) {
                if (byUser) {
                    viewModelCllRcc.seekToCllRcc(progress, record)
                }
            }
        }
        viewModelCllRcc.currentPositionCllRcc.observe(lifecycleOwnerCllRcc) {
            binding.redLineSeekBar.translationX = (binding.wave.width * it.toFloat()) / 101.5f
        }
        viewModelCllRcc.isPlayingCllRcc.observe(lifecycleOwnerCllRcc) {
            binding.isPlaying = it
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

关键说明

  • 广告项作为独立组项存在,通过getChildrenCount返回0,让ExpandableListView不对其提供展开交互
  • 密封类确保类型安全,Adapter可精准区分不同组项的处理逻辑
  • 广告插入位置可通过updateRecords方法中的规则自由调整,满足任意位置插入需求

内容的提问来源于stack exchange,提问作者Himanshu Yadav Anshu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 04:55:23