Expected
The MoPubRecyclerAdapter is expected to inflate native Facebook RecyclerView cells using a defined ConstraintLayout.
Observed
Error
The MoPubRecyclerAdapter is crashing intermittently for ConstraintLayouts created from Facebook native ads. This issue has been noted in the MoPub SDK forum and MoPub Android Mediation GitHub repository.
Log
Fatal Exception: java.lang.ClassCastException: androidx.constraintlayout.widget.ConstraintLayout cannot be cast to android.widget.RelativeLayout
at com.mopub.nativeads.FacebookAdRenderer$FacebookNativeViewHolder.fromViewBinder(FacebookAdRenderer.java:139)
at com.mopub.nativeads.FacebookAdRenderer.renderAdView(FacebookAdRenderer.java:58)
at com.mopub.nativeads.FacebookAdRenderer.renderAdView(FacebookAdRenderer.java:28)
at com.mopub.nativeads.NativeAd.renderAdView(NativeAd.java:166)
at com.mopub.nativeads.MoPubStreamAdPlacer.bindAdView(MoPubStreamAdPlacer.java:433)
at com.mopub.nativeads.MoPubRecyclerAdapter.onBindViewHolder(MoPubRecyclerAdapter.java:424)
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:6781)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:6823)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:5752)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6019)
at androidx.recyclerview.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:286)
at androidx.recyclerview.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:343)
at androidx.recyclerview.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:359)
at androidx.recyclerview.widget.GapWorker.prefetch(GapWorker.java:366)
at androidx.recyclerview.widget.GapWorker.run(GapWorker.java:397)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:205)
at android.app.ActivityThread.main(ActivityThread.java:6991)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:884)
Implementation
facebook_native_ad_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/native_outer_view"
style="#style/AdContentCardStyle"
android:layout_width="match_parent"
android:layout_height="#dimen/cell_content_feed_height"
android:textDirection="locale">
<TextView
android:id="#+id/native_title"
style="#style/CellCreatorStyle"
app:layout_constraintBottom_toBottomOf="#+id/guideline"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="#id/native_icon_image" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="#id/native_media_view"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/sponsored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/sponsored"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="#id/guideline" />
<com.facebook.ads.AdIconView
android:id="#+id/native_icon_image"
android:layout_width="#dimen/native_icon_image_dimen"
android:layout_height="#dimen/native_icon_image_dimen"
android:paddingRight="#dimen/padding_tiny"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.facebook.ads.MediaView
android:id="#+id/native_media_view"
style="#style/AdCellPreviewImageStyle"
android:contentDescription="#string/native_main_image"
app:layout_constraintBottom_toTopOf="#id/native_text"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#id/sponsored" />
<TextView
android:id="#+id/native_text"
style="#style/CellTitleStyle"
app:layout_constraintBottom_toTopOf="#id/native_cta"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="#id/native_media_view"
tools:text="#string/learn_more" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/native_ad_choices_relative_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="left"
app:layout_constraintBottom_toBottomOf="#id/native_cta"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="#id/native_cta" />
<TextView
android:id="#+id/native_cta"
style="#style/NativeCtaStyle"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#id/native_text"
tools:text="#string/learn_more" />
</androidx.constraintlayout.widget.ConstraintLayout>
SomeFragment.kt
adapter = FeedAdapter(feedViewModel, viewEvent)
moPubAdapter = MoPubRecyclerAdapter(
requireActivity(),
adapter,
MoPubNativeAdPositioning.MoPubServerPositioning())
moPubAdapter.registerAdRenderer(FacebookAdRenderer(
FacebookViewBinder.Builder(fb_native_ad_item)
.titleId(native_title)
.textId(native_text)
.mediaViewId(native_media_view)
.adIconViewId(native_icon_image)
.adChoicesRelativeLayoutId(native_ad_choices_relative_layout)
.advertiserNameId(native_title)
.callToActionId(native_cta)
.build()))
val viewBinder = ViewBinder.Builder(native_ad_item)
.titleId(native_title)
.textId(native_text)
.mainImageId(R.id.native_main_image)
.iconImageId(native_icon_image)
.callToActionId(native_cta)
.privacyInformationIconImageId(string.native_privacy_information_icon_image)
.build()
moPubAdapter.registerAdRenderer(FlurryNativeAdRenderer(FlurryViewBinder(Builder(viewBinder))))
moPubAdapter.registerAdRenderer(MoPubVideoNativeAdRenderer(
MediaViewBinder.Builder(fb_native_ad_item)
.mediaLayoutId(native_media_view)
.iconImageId(native_icon_image)
.titleId(native_title)
.textId(native_text)
.privacyInformationIconImageId(native_ad_choices_relative_layout)
.build()))
moPubAdapter.registerAdRenderer(MoPubStaticNativeAdRenderer(viewBinder))
moPubAdapter.setContentChangeStrategy(MOVE_ALL_ADS_WITH_CONTENT)
contentRecyclerView.adapter = moPubAdapter
FeedApater.kt
#ExperimentalCoroutinesApi
class FeedAdapter(val viewModel: FeedViewModel, val viewEvent: FeedViewEvent)
: PagedListAdapter<Content, FeedAdapter.ViewHolder>(DIFF_CALLBACK) {
class ViewHolder(private var binding: CellContentBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(viewModel: FeedViewModel, content: Content, onClickListener: OnClickListener) {
binding.viewModel = viewModel
binding.data = content
binding.clickListener = onClickListener
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = CellContentBinding.inflate(inflater, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
getItem(position)?.let { content ->
holder.bind(viewModel, content, createOnClickListener(content, position))
}
}
private fun createOnClickListener(content: Content, position: Int) = OnClickListener { view ->
}
Environments
Libraries
implementation("com.mopub:mopub-sdk-native-static:5.11.1#aar") { transitive = true }
implementation("com.mopub:mopub-sdk-native-video:5.11.1#aar") { transitive = true }
implementation 'com.facebook.android:audience-network-sdk:5.1.0'
implementation 'com.mopub.mediation:facebookaudiencenetwork:5.1.0.0'
implementation 'com.flurry.android:ads:12.1.0#aar'
implementation 'com.flurry.android:analytics:12.1.0#aar'
implementation 'com.mopub.mediation:flurry:11.4.0.0'
Android levels
8.1.0
9
10
Devices
LG Q60
Pixel 3a
Redmi Note 5 Pro
Attempted solution
The library versions have been updated for the MoPub SDK to 5.12.0, the Facebook Audience Network to 5.8.0, and Facebook mediation to 5.8.0.0. It is to be determined whether this resolves the above crash.
implementation("com.mopub:mopub-sdk-native-static:5.12.0") { transitive = true }
implementation("com.mopub:mopub-sdk-native-video:5.12.0") { transitive = true }
implementation 'com.facebook.android:audience-network-sdk:5.8.0'
implementation 'com.mopub.mediation:facebookaudiencenetwork:5.8.0.0'
Use RelativeLayout instead of ConstraintLayout
As MoPub's engineer points out in this GitHub issue, Facebook mediation is not yet compatible with ConstraintLayout.
AdChoices icon XML view (with the ID native_ad_choices_relative_layout) needs to be a RelativeLayout due to internal changes in the 4.99.0+ version of the Facebook Audience Network SDK. The adapter expects a RelativeLayout here.
Before
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/native_ad_choices_relative_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="left"
app:layout_constraintBottom_toBottomOf="#id/native_cta"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="#id/native_cta" />
After
<RelativeLayout
android:id="#+id/native_ad_choices_relative_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="left" />
Documentation: Setup Ad Renderers for Native Ads
Sample: github.com/mopub/mopub-sdk-android
Related
my application is giving the below error that you mentioned above and it says there is an error on line 69, but you have tried all the solutions and could not fix it. You are new to programming and trying to learn, so you are asking for help from experienced people like us.
Can you please help me?
Error Message:
AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.kotlincountries, PID: 15240
kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized
at com.example.kotlincountries.adapter.CountryAdapter.onCountryClicked(CountryAdapter.kt:69)
at com.example.kotlincountries.databinding.ItemCountryBindingImpl$OnClickListenerImpl.onClick(ItemCountryBindingImpl.java:173)
at android.view.View.performClick(View.java:7506)
at android.view.View.performClickInternal(View.java:7483)
at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
at android.view.View$PerformClick.run(View.java:29334)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
I have enabled two options in my gradle file.
buildFeatures {
viewBinding = true
dataBinding = true
}
I checked the connections in the XML file.
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="country"
type="com.example.kotlincountries.model.Country" />
<variable
name="listener"
type="com.example.kotlincountries.adapter.CountryClickListener" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="150dp"
android:onClick="#{listener::onCountryClicked}"
android:orientation="horizontal">
<TextView
android:id="#+id/countryUuidText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="#{String.valueOf(country.uuid)}">
</TextView>
<ImageView
android:id="#+id/imageView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:padding="3dp"
android:downloadUrl="#{country.imageUrl}">
</ImageView>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_vertical"
android:layout_weight="3">
<TextView
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{country.countryName}"
android:padding="5dp"
android:textSize="18sp"
android:textStyle="bold">
</TextView>
<TextView
android:id="#+id/region"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{country.countryRegion}"
android:textSize="16sp"
android:padding="5dp">
</TextView>
</LinearLayout>
</LinearLayout>
</layout>
I checked the data I fetched in my adapter class.
class CountryAdapter(val countryList:ArrayList<Country>): RecyclerView.Adapter<CountryAdapter.CountryViewHolder>(),CountryClickListener {
private lateinit var binding : ItemCountryBinding
class CountryViewHolder(var view:ItemCountryBinding) :RecyclerView.ViewHolder(view.root) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CountryViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ItemCountryBinding.inflate(inflater, parent, false)
return CountryViewHolder(binding)
}
override fun getItemCount(): Int {
return countryList.size
}
override fun onBindViewHolder(holder: CountryViewHolder, position: Int) {
holder.view.country=countryList[position]
holder.view.listener=this
}
fun updateCountryList(newCountryList:List<Country>){
countryList.clear()
countryList.addAll(newCountryList)
notifyDataSetChanged()
}
override fun onCountryClicked(v: View) {
val uuid=binding.countryUuidText.text.toString().toInt()
val action=FeedFragmentDirections.actionFeedFragmentToCountryFragment(uuid)
Navigation.findNavController(v).navigate(action)
}
}
Could you please help me? I couldn't find a solution even though I checked the connections in my XML file, verified the data in my adapter class.
Error message is clear that binding variable which is declared as lateinit is not initialised and tried to use it in your adapter class .
and if you will check your code of adapter (as location mentioned in error) you will get that problem is in onCreateViewHolder
So just change in onCreateViewHolder method of adapter
From
val binding = ItemCountryBinding.inflate(inflater, parent, false)
To
binding = ItemCountryBinding.inflate(inflater, parent, false)
i have just removed val as you have already declared binding
I'm a Kotlin newbie learning how to create simple recyclerview apps. My code is supposed to list the integers 1..10 in vertically stacked cells. However, it only lists the first item. I've consulted several tutorials and reviewed my code several times(after long breaks), but I can't see anything wrong in my code.
I got the bright idea early today to print Log statements. Examining them, I note that my onBindViewHolder function is only called once. What blunder am I making?
Here is my log output:
D/QuoteAdapter: value is: 1
D/QuoteAdapter: index is: 0
D/QuoteAdapter: Size is: 10
my activity:
class MainActivity : AppCompatActivity() {
lateinit var mRecyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mRecyclerView = findViewById(R.id.recyclerView)
mRecyclerView.layoutManager = LinearLayoutManager(this)
mRecyclerView.adapter = QuoteAdapter()
//mRecyclerView.setHasFixedSize(true)
}
}
my adapter:
class QuoteAdapter : RecyclerView.Adapter<QuoteViewHolder>() {
private val listOfInts = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
private val TAG = "QuoteAdapter"
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuoteViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.recyclerview_item_row, parent, false)
return QuoteViewHolder(view)
}
override fun getItemCount(): Int {
Log.d(TAG, "Size is: ${listOfInts.size.toString()}")
return listOfInts.size
}
override fun onBindViewHolder(holder: QuoteViewHolder, position: Int) {
val item = listOfInts[position]
Log.d(TAG, "value is: ${item.toString()}")
Log.d(TAG, "index is: ${position.toString()}")
holder.listTitle.text = item.toString()
}
}
my viewholder:
class QuoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val listTitle = itemView.findViewById(R.id.itemString) as TextView
}
my layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
my main layout is shown below:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
In your "my layout" try this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Notice layout_height of LinearLayout has changed to wrap_content
Also doubt you need the android:orientation="vertical" on your ViewHolders item xml unless you will add more than just 1 TextView in the future.
Like Zain says, you can just use a TextView on its own in a layout file, which will also fix the problem (so long as its height is wrap_content!)
There are actually a few included with Android - type android.R.layout. and you'll see a few things, like simple_list_item_1 which is just a styled TextView (you can ctrl+click the reference or whatever to look at the file). Can be nice if you just want to make a quick thing!
The ID of the TextView in android.R.layout.simple_list_item_1 is #android:id/text1 - note the android prefix, because its part of the android resources, not your app's. Which means you have to reference the ID in the same way as the layout, with android at the front: android.R.id.text1
You can get rid of the LinearLayout in the list item layout, and only keep the TextView.
So, replace your list item layout with:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/itemString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
I am making a RecyclerView with Kotlin that will display an image and text. So far the text should be just "Textview" and the image should be one of the images that come with android. The text displays "Textview" the specified number of times but doesn't show the image at all.
I am actually following [this video](https://www.youtube.com/watch?v=jS0buQyfJfs&list=PL0dzCUj1L5JGfHj1lwxOq67zAJV3e1S9S&index=2&t=409s! exactly and run into the problem.
I have changed the cell layout of the image to wrap content and it did not help.
Adapter
class MainAdapter : RecyclerView.Adapter<CustomViewHolder>() {
val vidTitles = listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
override fun getItemCount(): Int {
return vidTitles.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
//create the view for the rows
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.video_row, parent, false)
return CustomViewHolder(cellForRow)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
holder?.view?.videoTitleTextView?.text= vidTitles[position]
}
}
class CustomViewHolder(val view: View): RecyclerView.ViewHolder(view) {
}
RecyclerView's Cell layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/colorAccent"
android:contentDescription="avatar image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="#mipmap/ic_launcher" />
<TextView
android:id="#+id/videoTitleTextView"
android:layout_width="0dp"
android:layout_height="31dp"
android:text="animals"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageView" />
</androidx.constraintlayout.widget.ConstraintLayout>
Main Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//recyclerView_main.setBackgroundColor(Color.CYAN)
recyclerView_main.layoutManager = LinearLayoutManager(this)
recyclerView_main.adapter = MainAdapter()
}
}
I expect it to show both the image and the text; It shows the text but not any image and it doesn't even leave any space for the image.
Change Your Image View as below.
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/colorAccent"
android:contentDescription="avatar image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="#mipmap/ic_launcher" />
You are using tools:srcCompat which is wrong.
Also Change height of Constraint Layout to wrap_content. Otherwise each item in a Recyclerview is going to take full height of screen.
Tools namespace is used for Compile time behaviour. When you build the app this tools features is removed. Its helps you to preview your view at compile time.
Replace this
tools:srcCompat="#mipmap/ic_launcher"
with this:
android:src="#mipmap/ic_launcher"
Note: Here tools:srcCompat will show only in the preview of Android studio but not in the real device.
For more info check here
When I read your code right, you should see "animals" in your TextViews only :)
This is because in your cell's layout file you set the android:text and not the tools:text attribute.
Just implement your ViewHolder and bind the Views to your model.
If you want only to verify your Views are loaded, you could replace the
tools:srcCompat="#mipmap/ic_launcher"
to
app:srcCompat="#mipmap/ic_launcher"
I want to combine imageview with data bindingadapter. I was searching Google about my problem. The problem is databindingadapter doesn't work in my XML.
To put it easy, other views recognize well such as textView. If I enter a variable name at ImageView, the color should change but it will not change.
Here is my code
<data class> (databindingadapter)
import android.net.Uri
import android.widget.ImageView
import androidx.databinding.BindingAdapter
import com.squareup.picasso.Picasso
class Person {
val name = ""
val age = ""
var image = ""
}
object imageBindingAdapter {
val person = Person()
#JvmStatic
#BindingAdapter("image")
fun bindImage(imageView: ImageView) {
Picasso.get().load(Uri.parse(person.image))
.fit().centerCrop().into(imageView)
}
}
XML code
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable name="person"
type="com.example.kotlinerecyclerview.Person"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<ImageView android:layout_width="100dp" android:layout_height="100dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="#android:drawable/btn_default"
app:image="person.image"
/>
<TextView
android:text="#{person.name}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tv_name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:text="#{String.valueOf(person.age)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#+id/tv_name"
app:layout_constraintStart_toStartOf="parent"
android:id="#+id/tv_age"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Here is my recyclerview adapter class
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.kotlinerecyclerview.databinding.ItemBinding
class ListAdapter(val items: List<Person>, private val clickListener:
(person: Person) -> Unit) :
RecyclerView.Adapter<ListAdapter.SampleViewHolder>() {
class SampleViewHolder(val binding: ItemBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
SampleViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item, parent, false)
val holder = SampleViewHolder(ItemBinding.bind(view))
view.setOnClickListener {
clickListener.invoke(items[holder.adapterPosition])
}
return holder
}
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: SampleViewHolder, position: Int) {
holder.binding.person = items[position]`enter code here`
}
}
I moved this code to another class ex) mainactivity, listadapter
but it doesn't work.
I wrote the code as described in other videos or documents about databindingadapter.
Try to pass the Image Url also to the Binding Adapter
#JvmStatic
#BindingAdapter("image")
fun bindImage(imageView: ImageView, url: String) {
Picasso.get().load(url)
.fit().centerCrop().into(imageView)
}
Also change your xml as given below
<ImageView android:layout_width="100dp" android:layout_height="100dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="#android:drawable/btn_default"
app:image="#{person.image}"
/>
Check this Guide also
First workaround:
Move binding adapter in separate file and add "app:"
ImageViewBindings.java
#BindingAdapter("app:image")
fun setImage(view: ImageView, url: String) {
Picasso.get().load(url).fit().centerCrop().into(view)
}
In
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="#android:drawable/btn_default"
app:image ="person.image"/>
android:src and app:image
try to do the same thing. It can be a reason of conflict.
If your's intent is set multiple images, consider to separate them (for example to 2 different ImageViews).
person.image is not an ObservableField so you should use executePendingBindings like
override fun onBindViewHolder(holder: SampleViewHolder, position: Int) {
holder.binding.person = items[position]
holder.binding.executePendingBindings()
}
Second workaround:
There is possibility what your urls use http protocol, not https.
In this case this link may help: Picasso not loading image from http url but loads images from https url?
I have a Fragment that displays a list of cities with weather informations. I am using a RecyclerView and I am trying to implement the Data Binding Library in my RecyclerView Adapter but for some reason I get this compile error :
> error: cannot find symbol import
com.example.zach.weatherapp.databinding.CityListItemBindingImpl;
> ^
> symbol: class CityListItemBindingImpl
> location: package com.example.zach.weatherapp.databinding
It's an auto generated class so i really don't know where the error is. I had the same error previously for other layouts when there was someting wrong in the xml file but here it seems fine.
ForecastAdapter.kt
package com.example.zach.weatherapp.Adapter
import ...
class ForecastAdapter(var myDataset: List<City>) :
RecyclerView.Adapter<ForecastAdapter.ForecastViewHolder>() {
var context:Context?=null
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder.
class ForecastViewHolder(var binding: CityListItemBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(city: City){
binding.city = city
}
}
// Create new views (invoked by the layout manager)
override fun onCreateViewHolder(parent: ViewGroup,
viewType: Int): ForecastAdapter.ForecastViewHolder {
context = parent.context
val layoutIdForListItem = R.layout.city_list_item
val inflater = LayoutInflater.from(context)
val shouldAttachToParentImmediately = false
val binding = DataBindingUtil.inflate<CityListItemBinding>(inflater,layoutIdForListItem,parent,shouldAttachToParentImmediately)
//val view = inflater.inflate(layoutIdForListItem, parent, shouldAttachToParentImmediately)
return ForecastViewHolder(binding)
}
// Replace the contents of a view (invoked by the layout manager)
override fun onBindViewHolder(holder: ForecastViewHolder, position: Int) {
val city = myDataset[position]
holder.bind(city)
Glide.with(context)
.load("http://openweathermap.org/img/w/${city.weather[0].icon}.png")
.into(holder.binding.forceastImageView)
holder.binding.container.setOnClickListener{ view: View ->
Timber.d("Clicked on city %s",city.name)
Navigation.findNavController(view).navigate(ListFragmentDirections.actionListFragmentToForecastDetailsFragment(city.id))}
}
// Return the size of your dataset (invoked by the layout manager)
override fun getItemCount() = myDataset.size
}
city_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="city" type="com.example.zach.weatherapp.data.City"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/container">
<TextView
tools:text="Caen"
android:text="#{city.name}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="#+id/city_name_textview"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="16dp"
android:layout_marginTop="16dp" app:layout_constraintTop_toTopOf="parent"
android:fontFamily="#font/roboto_light" android:textSize="22sp" android:textStyle="bold"
android:maxLines="1" android:ellipsize="end"/>
<TextView
tools:text="Sunny"
android:text="#{city.weather[0].description}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/city_forecast_textview" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="16dp"
app:layout_constraintTop_toBottomOf="#+id/city_name_textview" android:fontFamily="#font/roboto_light"
android:layout_marginBottom="16dp" app:layout_constraintBottom_toBottomOf="parent"/>
<ImageView
android:layout_width="48dp"
android:layout_height="48dp" app:srcCompat="#drawable/sunny"
android:id="#+id/forceast_imageView" android:layout_marginTop="8dp"
app:layout_constraintTop_toTopOf="parent" android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintVertical_bias="0.562"
android:layout_marginEnd="32dp" app:layout_constraintEnd_toStartOf="#+id/temperatures_layout"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="#+id/temperatures_layout"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="16dp" android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginTop="8dp"
app:layout_constraintTop_toTopOf="parent">
<TextView
tools:text="15°"
android:text="#{city.main.temp_max}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="#+id/max_temperature_textview"
android:fontFamily="#font/roboto_light"
tools:layout_editor_absoluteY="17dp" tools:layout_editor_absoluteX="313dp"/>
<TextView
tools:text="9°"
android:text="#{city.main.temp_min}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/min_temperature_textview"
android:fontFamily="#font/roboto_light" tools:layout_editor_absoluteY="45dp"
tools:layout_editor_absoluteX="321dp" android:layout_gravity="right"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
It might just be an Android Studio error because the xml file seems fine.
UPDATE :
Error seems to come from Xml. I removed the android:text="#{city.xxx}" in my xml layout and instead updated my textViews manually in my ViewHolder bind method like so :
fun bind(boundCity: City){
with(binding){
cityNameTextview.text = boundCity.name
cityForecastTextview.text = boundCity.weather[0].description
maxTemperatureTextview.text = "${boundCity.main.temp_max}°"
minTemperatureTextview.text = "${boundCity.main.temp_min}°"
Glide.with(root.context)
.load("http://openweathermap.org/img/w/${boundCity.weather[0].icon}.png")
.into(forceastImageView)
container.setOnClickListener{ view: View ->
Timber.d("Clicked on city %s",boundCity.name)
Navigation.findNavController(view).navigate(ListFragmentDirections.actionListFragmentToForecastDetailsFragment(boundCity.id))}
}
}
And I no longer get the error. The error comes whenever I add android:text="#{city.xx}" in my textviews and bind the city variable in the bind method. I still don't know why though....
This should work for you I believe;
class ForecastAdapter(private var myDataset: List<City>) : RecyclerView.Adapter<ForecastAdapter.ForecastViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ForecastAdapter.ForecastViewHolder {
val itemBinding = CityListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ForecastViewHolder(itemBinding)
}
override fun onBindViewHolder(holder: ForecastViewHolder, position: Int) {
val city = myDataset[position]
holder.bind(city)
}
override fun getItemCount() = myDataset.size
inner class ForecastViewHolder(var binding: CityListItemBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(boundCity: City){
with(binding) {
city = boundCity
Glide.with(root.context)
.load("http://openweathermap.org/img/w/${city.weather[0].icon}.png")
.into(forceastImageView)
container.setOnClickListener { view ->
Timber.d("Clicked on city %s", city.name)
Navigation.findNavController(view).navigate(ListFragmentDirections.actionListFragmentToForecastDetailsFragment(city.id))
}
}
}
Hey you could try adding the following line after the <data> tag :
<import type="android.view.View" />
I found that worked for me when I had that error.