Fragments RecyclerView Databinding Kotlin - Unresolved reference variable name - android

I was trying DataBinding on a Small Project which has some fragments and one of the fragments hosts a RecyclerView.
The child item has a DataBindingwhich I am unable to access in the RecyclerView Adapter.
How to solve this?
ShopFragment.KT
class ShopFragment : Fragment(),ShopItemsAdapter.ShopInterface {
lateinit var fragmentShopBinding: FragmentShopBinding;
lateinit var shopItemsAdapter: ShopItemsAdapter ;
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
fragmentShopBinding= FragmentShopBinding.inflate(inflater,container,false)
return fragmentShopBinding.root;
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
shopItemsAdapter = ShopItemsAdapter();
fragmentShopBinding.shopRV.adapter=shopItemsAdapter;
super.onViewCreated(view, savedInstanceState)
}
override fun addItem(productModel: ProductModel) {
TODO("Not yet implemented")
}
override fun onItemClick(productModel: ProductModel) {
TODO("Not yet implemented")
}
}
FragmentShop.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".views.ShopFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/shopRV"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
tools:listitem="#layout/shop_item"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
shopItem.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="productModel"
type="com.example.mvvm_shopping_cart.models.ProductModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<ImageView
android:id="#+id/productIV"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
app:srcCompat="#drawable/iphone" />
<TextView
style="#style/TextAppearance.MaterialComponents.Body1"
android:layout_gravity="center"
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{productModel.name}" />
<TextView
android:layout_gravity="center"
android:id="#+id/priceTV"
style="#style/TextAppearance.MaterialComponents.Caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{productModel.price.toString()}" />
<TextView
android:layout_gravity="center"
android:id="#+id/availableTV"
style="#style/TextAppearance.MaterialComponents.Caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{productModel.available? `Available` : `Out of stock`}" />
<Button
android:textAppearance="#style/TextAppearance.MaterialComponents.Caption"
style="#style/Widget.MaterialComponents.Button.TextButton"
android:id="#+id/AddBtn"
android:enabled="#{productModel.available}"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add to cart" />
</LinearLayout>
</layout>
ProductModel.kt
class ProductModel(var id : Number,var name : String,var imageUrl : String, var price : Number, var isAvailable : Boolean) {
override fun toString(): String {
return super.toString()
}
override fun equals(other: Any?): Boolean {
return super.equals(other)
}
class ShopItemCallBack : DiffUtil.ItemCallback<ProductModel>() {
override fun areItemsTheSame(oldItem: ProductModel, newItem: ProductModel): Boolean {
return newItem.id == oldItem.id;
}
override fun areContentsTheSame(oldItem: ProductModel, newItem: ProductModel): Boolean {
return newItem == oldItem;
}
}
}
ShopItemAdapter.kt
class ProductModel(var id : Number,var name : String,var imageUrl : String, var price : Number, var isAvailable : Boolean) {
override fun toString(): String {
return super.toString()
}
override fun equals(other: Any?): Boolean {
return super.equals(other)
}
class ShopItemCallBack : DiffUtil.ItemCallback<ProductModel>() {
override fun areItemsTheSame(oldItem: ProductModel, newItem: ProductModel): Boolean {
return newItem.id == oldItem.id;
}
override fun areContentsTheSame(oldItem: ProductModel, newItem: ProductModel): Boolean {
return newItem == oldItem;
}
}
}
I am unable to find the issue, please help.

Problem:
You are using the wrong generated binding class; as the FragmentShopBinding is the generated class of the fragment_shop.xml which is a fragment layout that contains the RecyclerView ifselft not RecyclerView item layout. And therefore it didn't resolve the product view.
Solution:
To fix this you need to replace the FragmentShopBinding with the generated class associated to the row item layout where it is shopItem.xml, and the generated class would be ShopItemBinding:
class ShopViewHolder(var itemView: ShopItemBinding): RecyclerView.ViewHolder(itemView.root) {
//......
}

Related

Android - Binding adapter failed to load a text

I write simple app displaying cocktails list. The problem is there's no title text but the pics is loading with picasso working just fine. I suppose the weak point in my Binding Adapter. Cause I can't just pass data value, i need to pass data?.list and I am really confused why I can't. Please help!
The app result
CocktailListFragment.kt
class CocktailListFragment : Fragment() {
private val viewModel: CocktailListViewModel by lazy {
ViewModelProvider(this).get(CocktailListViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = CocktailListFragmentBinding.inflate(inflater)
binding.lifecycleOwner = this
setHasOptionsMenu(true)
binding.cocktail = viewModel
binding.cocktailList.adapter = CocktailListAdapter()
return binding.root
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.filter_menu, menu)
super.onCreateOptionsMenu(menu, inflater)
}
}
CocktailListViewModel.kt
class CocktailListViewModel : ViewModel() {
private val _cocktails = MutableLiveData<CocktailsList>()
val cocktails: LiveData<CocktailsList>
get() = _cocktails
private var viewModelJob = Job()
private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main)
init {
getCocktails()
}
private fun getCocktails() {
coroutineScope.launch {
val getCocktailsDeferred = CocktailsApi.RETROFIT_SERVICE.getCocktailsAsync()
try {
val result = getCocktailsDeferred.await()
_cocktails.value = result
} catch (e: Exception) {
Log.d("error", "error")
}
}
}
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
}
CocktailListAdapter.kt
class CocktailListAdapter :
ListAdapter<Cocktail, CocktailListAdapter.CocktailListViewHolder>(DiffCallback) {
class CocktailListViewHolder(private var binding: CocktailItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(cocktail: Cocktail) {
binding.cocktail = cocktail
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CocktailListViewHolder {
return CocktailListViewHolder(CocktailItemBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun onBindViewHolder(holder: CocktailListViewHolder, position: Int) {
val cocktail = getItem(position)
holder.bind(cocktail)
}
companion object DiffCallback : DiffUtil.ItemCallback<Cocktail>() {
override fun areItemsTheSame(oldItem: Cocktail, newItem: Cocktail): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: Cocktail, newItem: Cocktail): Boolean {
return oldItem.id == newItem.id
}
}
}
CocktailDataClass
data class CocktailsList(
#SerializedName("drinks")
val list: List<Cocktail>
)
data class Cocktail (
#SerializedName("idDrink")
val id: String,
#SerializedName("strDrinkThumb")
val drinkImg: String,
#SerializedName("strDrink")
val drinkTitle: String
)
BindingAdapters - I think the problem is in binding adapter for list data:
#BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: CocktailsList?) {
val adapter = recyclerView.adapter as CocktailListAdapter
adapter.submitList(data?.list)
}
#BindingAdapter("strDrinkThumb")
fun bindImage(imgView: ImageView, imgUrl: String?) {
imgUrl?.let {
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
Picasso.get()
.load(imgUri)
.placeholder(R.drawable.loading_animation)
.error(R.drawable.ic_broken_image)
.into(imgView)
}
}
cocktail_list_fragemnt.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="cocktail"
type="com.example.coctaildb.cocktaillist.CocktailListViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appBarLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="30dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
style="#style/Widget.AppCompat.Toolbar"
android:background="#android:color/white"
app:menu="#menu/filter_menu"
app:title="#string/drinks"
app:titleTextColor="#android:color/black" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/cocktail_list"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/appBarLayout2"
app:listData="#{cocktail.cocktails}"
tools:listitem="#layout/cocktail_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
cocktail_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="cocktail"
type="com.example.coctaildb.network.Cocktail" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/cocktail_img"
android:layout_width="100dp"
android:layout_height="100dp"
android:maxHeight="100dp"
android:scaleType="centerCrop"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:strDrinkThumb="#{cocktail.drinkImg}"
tools:srcCompat="#tools:sample/avatars" />
<TextView
android:id="#+id/cocktail_title"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="21dp"
android:layout_marginTop="40dp"
android:layout_marginEnd="20dp"
android:fontFamily="#font/roboto"
android:text="#{cocktail.drinkTitle}"
android:textColor="#7E7E7E"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toEndOf="#+id/cocktail_img"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

How to implement RecyclerViewPager in kotlin?

I am trying to implement RecyclerViewPagerIndicator in my project, but I can't get it to work.
I work with Kotlin and AndroidX
I expect send a array with text and images from database and show in RecyclerViewPager, something like tinder.
I would suggest you to use dotsIndicator, since it
supports ViewPager 2 which is based on RecyclerView
is maintained, one you picked is not (project seems abandoned)
is written in Kotlin
Example:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/view_pager_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<com.tbuonomo.viewpagerdotsindicator.WormDotsIndicator
android:id="#+id/dots_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"/>
</LinearLayout>
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupViewPager2()
}
private fun setupViewPager2() {
val viewPager2 = findViewById<ViewPager2>(R.id.view_pager_2)
val dotsIndicator = findViewById<WormDotsIndicator>(R.id.dots_indicator)
viewPager2.adapter = PagerAdapter(this)
dotsIndicator.setViewPager2(viewPager2)
}
}
PagerAdapter.kt
class PagerAdapter(fa:FragmentActivity): FragmentStateAdapter(fa) {
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment {
return when(position){
0 -> SampleFragment.newInstance("One", "desc 1")
1 -> SampleFragment.newInstance("Two", "desc 2")
2 -> SampleFragment.newInstance("Three", "desc 3")
else -> throw Exception("No such position")
}
}
}
fragment_sample.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".SampleFragment">
<TextView
android:id="#+id/txt_header"
tools:text="Header 1"
android:textAlignment="center"
android:textSize="24sp"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/txt_desc"
tools:text="Desc 1"
android:textAlignment="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
SampleFragment.kt
class SampleFragment : Fragment() {
private var header: String? = null
private var desc: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
header = it.getString(ARG_Header)
desc = it.getString(ARG_Desc)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_sample, container, false)
setView(view)
return view
}
private fun setView(view:View){
val headerText = view.findViewById<TextView>(R.id.txt_header)
headerText.text = header
val descText = view.findViewById<TextView>(R.id.txt_desc)
descText.text = desc
}
companion object {
#JvmStatic
fun newInstance(param1: String, param2: String) =
SampleFragment().apply {
arguments = Bundle().apply {
putString(ARG_Header, param1)
putString(ARG_Desc, param2)
}
}
}
}

Why isn't 'onPropertyChanged' being called?

I'm writing an application with 2-way data binding. I can get the edit text field to update, but I'm also trying to update internal logic too. I've tried to do this by calling addOnPropertyChangedCallback(Observable.OnPropertyChangedCallback) on the binding, however despite checking notifyPropertyChanged(int) is being called by the observable, the callback isn't being called
I've tried adding the #Bindable annotation to both the getter and the setter as mentioned in this stackoverflow question but to no avail.
AddItemObservable.kt
class AddItemObservable : BaseObservable(), AddItemImmutableObservable {
private var name = ""
private var quantity: Int? = null
#Bindable
override fun getName() = name
fun setName(name: String) {
if (this.name == name) {
return
}
this.name = name
notifyPropertyChanged(BR.name)
}
#Bindable
override fun getQuantity() = quantity
fun setQuantity(quantity: Int?) {
if (this.quantity?.equals(quantity) == true) {
return
}
this.quantity = quantity
notifyPropertyChanged(BR.quantity)
}
}
AndroidView.kt
class AndroidView : Fragment() {
private lateinit var binding: FragmentAddItemBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentAddItemBinding.inflate(inflater, container, false)
binding.addItem = AddItemObservable()
binding.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
Log.i("VIEW", "updated")
}
})
binding.lifecycleOwner = this
return binding.root
}
}
fragment_add_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<import type="tk.jonathancowling.inventorytracker.additem.AddItemObservable" />
<variable
name="addItem"
type="AddItemObservable" />
</data>
<LinearLayout 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"
android:orientation="vertical">
<EditText
android:id="#+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:ems="10"
android:hint="#string/add_item_name"
android:inputType="textPersonName"
android:text="#={addItem.name}"
android:importantForAutofill="no"
tools:ignore="UnusedAttribute" />
<EditText
android:id="#+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:ems="10"
android:hint="#string/add_item_quantity"
android:inputType="number"
app:add_item_quantity="#={addItem.quantity}"
android:importantForAutofill="no"
tools:ignore="UnusedAttribute" />
</LinearLayout>
</layout>
Adapters omitted for brevity
I expect the OnPropertyChangedCallback to be called and log "updated" when notifyPropertyChanged is called (which happens when text is entered into one of the EditText).
To make it work, you need call addOnPropertyChangedCallback directly on Observable instance. For example,
val observableItem = AddItemObservable()
binding.addItem = observableItem
observableItem.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
Log.i("VIEW", "updated")
}
})

Kotlin Android Recycler view inside Fragment with Bottom Navigation not working

I am trying to develop social networking app which uses Android Jetpack Libraries but while using Navigation Component to use bottom navigation to navigate through fragments inside an activity , This Adapter throws an error at LayoutInflator() which causes app to crash
Can anyone help me through this:
My Adapter Class:
class FeedAdapter : PagedListAdapter<feed,FeedAdapter.ViewHolder>(FeedDiffCallBack()){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val userPost = LayoutInflater.from(parent.context)
.inflate(R.layout.feedrow,parent,false)
return ViewHolder(userPost)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val feedItem = getItem(position)
if(feedItem != null){
holder.bind(feedItem)
}
}
class ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView) {
//Retrieve data
private val username:TextView = itemView.post_name
private val userPic:ImageView = itemView.feedImage1
private val location:TextView = itemView.postLocation
private val time:TextView = itemView.postTime
private val post:ImageView = itemView.postImage
fun bind(feed: feed) = with(itemView){
//TODO:Bind Data with View
showFeedData(feed)
}
private fun showFeedData(feed: feed) {
username.text = feed.username
userPic.setImageURI(null)
userPic.visibility = View.GONE
location.text = feed.location
time.text = feed.timeStamp.toString()
post.setImageURI(Uri.parse(feed.mUrl))
}
}
}
class FeedDiffCallBack : DiffUtil.ItemCallback<feed>() {
override fun areItemsTheSame(oldItem:feed, newItem: feed): Boolean {
return oldItem?.id == newItem?.id
}
override fun areContentsTheSame(oldItem: feed, newItem: feed): Boolean {
return oldItem == newItem
}
}
Fragment Class:
class FeedFragment : Fragment() {
companion object {
fun newInstance() = FeedFragment()
}
private lateinit var viewModel: FeedViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.feed_fragment, container, false)
val context = getContext() ?: return view
val factory = InjectorUtils.provideViewModelFactory(context)
viewModel =
ViewModelProviders.of(this,factory).get(FeedViewModel::class.java)
val adapter = FeedAdapter()
view.findViewById<RecyclerView>(R.id.feedView).adapter = adapter
view.findViewById<RecyclerView>(R.id.feedView).layoutManager =
LinearLayoutManager(MyApplication.getContext())
subscribeUI(adapter)
return view
}
private fun subscribeUI(adapter: FeedAdapter) {
viewModel.showFeed().observe(this, object:Observer<PagedList<feed>>{
override fun onChanged(t: PagedList<feed>?) {
adapter.submitList(t)
adapter.notifyDataSetChanged()
}
})
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
}
}
feed_row.xml
Individual item for recycler view -->
<RelativeLayout
android:id="#+id/postContainer"
android:layout_margin="10dp"
android:elevation="2dp"
android:background="#drawable/bg_parent_rounded_corner"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--TODO:Change to Circle Image View-->
<ImageView
android:id="#+id/profileImage"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="5dp"
android:layout_marginLeft="5dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
/>
<LinearLayout
android:id="#+id/postDetail_1"
android:orientation="vertical"
android:layout_marginTop="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="20dp"
android:layout_alignParentRight="true">
<TextView
android:id="#+id/post_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/LabelStyle"
android:textSize="15sp"
android:fontFamily="#font/sf_pro_display_semibold" />
<LinearLayout
android:id="#+id/postDetail_2"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/postLocation"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/postTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="120dp" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="#+id/postImage"
android:layout_height="200dp"
android:layout_width="match_parent"
android:layout_below="#+id/postDetail_1"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:layout_marginTop="6dp"
/>
</RelativeLayout>
Your issue might be in this method,
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val feedItem = getItem(position)
if(feedItem != null){
holder.bind(feedItem)
}
}
Your ViewHolder is using poisition from method parameter which might be inconsistent
See from here,
So, you should change line to this:
val feedItem = getItem(holder.adapterPosition)
instead of
val feedItem = getItem(position)
I hope it resolves the issue.
This might help some one looking for the same Exception to be cleared :
What I did is that my feed_row.xml above is included inside < layout > tags and I changed it in this way , So the exception got cleared:
Before Exception:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
After changing to this Exception cleared:
<layout 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">
I don't know how it works but it did work!!! So Anyone who knows what happens there can explain please!

Using weight on RecyclerView the items width starts behaving weird

Whats wrong with the next layouts?
I pretend to include a RecyclerView inside a DialogFragment to list Checkboxes and handle the check.
Issues:
Item text should fill the content of the screen.
On scroll the items arrange to the right position.
dialog.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="#dimen/spacing_large"
android:paddingRight="#dimen/spacing_large"
android:paddingTop="#dimen/spacing_large">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/degrees_picker"
android:textAppearance="#style/TextAppearance.AppCompat.Title" />
<ProgressBar
android:id="#+id/degreesProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="#dimen/spacing_large"
android:indeterminate="true"
android:visibility="gone" />
<android.support.v7.widget.RecyclerView
android:id="#+id/degreesRecyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="#dimen/spacing_medium"
android:layout_weight="1"
tools:listitem="#layout/degrees_item" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right">
<Button
android:id="#+id/degreesCancelButton"
style="#style/Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/cancel" />
<Button
android:id="#+id/degreesOkButton"
style="#style/Widget.AppCompat.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/ok" />
</LinearLayout>
</LinearLayout>
degrees_item.xml (if this just have a CheckBox same thing happens):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:orientation="horizontal">
<TextView
android:id="#+id/degreesItemTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:gravity="center_vertical"
tools:text="Degree tal tal tal tal tal tal tal tal tal tal tal tal tal tal tal tal" />
<CheckBox
android:id="#+id/degreesItemCheck"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="#dimen/spacing_medium" />
</LinearLayout>
adapter:
class DegreesAdapter(val preselectedItems: List<Degree>?) : RecyclerView.Adapter<DegreesAdapter.ViewHolder>(), SubtopicsItemListener {
var items = listOf<Degree>()
set(values) {
field = values.map { CheckableDegree(it, checked = preselectedItems?.contains(it) == true) }
notifyDataSetChanged()
}
val selectedItems get() = items.filter { (it as? CheckableDegree)?.checked == true }
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent?.context)
val contactView = inflater.inflate(R.layout.degrees_item, parent, false)
return ViewHolder(contactView, this)
}
override fun getItemCount() = items.size
override fun getItemId(position: Int): Long {
return items[position].id.toLong()
}
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
val degree = items[position]
if (degree is CheckableDegree) {
holder?.itemView?.degreesItemCheck?.isChecked = degree.checked
}
holder?.itemView?.degreesItemTextView?.text = degree.description
}
override fun onItemCheck(position: Int, checked: Boolean) {
(items[position] as? CheckableDegree)?.checked = checked
}
class ViewHolder(itemView: View, listener: SubtopicsItemListener) : RecyclerView.ViewHolder(itemView) {
init {
itemView.degreesItemCheck.setOnCheckedChangeListener { _, isChecked ->
listener.onItemCheck(layoutPosition, isChecked)
}
}
}
}
interface SubtopicsItemListener {
fun onItemCheck(position: Int, checked: Boolean)
}
class CheckableDegree(degree: Degree, var checked: Boolean = false)
: Degree(degree.id, degree.description), Parcelable {
companion object {
#JvmField val CREATOR: Parcelable.Creator<CheckableDegree> = object : Parcelable.Creator<CheckableDegree> {
override fun createFromParcel(source: Parcel): CheckableDegree = CheckableDegree(source)
override fun newArray(size: Int): Array<CheckableDegree?> = arrayOfNulls(size)
}
}
constructor(source: Parcel)
: this(source.readParcelable<Degree>(Degree::class.java.classLoader),
1 == source.readInt())
override fun describeContents() = 0
override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags)
dest.writeInt((if (checked) 1 else 0))
}
}
DegreesDialogFragment:
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.medanswers.R
import com.medanswers.api.error.ApiError
import com.medanswers.api.repositories.user.Degree
import com.medanswers.base.MyActivity
import kotlinx.android.synthetic.main.degrees_dialog.*
import javax.inject.Inject
/**
* Show the lists of subtopics
*/
class DegreesDialogFragment : DialogFragment(), DegreesDialogContract.View {
val listener: Listener?
get() = (activity as? Listener) ?: (targetFragment as? Listener)
#Inject lateinit var presenter: DegreesDialogContract.Presenter
companion object {
val extrasPreselectedItems = "preselected_items"
fun newInstance(preselectedItems: List<Degree>?): DegreesDialogFragment {
val frag = DegreesDialogFragment()
frag.arguments = Bundle().apply {
preselectedItems?.let { putParcelableArray(extrasPreselectedItems, it.toTypedArray()) }
}
return frag
}
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, state: Bundle?): View? {
return inflater?.inflate(R.layout.degrees_dialog, container)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as MyActivity).activityComponent.inject(this)
#Suppress("UNCHECKED_CAST")
val preselectedItems =
(arguments.getParcelableArray(extrasPreselectedItems) as? Array<Degree>)?.toList()
// calls initViews(preselectedItems)
presenter.attach(this, preselectedItems)
}
override fun initViews(preselectedItems: List<Degree>?) {
degreesRecyclerView.apply {
layoutManager = LinearLayoutManager(activity)
adapter = DegreesAdapter(preselectedItems)
}
degreesOkButton.setOnClickListener {
(degreesRecyclerView?.adapter as? DegreesAdapter)?.selectedItems?.let {
listener?.onDegreesDialogOkClick(it)
dismiss()
}
}
degreesCancelButton.setOnClickListener { this#DegreesDialogFragment.dismiss() }
}
override fun showItems(degrees: List<Degree>) {
(degreesRecyclerView?.adapter as? DegreesAdapter)?.items = degrees
}
override fun showProgress() {
degreesProgressBar.visibility = View.VISIBLE
}
override fun dismissProgress() {
degreesProgressBar.visibility = View.GONE
}
override fun close(apiError: ApiError?) {
apiError?.let { listener?.onDegreesDialogError(it) }
dismiss()
}
interface Listener {
fun onDegreesDialogOkClick(degrees: List<Degree>)
fun onDegreesDialogError(apiError: ApiError)
}
}
You need to set up your DialogFragment width from code like below:
WindowManager.LayoutParams p = getDialog().getWindow().getAttributes();
p.width = ViewGroup.LayoutParams.MATCH_PARENT;
p.height = ViewGroup.LayoutParams.MATCH_PARENT;
getDialog().getWindow().setGravity(Gravity.CENTER);
getDialog().getWindow().setAttributes(p);
You can take a look at my EasyFilePickerDialog, and check the file FilePickerDialogFragment.java for more detail.

Categories

Resources