|
@@ -7,21 +7,22 @@ import android.util.Log
|
|
|
import android.view.LayoutInflater
|
|
|
import android.view.View
|
|
|
import android.view.ViewGroup
|
|
|
+import android.view.ViewTreeObserver
|
|
|
import android.widget.LinearLayout
|
|
|
import androidx.core.view.isVisible
|
|
|
import androidx.lifecycle.findViewTreeLifecycleOwner
|
|
|
import androidx.lifecycle.lifecycleScope
|
|
|
import com.composition.android.lib.ad.AdLoad
|
|
|
-import com.composition.android.lib.ad.AdManager
|
|
|
import com.composition.android.lib.ad.AdUnitConfigManager
|
|
|
import com.composition.android.lib.ad.R
|
|
|
-import com.composition.android.lib.ad.basic.AdResult
|
|
|
-import com.composition.android.lib.ad.basic.Advertisers
|
|
|
-import com.composition.android.lib.ad.basic.NativeAdViewHolder
|
|
|
-import com.composition.android.lib.ad.basic.buildAdNativeViewHolder
|
|
|
+import com.composition.android.lib.ad.basic.*
|
|
|
+import com.composition.android.lib.ad.bean.AdUnitBean
|
|
|
import com.composition.android.lib.ad.impl.NormalAdListener
|
|
|
+import com.composition.android.lib.ad.interfaces.AdSize
|
|
|
import com.composition.android.lib.ad.util.adLogE
|
|
|
+import com.composition.android.lib.ad.util.adLogWithFrame
|
|
|
import com.composition.android.lib.ad.util.getActivityFromView
|
|
|
+import com.composition.android.lib.ad.util.getLayoutResId
|
|
|
import kotlinx.coroutines.flow.firstOrNull
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
|
@@ -29,34 +30,9 @@ import kotlinx.coroutines.launch
|
|
|
* @classname:
|
|
|
* @author: LiuXiaoLong
|
|
|
* @date: 2022/9/7
|
|
|
- * description: 自定义的原生广告View, 通过在xml中的配置,view中进行加载广告并展示
|
|
|
- *
|
|
|
- * <NativeAdView
|
|
|
- * //对应的NativeAdViewHolder view配置,设置view的id..还有其他的,具体查看attr.xml/NativeAdView
|
|
|
- * app:ad_view_title="@id/tv_ad_title"
|
|
|
- * app:ad_view_title_desc="..."
|
|
|
- *
|
|
|
- * //不同广告厂商的原生广告根布局,在每个库中都会默认定义一个,或者自行添加
|
|
|
- * admob : layout_admob_native_ad_view_root.xml gms.NativeAdView
|
|
|
- *
|
|
|
- * app:native_admob_advertisers_root_view="@layout/layout_admob_native_ad_view_root"
|
|
|
- * app:native_csj_advertisers_root_view="xxx"
|
|
|
- * app:native_app_lovin_advertisers_root_view="xxx"
|
|
|
- * app:native_custom_advertisers_root_view="xxx"
|
|
|
- *
|
|
|
- * >
|
|
|
- *
|
|
|
- * //具体的原生广告view布局
|
|
|
- * <TextView
|
|
|
- * android:id="@+id/tv_ad_title"
|
|
|
- * />
|
|
|
- *
|
|
|
- * </NativeAdView>
|
|
|
- *
|
|
|
- *
|
|
|
- *
|
|
|
+ * description: 广告View
|
|
|
*/
|
|
|
-class NativeAdView : LinearLayout {
|
|
|
+class AdView : LinearLayout {
|
|
|
|
|
|
/**
|
|
|
* 广告位名称
|
|
@@ -86,69 +62,64 @@ class NativeAdView : LinearLayout {
|
|
|
|
|
|
private var advertisersContainerView: ViewGroup? = null
|
|
|
|
|
|
- /**
|
|
|
- * <-----------
|
|
|
- */
|
|
|
+ private var autoLoad = true
|
|
|
|
|
|
- private var autoPopulate = true
|
|
|
+ private var autoSize = false
|
|
|
|
|
|
- constructor(context: Context?) : super(context)
|
|
|
- constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
|
|
- constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
|
|
|
+ private var adLoading = false
|
|
|
+
|
|
|
+ constructor(context: Context?) : this(context, null)
|
|
|
+ constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)
|
|
|
+ constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
|
|
|
init(attrs)
|
|
|
}
|
|
|
|
|
|
+
|
|
|
private fun init(attrs: AttributeSet?) {
|
|
|
- val typedArray: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.NativeAdView)
|
|
|
+ val typedArray: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.AdView)
|
|
|
//获取到配置的广告名称
|
|
|
- adSlotName = typedArray.getString(R.styleable.NativeAdView_ad_slot_name) ?: ""
|
|
|
-
|
|
|
- val adUnitBean = AdUnitConfigManager.instance.getAdUnitBySlotName(adSlotNames = listOf(adSlotName)).getOrNull(0)
|
|
|
- if (adUnitBean == null) {
|
|
|
- adLogE(AdManager.TAG, "配置的广告位数据不存在, 当前广告位:${adSlotName}")
|
|
|
- }
|
|
|
- when (adUnitBean?.getAdvertisers()) {
|
|
|
- Advertisers.Admob -> attachedAdvertisersNativeAdViewToRoot(typedArray, R.styleable.NativeAdView_ad_admob_native_ad_view_layout)
|
|
|
- Advertisers.CSJ, Advertisers.AppLovinMax -> advertisersContainerView = this
|
|
|
- Advertisers.GroMore -> attachedAdvertisersNativeAdViewToRoot(typedArray, R.styleable.NativeAdView_ad_gro_more_tt_native_ad_view_layout)
|
|
|
- Advertisers.CUSTOM -> advertisersContainerView = this
|
|
|
- else -> {}
|
|
|
+ adSlotName = typedArray.getString(R.styleable.AdView_ad_slot_name) ?: ""
|
|
|
+ var adUnitBean = AdUnitConfigManager.instance.getAdUnitBySlotName(listOf(adSlotName))
|
|
|
+ if (adUnitBean.isNullOrEmpty().not()) {
|
|
|
+ initAdContainerView(adUnitBean[0])
|
|
|
}
|
|
|
|
|
|
- adTitleViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_title_id, 0)
|
|
|
- adTitleDescViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_title_desc_id, 0)
|
|
|
- adIconViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_icon_id, 0)
|
|
|
- adMediaContentViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_media_content_group_id, 0)
|
|
|
- adCallToActionViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_call_to_action_id, 0)
|
|
|
- adAdvertisersLogoViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_advertisers_logo_id, 0)
|
|
|
- adCloseIconViewId = typedArray.getResourceId(R.styleable.NativeAdView_ad_view_close_ad_icon_id, 0)
|
|
|
- autoPopulate = typedArray.getBoolean(R.styleable.NativeAdView_ad_auto_populate, true)
|
|
|
+ autoLoad = typedArray.getBoolean(R.styleable.AdView_ad_auto_load, true)
|
|
|
+ autoSize = typedArray.getBoolean(R.styleable.AdView_ad_auto_size, false)
|
|
|
+ adTitleViewId = typedArray.getResourceId(R.styleable.AdView_ad_view_title_id, 0)
|
|
|
+ adTitleDescViewId = typedArray.getResourceId(R.styleable.AdView_ad_view_title_desc_id, 0)
|
|
|
+ adIconViewId = typedArray.getResourceId(R.styleable.AdView_ad_view_icon_id, 0)
|
|
|
+ adMediaContentViewId = typedArray.getResourceId(R.styleable.AdView_ad_view_media_content_group_id, 0)
|
|
|
+ adCallToActionViewId = typedArray.getResourceId(R.styleable.AdView_ad_view_call_to_action_id, 0)
|
|
|
+ adAdvertisersLogoViewId = typedArray.getResourceId(R.styleable.AdView_ad_view_advertisers_logo_id, 0)
|
|
|
+ adCloseIconViewId = typedArray.getResourceId(R.styleable.AdView_ad_view_close_ad_icon_id, 0)
|
|
|
|
|
|
typedArray.recycle()
|
|
|
}
|
|
|
|
|
|
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
|
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
|
|
|
|
- override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
|
|
|
|
|
|
- val root = getChildAt(0)
|
|
|
- if (childCount != 0 && root is ViewGroup) {
|
|
|
- //这里会把xml 中写的自定义布局加入进来
|
|
|
- (getChildAt(0) as ViewGroup).addView(child, index, params)
|
|
|
- Log.e("测试", "if true, child:${child?.javaClass?.simpleName}")
|
|
|
- } else {
|
|
|
- //把广告商的View先加进来
|
|
|
- super.addView(child, index, params)
|
|
|
- Log.e("测试", "if else, child:${child?.javaClass?.simpleName}")
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
+ override fun onFinishInflate() {
|
|
|
+ super.onFinishInflate()
|
|
|
+ viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener{
|
|
|
+ override fun onGlobalLayout() {
|
|
|
+ adLogWithFrame(msg = "AdView size :${measuredWidth},${measuredHeight}")
|
|
|
+ viewTreeObserver.removeOnGlobalLayoutListener(this)
|
|
|
+ if (autoLoad && !adLoading) {
|
|
|
+ loadAd()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
|
|
|
override fun onAttachedToWindow() {
|
|
|
super.onAttachedToWindow()
|
|
|
initViewHolder()
|
|
|
- if (autoPopulate) {
|
|
|
- loadAd()
|
|
|
- }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
override fun onDetachedFromWindow() {
|
|
@@ -168,67 +139,128 @@ class NativeAdView : LinearLayout {
|
|
|
this.advertiserLogoImageView = findViewById(adAdvertisersLogoViewId)
|
|
|
this.dislikeView = findViewById(adCloseIconViewId)
|
|
|
this.activity = getActivityFromView()
|
|
|
- this.adListener = object : NormalAdListener() {
|
|
|
- override fun onAdDisLike() {
|
|
|
- super.onAdDisLike()
|
|
|
- isVisible = false
|
|
|
- advertisersContainerView?.removeAllViews()
|
|
|
+ this.adListener = this@AdView.adListener
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ viewHolder?.adListener = this@AdView.adListener
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun initAdContainerView(adUnitBean: AdUnitBean?) {
|
|
|
+ if (advertisersContainerView != null) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ when (adUnitBean?.getAdvertisers()) {
|
|
|
+ Advertisers.Admob -> {
|
|
|
+ when (adUnitBean.getAdType()) {
|
|
|
+ AdType.NATIVE -> {
|
|
|
+ parseAdRootView("layout_admob_native_ad_view_root")
|
|
|
+ }
|
|
|
+ else -> {
|
|
|
+ advertisersContainerView = this
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ Advertisers.CSJ, Advertisers.AppLovinMax -> advertisersContainerView = this
|
|
|
+ Advertisers.GroMore -> {
|
|
|
+ advertisersContainerView = this
|
|
|
+ }
|
|
|
+ else -> {
|
|
|
+ advertisersContainerView = this
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
|
|
|
+ val root = getChildAt(0)
|
|
|
+ if (childCount != 0 && root is ViewGroup) {
|
|
|
+ //这里会把xml 中写的自定义布局加入进来
|
|
|
+ (getChildAt(0) as ViewGroup).addView(child, index, params)
|
|
|
+ Log.e("测试", "if true, child:${child?.javaClass?.simpleName}")
|
|
|
+ } else {
|
|
|
+ //把广告商的View先加进来
|
|
|
+ super.addView(child, index, params)
|
|
|
+ Log.e("测试", "if else, child:${child?.javaClass?.simpleName}")
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private fun attachedAdvertisersNativeAdViewToRoot(typedArray: TypedArray, id: Int) {
|
|
|
- typedArray.getResourceId(id, -1).let {
|
|
|
- if (it != -1) {
|
|
|
- Log.e("测试", "开始解析厂商布局")
|
|
|
- LayoutInflater.from(context).inflate(it, this, true) as ViewGroup
|
|
|
+
|
|
|
+ private fun parseAdRootView(layoutName: String) {
|
|
|
+ try {
|
|
|
+ adLogWithFrame(msg = "AdView 开始获取:${layoutName}.xml")
|
|
|
+ val layoutResId = context.getLayoutResId(layoutName)
|
|
|
+ if (layoutResId != -1) {
|
|
|
+ adLogWithFrame(msg = "AdView ${layoutName}.xml 解析成功")
|
|
|
+ LayoutInflater.from(context).inflate(layoutResId, this, true) as ViewGroup
|
|
|
val root = getChildAt(0) as ViewGroup
|
|
|
advertisersContainerView = root
|
|
|
- Log.e("测试", "厂商布局解析完成: root${root.javaClass.simpleName}")
|
|
|
+ adLogWithFrame(msg = "AdView 获取成功 root: ${root.javaClass.simpleName}")
|
|
|
+ } else {
|
|
|
+ adLogWithFrame(msg = "AdView ${layoutName}.xml 解析失败,请确认该layout是否存在")
|
|
|
}
|
|
|
+
|
|
|
+ } catch (e: Exception) {
|
|
|
+ adLogWithFrame(msg = "AdView ${layoutName}.xml 解析失败")
|
|
|
+ e.printStackTrace()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
fun loadAd() {
|
|
|
findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
|
|
|
destroyAd()
|
|
|
- adResult = AdLoad.loadAd(context, adSlotName).firstOrNull()
|
|
|
+ var adUnits = AdUnitConfigManager.instance.getAdUnitBySlotName(listOf(adSlotName))
|
|
|
+ if (autoSize) {
|
|
|
+ if (measuredWidth > 0 && measuredHeight > 0) {
|
|
|
+ adLogWithFrame(msg = "AdView 使用AutoSize : (${measuredWidth} * ${measuredHeight})")
|
|
|
+ for (adUnit in adUnits) {
|
|
|
+ adUnit.adSize = AdSize(measuredWidth, measuredHeight)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ adLoading = true
|
|
|
+ adResult = AdLoad.loadAd(context, adUnits).firstOrNull()
|
|
|
+ adLoading = false
|
|
|
initViewHolder()
|
|
|
if (adResult != null && adResult is AdResult.Success && viewHolder != null && advertisersContainerView != null) {
|
|
|
AdLoad.populateNativeAdView(context, adResult as AdResult.Success, viewHolder!!, advertisersContainerView!!)
|
|
|
isVisible = true
|
|
|
}
|
|
|
- if (advertisersContainerView == null){
|
|
|
- adLogE(tag = "原生广告NativeAdView", msg = "厂商的原生广告容器为null, 请指定 ad_admob_native_ad_view_layout 或 其他属性")
|
|
|
+ if (advertisersContainerView == null) {
|
|
|
+ adLogE(tag = "原生广告NativeAdView", msg = "厂商的原生广告容器为null")
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
private fun destroyAd() {
|
|
|
AdLoad.destroyAd(adResult)
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* 可以不使用View中的自动加载,在这里进行自行填充,非常适用于在RecyclerView中使用
|
|
|
*/
|
|
|
- fun populateNativeAd(adResult: AdResult.Success) {
|
|
|
+ fun populateAd(adResult: AdResult.Success) {
|
|
|
+ initAdContainerView(adResult.adBean)
|
|
|
initViewHolder()
|
|
|
if (viewHolder != null && advertisersContainerView != null) {
|
|
|
AdLoad.populateNativeAdView(context, adResult, viewHolder!!, advertisersContainerView!!)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private var adListener : NormalAdListener? = object : NormalAdListener(){
|
|
|
+ override fun onAdDisLike() {
|
|
|
+ removeAd()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public fun setAdListener(adListener: NormalAdListener.()->Unit){
|
|
|
+ this.adListener = NormalAdListener().also(adListener)
|
|
|
+ }
|
|
|
|
|
|
- fun setAdSlotNameAndLoadAd(adSlotName: String) {
|
|
|
+ fun loadAd(adSlotName: String) {
|
|
|
this.adSlotName = adSlotName
|
|
|
loadAd()
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* 刷新广告
|
|
|
*/
|
|
@@ -238,6 +270,6 @@ class NativeAdView : LinearLayout {
|
|
|
|
|
|
fun removeAd() {
|
|
|
isVisible = false
|
|
|
+ destroyAd()
|
|
|
}
|
|
|
-
|
|
|
}
|