Proper way to access ViewModel from Recyler Adaper - android

I have a Data Repository that saved data & fetches data, this is passed into a ViewModel as constructor. I have methods in a ViewModel that fetches and saves data.
I have a button that I click for each row (Item in a RecyclerView list) this saves data using the ViewModel.
I have found that I can directly call a ViewModel initialised it into the constructor, I checked the Google Android examples & this part is not covered.
Something like this below:
Copied from: Databinding Recyclerview and onClick
private ExampleViewModel exampleViewModel;
public ExampleListAdapter(Context context, List<Model> models) {
this.context = context;
this.models = models;
// ...
exampleViewModel = ViewModelProviders.of((FragmentActivity) context).get(ExampleViewModel.class);
}
But then, I could also call a ViewModel by passing a ViewModel object from the Activity alongside with the context.
So what the proper way of calling a ViewModel?

This is your pojo class
data class Item(val id: Int)
This is your adapter.
class Adapter : RecyclerView.Adapter<Adapter.ViewHolder>() {
var items: List<Item> = emptyList()
set(value) {
field = value
notifyDataSetChanged()
}
var callback: Callback? = null
override fun getItemCount(): Int {
return items.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.simple_textview, parent, false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = items[position]
holder.itemView.setOnClickListener {
callback?.onItemClicked(item)
}
holder.bindItem(item)
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItem(item: Item) {
// Fill layout
}
}
interface Callback {
fun onItemClicked(item: Item)
}
}
This is your viewModel class.
class MyViewModel : ViewModel(), Adapter.Callback {
override fun onItemClicked(item: Item) {
}
}
And this is your fragment.
class MyFragment : Fragment() {
private val adapter = Adapter()
private lateinit var myViewModel: MyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myViewModel = ViewModelProviders.of(activity!!).get(MyViewModel::class.java)
adapter.callback = myViewModel
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.my_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
adapter.items = listOf(
Item(1),
Item(2)
)
//Setup recyclerView etc.
}
}

Related

How to open a "Details" Fragment onclick of Recyclerview in Kotlin

I have home fragment and I want it to go to another fragment which will show its "details", which passes the data of the clicked recyclerview item to that "details" fragment.
When I click the article it will move to detail article which passes the data.
As for the code, here's the adapter:
class ArticleAdapter(private val articleList: ArrayList<Article>) :
RecyclerView.Adapter<ArticleAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = ItemArticleBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val data = articleList[position]
holder.bind(data)
}
class MyViewHolder(private val binding: ItemArticleBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(data: Article) {
Glide.with(binding.root.context)
.load(data.img)
.into(binding.articleImage)
binding.articleTitle.text = data.title
binding.root.setOnClickListener {
val article = Article(
data.title,
data.img,
data.content
)
}
}
}
override fun getItemCount(): Int {
return articleList.size
}
}
Here's my detailFragment
class DetailArticleFragment : Fragment() {
private var _binding: FragmentDetailArticleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDetailArticleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val intent = Intent(binding.root.context, DetailArticleFragment::class.java)
val article = intent.getParcelableExtra<Article>(DETAIL_ARTICLE) as Article
Glide.with(this)
.load(article.img)
.into(_binding!!.articleImage)
_binding!!.articleTitle.text = article.title
_binding!!.articleDescription.text = article.content
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
const val DETAIL_ARTICLE = "detail_article"
}
}
You need to pass click listener interface to adapter, for example:
typealias OnArticleClick = (article: Article) -> Unit
class ArticleAdapter(
private val articleList: ArrayList<Article>
) :
RecyclerView.Adapter<ArticleAdapter.MyViewHolder>() {
var onArticleClick: OnArticleClick? = null
...
binding.root.setOnClickListener {
val article = Article(
data.title,
data.img,
data.content
)
onArticleClick?.invoke(article)
}
...
}
And init onArticleClick in your home fragment with RecyclerView:
adapter.apply {
onArticleClick = {
// show details fragment
}
}

Data not refreshing on fragment recreation in ViewPager2 PagedList DiffUtil FragmentStateAdapter

I was trying to display Two sets of Product List in ViewPager tabs inside a fragment. I was also using Paging library. Able to get data loaded and pagination is also working fine. I was also able to invalidate and reload paged list data, if I am in same fragment.
On moving to another fragment from this Tab layout and return to this fragment. I am able to see old data in both the tabs. But reload/pagination is not happening. Afer Invalidating the data, the fetched new data is not shown. The recycler view contains old data. I am getting the data from API and execute submit list - Diff Utils is also getting called. But the ProductPagedListAdapter - recycler view methods are not called.
I feel on recreating the fragment adapter is recreating but it is not getting bound to existing recycler view. I tried retainInstance, it was not working. I am using the two different instances of same ProductFragment in view pager. Due to some issues I am observing data from Parent Fragment ProductTabFragment and sending data to ViewPager fragments.
Any help on this would be highly appreciated.
ViewPager Fragment
class ProductTabFragment : Fragment() {
private lateinit var tabLayout: TabLayout
private lateinit var viewPager: ViewPager2
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val viewModel: ProductViewModel by viewModels(
ownerProducer = { activity as FragmentActivity },
factoryProducer = { viewModelFactory }
)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.product_tab_fragment, container, false)
tabLayout = view.findViewById(R.id.tabLayoutProduct)
viewPager = view.findViewById(R.id.viewPagerLayoutProduct)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewPager.apply {
adapter = ProductAdapter(childFragmentManager, viewLifecycleOwner.lifecycle, viewModel.getTabCount())
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
viewModel.selectedTab = position
}
})
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = when (position) {
POSITION_TAB_RECOMMENDED -> TAB_TITLE_RECOMMENDED
POSITION_TAB_RECENT -> TAB_TITLE_RECENT
else -> TAB_TITLE_RECOMMENDED
}
}.attach()
}
viewModel.recommendedList.observe(viewLifecycleOwner, Observer {
sendDataToFragment(POSITION_TAB_RECOMMENDED, it)
})
viewModel.recentList.observe(viewLifecycleOwner, Observer {
sendDataToFragment(POSITION_TAB_RECENT, it)
})
}
override fun onAttach(context: Context) {
super.onAttach(context)
retainInstance = true
}
private fun sendDataToFragment(fragmentPosition: Int, productList: PagedList<Product>?) {
val mFragment: Fragment? =
(viewPager.adapter as? ProductTabPagerAdapter)?.tabFragments?.get(fragmentPosition)
mFragment?.let { (it as? ProductFragment)?.setUpListData(productList) }
}
companion object {
const val POSITION_TAB_RECOMMENDED = 0
const val POSITION_TAB_RECENT = 1
private const val TAB_TITLE_RECOMMENDED = "RECOMMENDED"
private const val TAB_TITLE_RECENT = "RECENT"
#JvmStatic
fun newInstance() = ProductTabFragment()
}
}
Pager Adapter
class ProductTabPagerAdapter(fragmentManager: FragmentManager,
lifecycle: Lifecycle, val tabCount :Int) :
FragmentStateAdapter(fragmentManager,lifecycle) {
internal var tabFragments = listOf<Fragment>(ProductFragment.newInstance(ProductTabFragment.POSITION_TAB_RECOMMENDED),ProductFragment.newInstance(ProductTabFragment.POSITION_TAB_RECENT))
override fun getItemCount(): Int {
return tabCount
}
override fun createFragment(position: Int): Fragment {
return tabFragments[position]
}
}
**Same Reusable fragments used in ViewPager**
class ProductFragment : Fragment() {
private var productAdapter = ProductPagedListAdapter()
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private val viewModel: ProductViewModel by viewModels(
ownerProducer = { activity as FragmentActivity },
factoryProducer = { viewModelFactory }
)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.productÆ’_fragment, container, false)?.apply {
initialProgressBar = findViewById(R.id.initialLoadProgressBar)
loadMoreProgressBar = findViewById(R.id.loadMoreProgressBar)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<RecyclerView>(R.id.productList)?.apply {
context?.also { ctx ->
layoutManager = LinearLayoutManager(ctx)
}
adapter = productAdapter
}
}
fun setUpListData(data: PagedList<Product>?) {
productAdapter.submitList(data)
}
override fun onAttach(context: Context) {
super.onAttach(context)
retainInstance = true
}
}
Paged List Adapater
class ProductPagedListAdapter() : PagedListAdapter<Product, ProductItemHolder>(diffUtilCallBack) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductItemHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.product_item, parent, false)
return ProductItemHolder(view)
}
override fun onBindViewHolder(holder: ProductItemHolder, position: Int) {
getItem(position)?.let { holder.bind(it) }
}
companion object {
var diffUtilCallBack = object : DiffUtil.ItemCallback<Product>() {
override fun areItemsTheSame(
oldItem: Product,
newItem: Product
) = oldItem.id == newItem.id
override fun areContentsTheSame(
oldItem: Product,
newItem: Product
)= oldItem == newItem
}
}
}
View Model
class ProductViewModel()) : ViewModel() {
var recommendedList : LiveData<PagedList<Product>> = MutableLiveData<PagedList<Product>>()
var recentList : LiveData<PagedList<Product>> = MutableLiveData<PagedList<Product>>()
init {
fetchRecentProducts()
fetchRecommended()
}
fun fetchRecommended() {
val pagedListConfig = PagedList.Config.Builder()
.setPageSize(ProductDataSource.PAGE_LIMIT)
.setEnablePlaceholders(false).build()
val dataSourceFactory = object : DataSource.Factory<Int, Product>() {
override fun create(): DataSource<Int, Product> {
return ProductDataSource(
type = "recommended",
)
}
}
recommendedList = LivePagedListBuilder<Int, Product>(
dataSourceFactory,
pagedListConfig
).build()
}
fun fetchRecent() {
val pagedListConfig = PagedList.Config.Builder()
.setPageSize(ProductDataSource.PAGE_LIMIT)
.setEnablePlaceholders(false).build()
val dataSourceFactory = object : DataSource.Factory<Int, Product>() {
override fun create(): DataSource<Int, Product> {
return ProductDataSource(
type = "recent",
)
}
}
recentList = LivePagedListBuilder<Int, Product>(
dataSourceFactory,
pagedListConfig
).build()
}
fun resetPaginationData() {
recommendedList.value?.dataSource?.invalidate()
recentList.value?.dataSource?.invalidate()
}
}
Data Source for paged List
class ProductDataSource(
private val type: String) : PageKeyedDataSource<Int, Product>() {
override fun loadInitial(
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Int, Product>
) {
compositeDisposable.add(
productRepo.getProductList(
tyoe
).subscribe(
{ productList ->
callback.onResult(
productList, null, PAGE_LIMIT
)
}
)
}
}

How to create custom arrayAdapter without context attribute

For a school project, I made a custom arrayadapter with a context, resources and items attribute. Today I received feedback from my teacher and he wants me to find a solution where I don't have a Context attribute because he doesnt like that I always need to specify the context.
This is my code:
class TalentListAdapter(
var mCtx: Context,
var resources: Int,
var items: MutableList<Talent>
) : ArrayAdapter<Talent>(mCtx, resources, items) {
lateinit var mItem: Talent
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val layoutInflater: LayoutInflater = LayoutInflater.from(mCtx)
val view: View = layoutInflater.inflate(resources, null)
mItem = items[position]
//set name of talent
val talentTextView: TextView = view.findViewById(R.id.talentName)
talentTextView.text = mItem.toString()
return view
}
}
He wants to get rid of the mCtx: Context attribute, but I don't find a solution for it. Any suggestions?
The adapter is created like this atm:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val listView: ListView = binding.talentList
// set custom adapter for talent_list
val adapter = TalentListAdapter(view.context, R.layout.talentlayout, binding.talentViewModel?.getTalents()?.value as MutableList<Talent>)
listView.adapter = adapter
}
Can you try this
class YourAdapter() : RecyclerView.Adapter<YourAdapter.ViewHolder>() {
inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
var Data: List<YourResponse> = emptyList()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.your_layout, parent, false
)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return Data.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val getYourModel = Data[position]
val Binding = holder.view
Binding.yourTextView.apply{
text = getYourModel.yourField
}
.....
}
}
and pass Data from your Fragment or Activity (my example uses Retrofit2)
private var theList: List<YourResponse> = emptyList()
private val theAdapter =yourAdapter()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
onUpdateData()
}
private fun onUpdateData() {
...
override fun onResponse(call: Call<YourResponse>, response: Response<YourResponse>) {
val body = response.body()?.results
if (response.isSuccessful && body != null) {
onUpdateDataSuccess(body)
} else {
// Something
}
}
...
}
private fun onUpdateDataSuccess(data: List<YourResponse>) {
theList = data
theAdapter.Data = data
}
...
You only need context while inflating so you should use parent.context instead of explicit context.
Parent.Context represent the Activity/Fragment where this recycler view is implemented.

Kotlin: How to pass data from recyclerview in fragment to another fragment using viewmodel/interface?

I was not able to find an example of passing data from a recyclerview adapter to a fragment using a viewmodel or interface in kotlin.
I have tried to do with using just a bundle, but I receive a null bundle in the receiving fragment.
Here is my Recyclerview Adapter code snippet.
class QnAMyProfileEditRecyclerViewAdapter(private val context: Context) : RecyclerView.Adapter<QnAMyProfileEditRecyclerViewAdapter.Holder>() {
private var currentUserQnADataMyProfileEdit = emptyList<QnAData>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val view: View = if (viewType == R.layout.rv_item_qn_a_my_profile) {
LayoutInflater.from(parent.context)
.inflate(R.layout.rv_item_qn_a_my_profile, parent, false)
} else {
LayoutInflater.from(parent.context)
.inflate(R.layout.rv_footer_new_qna_my_profile, parent, false)
}
return Holder(view)
}
override fun getItemCount(): Int {
return currentUserQnADataMyProfileEdit.size + 1
}
//implement Floating Action Button at the bottom of the recyclerview
override fun getItemViewType(position: Int): Int {
return if (position == currentUserQnADataMyProfileEdit.size) {
R.layout.rv_footer_new_qna_my_profile
} else {
R.layout.rv_item_qn_a_my_profile
}
}
override fun onBindViewHolder(holder: Holder, position: Int) {
if (position == currentUserQnADataMyProfileEdit.size) {
holder.viewHolderNewQnA?.setOnClickListener {
Toast.makeText(context, "New item intent", Toast.LENGTH_SHORT).show()
}
} else {
//Normal bind for RecyclerView
holder.bindData(currentUserQnADataMyProfileEdit[position])
holder.viewHolderCRUDQnAMyProfile?.setOnClickListener {
//Give QnACrud current QnAData --- send bundle
val bundle = Bundle()
bundle.putString(
"currentQnAQuestion",
currentUserQnADataMyProfileEdit[position].question
)
bundle.putString(
"currentQnAAnswer",
currentUserQnADataMyProfileEdit[position].answer
)
QnAMyProfileEditCRUDFragment().arguments = bundle
val activity: AppCompatActivity = context as AppCompatActivity
activity.supportFragmentManager.beginTransaction()
.add(R.id.cl_my_profile_edit, QnAMyProfileEditCRUDFragment())
.addToBackStack(null).commit()
}
}
}
fun setData(updateList: List<QnAData>) {
this.currentUserQnADataMyProfileEdit = updateList
notifyDataSetChanged()
}
inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val viewHolderQuestion =
itemView.findViewById(R.id.txt_question_qna_my_profile_edit) as TextView?
val viewHolderAnswer =
itemView.findViewById(R.id.txt_answer_qna_my_profile_edit) as TextView?
val viewHolderNewQnA =
itemView.findViewById(R.id.fab_new_qna_my_profile_edit) as FloatingActionButton?
val viewHolderCRUDQnAMyProfile =
itemView.findViewById(R.id.fab_crud_qna_my_profile_edit) as FloatingActionButton?
//Bind data with inflaters
fun bindData(currentUserInList: QnAData) {
viewHolderQuestion?.text = currentUserInList.question
viewHolderAnswer?.text = currentUserInList.answer
}
}
}
Here is my fragment which is suppose to receive the data(bundle):
class QnAMyProfileEditCRUDFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//Get bundle data and set Edit Text values
val bundle:Bundle? = arguments //Bundle == null!!!!
val qnaQuestionData: String? = bundle?.getString("currentQnAQuestion")
val qnaAnswerData: String? = bundle?.getString("currentQnAAnswer")
et_qna_question.setText(qnaQuestionData)
et_qna_answer.setText(qnaAnswerData)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_qn_a_my_profile_edit_c_r_u_d, container, false)
}
override fun onAttach(context: Context) {
super.onAttach(context)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//Pop fragment if grey area(outside the box) is tapped
imv_overlay.setOnClickListener {
fragmentManager?.popBackStack()
}
}
}
QnAData is a simple data class with two member question and answer both are string type
First, there are some refactors to be made on your code:
inner class can lead to memory leak so change your ViewHolder to:
Bring your click listener inside your ViewHolder
ViewHolder
class MyViewHolder(...) : RecyclerView.ViewHolder(...) {
fun bind(model: Model) {
...
itemView.setOnClickListener {
callback?.onSomeEventHappened(model)
//you can also pass lambda here instead of using an interface for callback
}
}
}
RecyclerViewAdapter needs to define a protocol to communicate with a fragment:
class Adapter : RecyclerView.Adapter<T>() {
interface callback {
fun onSomeEventHappened()
}
private var callback: callback? = null
fun setListener(listener: Callback) {
callback = listener
}
}
If data is shared between two fragments use SharedViewModel
If you need to pass data you can do it either AndroidNavigationCompoent arguments or passing them when creating the fragment:
val SomeFragment = SomeFragment().apply {
SomeFragment.arguments = ... //A bundle for example
}
Try creating a constructor inside your fragment, then beginTransaction
val fragment = QnAMyProfileEditCRUDFragment.newInstance(
currentUserQnADataMyProfileEdit[position].question,
currentUserQnADataMyProfileEdit[position].answer)
activity.supportFragmentManager.beginTransaction()
.add(R.id.cl_my_profile_edit,fragment)
.addToBackStack(null).commit()
Inside the QnAMyProfileEditCRUDFragment you can just use the values from the constructor
Assuming you have viewModel shared by for your Fragment container activity & child fragments, you can pass that same viewModel to your recyclerview adapter.
class Activity: AppCompatActivity {
private val viewModel: AnyViewModel by lazy {
//Initialization
}
private val adapter = Adapter(viewModel)
...
}
class Adapter(viewModel: SomeViewModel) : RecyclerView.Adapter<T>() {
...
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.onBind(anyList[position])
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
...
fun onBind(data: SomeData) {
btnSomeAction.setOnClickListener {
//Here change respected data in viewModel or call some method
//viewModel.anyObject = some_value
//viewModel.anyMethodCall()
}
}
}
}
Now you can access viewModel in you adapter and use it update data from Activity or Fragment.

Sharing recyclerview item ID with another fragment

Currently I have a recyclerview adapter that gets data from the database and displays it in the dashboard fragment. Once the item is clicked, I want to pass the item id to the details fragment to get the correct items information on the detailed view. How can I pass this ID to the detailed fragment?
Dashboard Fragment
class DashboardFragment : Fragment() {
private lateinit var viewModel: DashboardViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Get a reference to the binding object and inflate the fragment views.
val binding: DashboardFragmentBinding = DataBindingUtil.inflate(
inflater, R.layout.dashboard_fragment, container, false)
val application = requireNotNull(this.activity).application
val dataSource = NumberDatabase.getInstance(application).numberDatabaseDao
val viewModelFactory = DashboardViewModelFactory(dataSource, application)
// Get a reference to the ViewModel associated with this fragment.
val dashboardViewModel =
ViewModelProviders.of(
this, viewModelFactory).get(DashboardViewModel::class.java)
// To use the View Model with data binding, you have to explicitly
// give the binding object a reference to it.
binding.dashboardViewModel = dashboardViewModel
val adapter = CounterAdapter(CounterListener { nightId ->
dashboardViewModel.onCountClicked(nightId)
})
binding.counterList.adapter = adapter
dashboardViewModel.counts.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.submitList(it)
}
})
}
Adapter
class CounterAdapter (val clickListener: CounterListener): ListAdapter<Number, CounterAdapter.ViewHolder>(NightDiffCallback()) {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position)!!, clickListener)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.from(parent)
}
class ViewHolder private constructor(val binding: ListItemCounterBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(item: Number, clickListener: CounterListener) {
binding.night = item
binding.clickListener = clickListener
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ListItemCounterBinding.inflate(layoutInflater, parent, false)
return ViewHolder(binding)
}
}
}
}
class CounterListener(val clickListener: (nightId: Long) -> Unit) {
fun onClick(night: Number) = clickListener(night.nightId)
}
In your recyclerview
class RecipeRecyclerAdapter(
mFragmentCommunication: FragmentCommunication
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var mFragmentCommunication: FragmentCommunication? = null
init {
this.mFragmentCommunication =mFragmentCommunication
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
RecyclerView.ViewHolder {
view = LayoutInflater.from(parent.context)
.inflate(
com.broadcast.recipeslistapp.R.layout.layout_recipe_list_item,
parent,
false
)
return RecipeViewHolder(view)
}
}
override fun getItemCount(): Int {
if (mRecipes.isNullOrEmpty()) {
return 0
} else {
return mRecipes!!.size
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder.itemView.setOnClickListener {
mFragmentCommunication!!.PassNightId(nightId = 1) //value of nightId
}
}
interface FragmentCommunication {
fun PassNightId(nightId: Int)
}
Fragment A from where you want to send data :
class RecipeListActivity : BaseActivity(),
RecipeRecyclerAdapter.FragmentCommunication {
private lateinit var layoutManager: LinearLayoutManager
}
private fun initRecyclerView() {
mAdapter = RecipeRecyclerAdapter( this)
val itemDecorator = VerticalSpacingItemDecorator(30);
recipe_list.addItemDecoration(itemDecorator);
recipe_list.adapter = mAdapter
layoutManager = LinearLayoutManager(this);
recipe_list.layoutManager = layoutManager
}
override fun PassNightId(nightId: Int) {
val testFragment = TestFragment()
val bundle = Bundle()
bundle.putString("nightId", "$nightId")
testFragment.arguments = bundle
supportFragmentManager.beginTransaction().replace(R.id.containerId,testFragment).commit()
}
}
Fragment B where you want to getData :
class TestFragment : Fragment()
{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val nightId =arguments?.getString("nightId")
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.test_fragment,container,false)
}
}
This is how you can send data from on fragment to another fragment.

Categories

Resources