I have 2 fragments which each have a Recyclerview and use the same Adapter. But why does the data only appear in the Recyclerview on the DashboardFragment while the Recyclerview in TiketFragment doesn't?
Screenshot DashboardFragment
Screenshot TiketFragment
TiketFragment.kt
class TiketFragment : Fragment() {
private lateinit var preferences: Preferences
private lateinit var mDatabase:DatabaseReference
private var dataList=ArrayList<Film>()
override fun onCreateView(
inflater: LayoutInflater,container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_tiket, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
preferences= Preferences(activity!!.applicationContext)
mDatabase=FirebaseDatabase.getInstance().getReference("Film")
rc_ticket.layoutManager=LinearLayoutManager(context)
getData()
}
private fun getData() {
mDatabase.addValueEventListener(object :ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
dataList.clear()
for(getdataSnapshot in snapshot.children){
val film=getdataSnapshot.getValue(Film::class.java)
dataList.add(film!!)
}
rc_ticket.adapter=ComingSoonAdapter(dataList){
}
tv_total.setText("${dataList.size} Movies")
}
override fun onCancelled(error: DatabaseError) {
Toast.makeText(context, error.message, Toast.LENGTH_LONG).show()
}
})
}
}
DashboardFragment.kt
class DashboardFragment : Fragment() {
private lateinit var preferences:Preferences
private lateinit var mDatabase:DatabaseReference
private var dataList=ArrayList<Film>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_dashboard , container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
preferences= Preferences(activity!!.applicationContext)
mDatabase=FirebaseDatabase.getInstance().getReference("Film")
tv_nama.setText(preferences.getValues("nama"))
if(preferences.getValues("saldo").equals("")){
currency(preferences.getValues("saldo")!!.toDouble(),tv_saldo)
}
Glide.with(this)
.load(preferences.getValues("url"))
.apply(RequestOptions.circleCropTransform())
.into(iv_profile)
rv_now_playing.layoutManager=LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL,false)
rv_coming_soon.layoutManager=LinearLayoutManager(context)
getData()
}
private fun getData() {
mDatabase.addValueEventListener(object :ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
dataList.clear()
for(getSnapshot in snapshot.children){
var film=getSnapshot.getValue(Film::class.java)
dataList.add(film!!)
}
rv_now_playing.adapter=NowPlayingAdapter(dataList){
var intent=Intent(context,DetailActivity::class.java).putExtra("data",it)
startActivity(intent)
}
rv_coming_soon.adapter=ComingSoonAdapter(dataList){
var intent=Intent(context,DetailActivity::class.java).putExtra("data",it)
startActivity(intent)
}
}
override fun onCancelled(error: DatabaseError) {
Toast.makeText(context,""+error.message,Toast.LENGTH_LONG).show()
}
})
}
private fun currency(harga:Double, textView: TextView){
val localID=Locale("in","ID")
val format= NumberFormat.getCurrencyInstance(localID)
textView.setText(format.format(harga))
}
}
ComingSoonAdapter.kt
class ComingSoonAdapter(private var data:List<Film>,
private val listener:(Film)-> Unit) : RecyclerView.Adapter<ComingSoonAdapter.ViewHolder>() {
lateinit var contextAdapter:Context
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ComingSoonAdapter.ViewHolder {
val layoutInflater=LayoutInflater.from(parent.context)
contextAdapter=parent.context
val inflatedView=layoutInflater.inflate(R.layout.row_item_coming_soon,parent,false)
return ViewHolder(inflatedView)
}
override fun onBindViewHolder(holder: ComingSoonAdapter.ViewHolder, position: Int) {
holder.bindItem(data[position],listener,contextAdapter)
}
override fun getItemCount(): Int =data.size
class ViewHolder(view:View):RecyclerView.ViewHolder(view){
private val tvJudul:TextView=view.findViewById(R.id.tv_title)
private val tvRating:TextView=view.findViewById(R.id.tv_rate)
private val tvGenre:TextView=view.findViewById(R.id.tv_genre)
private val ivPoster:ImageView=view.findViewById(R.id.iv_poster_image)
fun bindItem(data: Film, listener: (Film) -> Unit, context: Context){
tvJudul.setText(data.judul)
tvGenre.setText(data.genre)
tvRating.setText(data.rating)
Glide.with(context)
.load(data.poster)
.into(ivPoster)
itemView.setOnClickListener{
listener(data)
}
}
}
}
fragment_tiket.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/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/shape_background_blue"
tools:context=".home.tiket.TiketFragment">
<TextView
android:id="#+id/textView15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="24dp"
android:fontFamily="#font/montserrat"
android:text="Today's"
android:textColor="#color/white"
android:textSize="24sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tv_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:fontFamily="#font/montserrat"
android:text="4 Movies"
android:textColor="#color/white_grey"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView15" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rc_ticket"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tv_total"
tools:itemCount="4"
tools:listitem="#layout/row_item_coming_soon" />
</androidx.constraintlayout.widget.ConstraintLayout>
Related
My aim : GIF
I followed this guide to implement shared element transitions in my app
My Result : Screen Capture
Problem : The shared animation is happening. But the image in the ItemMovieFragment is coming from top left corner of container.
Cannot figure out the solution.
Please suggest some solution
Here is the code :
ItemMovieFragment : complete code
class ItemMovieFragment : Fragment() {
private lateinit var itemMovieViewModel: ItemMovieViewModel
private lateinit var args : ItemMovieFragmentArgs
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentItemMovieBinding.inflate(inflater)
// (activity as AppCompatActivity).supportActionBar?.hide()
sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
// rest of the code
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ViewCompat.setTransitionName(image_poster,args.itemiId.toString())
Toast.makeText(context,"id: ${args.itemiId}", Toast.LENGTH_SHORT).show()
}
}
ItemMovieFragment - Layout : complete code
<androidx.cardview.widget.CardView
android:id="#+id/poster"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
app:cardCornerRadius="4dp"
app:cardElevation="10dp"
app:cardMaxElevation="10dp"
app:layout_constraintBottom_toTopOf="#+id/scrollView2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/scrollView2">
<ImageView
android:id="#+id/image_poster"
android:layout_width="120dp"
android:layout_height="170dp"
android:layout_margin="0dp"
android:padding="0dp"
android:scaleType="centerCrop"
app:imageUrl="#{itemMovieViewModel.movieInf.posterPath}"
app:srcCompat="#drawable/stp" />
</androidx.cardview.widget.CardView>
CompletedFragment : complete code
class CompletedFragment(private var type : String) : Fragment() {
//firebase
private lateinit var firebaseDatabase: FirebaseDatabase
private lateinit var dbReference: DatabaseReference
private lateinit var firebaseAdapter: FirebaseRVAdapter
private lateinit var auth: FirebaseAuth
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentCompletedBinding.inflate(inflater)
// rest of the code...
firebaseAdapter = FirebaseRVAdapter(FirebaseRVAdapter.ItemClickListener { item ->
val id : Int = item.id!!.toInt()
val extras = FragmentNavigatorExtras(
poster to item.id!!
)
if(type == TV)
findNavController().navigate(MyWatchlistFragmentDirections.actionGlobalItemTvFragment(id))
else
findNavController().navigate(MyWatchlistFragmentDirections.actionMyWatchlistFragmentToItemMovieFragment(id),extras) // this is the navigation line for shared animation
}, optionss)
binding.myfirebaseList.adapter = firebaseAdapter
return binding.root
}
//rest of the code
}
FirebaseRVAdapter :
package com.utkarsh.fxn.ui.mywatchlist
class FirebaseRVAdapter(private val clickListener: ItemClickListener , options: FirebaseRecyclerOptions<TvFirebase>)
: FirebaseRecyclerAdapter<TvFirebase, FirebaseRVAdapter.tvFirebaseViewHolder>(options) {
class tvFirebaseViewHolder ( private var binding: MywatchlistListItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(
tvfirebase: TvFirebase,
clickListener: ItemClickListener
){
ViewCompat.setTransitionName(binding.poster,tvfirebase.id) //setting transition name
binding.tvFirebase=tvfirebase
binding.clickListener=clickListener
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): tvFirebaseViewHolder {
return tvFirebaseViewHolder(MywatchlistListItemBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun onBindViewHolder(holder: tvFirebaseViewHolder, position: Int, model: TvFirebase) {
return holder.bind(model!!,clickListener)
}
class ItemClickListener(val listener : (tvfirebase: TvFirebase) -> Unit){
fun OnClick(tvfirebase: TvFirebase) = listener(tvfirebase)
}
}
XML of image of item of Completed Fragment : complete file
<ImageView
android:id="#+id/poster"
android:layout_width="114dp"
android:layout_height="170dp"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
app:imageUrl="#{tvFirebase.poster_path}"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.013"
tools:srcCompat="#drawable/smol" />
You must have same transitionName for item and details screen.
<ImageView
android:id="#+id/poster"
android:layout_width="114dp"
android:layout_height="170dp"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:transitionName="image_poster"
app:imageUrl="#{tvFirebase.poster_path}"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.013"
tools:srcCompat="#drawable/smol" />
I have a recycler view and I am passing data to the recyclerviewadapter but recycler view does not show any data.
ProdyctsRecyclerViewAdapter - it is getting the data in setList but does not display it
Where am I doing it wrong please
Thanks
R
class ProductsRecyclerViewAdapter(private val clickListener: (Product) -> Unit): RecyclerView.Adapter<ProductsMyViewHolder>() {
private val productsList = ArrayList<Product>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductsMyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding: ListItemBinding =
DataBindingUtil.inflate(layoutInflater, R.layout.products_list_item, parent, false)
return ProductsMyViewHolder(binding)
}
override fun getItemCount(): Int {
return productsList.size
}
override fun onBindViewHolder(holder: ProductsMyViewHolder, position: Int) {
holder.bind(productsList[position], clickListener)
}
fun setList(products: List<Product>) {
productsList.clear()
productsList.addAll(products)
}
}
class ProductsMyViewHolder(val binding: ListItemBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(product: Product, clickListener: (Product) -> Unit) {
binding.nameTextView.text = product.name
binding.emailTextView.text = product.catagory
binding.listItemLayout.setOnClickListener {
clickListener(product)
}
}
}
ProductsFragment
class ProductsFragment: Fragment() {
private lateinit var binding: ProductsBinding
private lateinit var navController: NavController
private lateinit var productsViewModel: ProductsViewModel
private lateinit var adapter: ProductsRecyclerViewAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.products, container, false)
val dao = SubscriberDatabase.getInstance(requireActivity().applicationContext).productDAO
val repository = ProductRepository(dao)
val factory = ProductsViewModelFactory(repository, requireActivity().applicationContext)
productsViewModel = ViewModelProvider(this, factory).get(ProductsViewModel::class.java)
binding.productsViewModel = productsViewModel
binding.lifecycleOwner = this
val view = binding.root
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
initRecyclerView()
productsViewModel.navigateScreen.observe(viewLifecycleOwner, EventObserver {
navController.navigate(it)
})
}
private fun initRecyclerView() {
binding.productsRecyclerView.layoutManager = LinearLayoutManager(context)
adapter = ProductsRecyclerViewAdapter ({ selectedItem: Product -> listItemClicked(selectedItem)})
displayProductssList()
}
private fun displayProductssList() {
productsViewModel.products.observe(viewLifecycleOwner, Observer {
Log.i("MYTAG", it.toString())
adapter.setList(it)
adapter.notifyDataSetChanged()
})
}
private fun listItemClicked(product: Product) {
Toast.makeText(context, "Selected name is ${product.name}", Toast.LENGTH_LONG).show()
//productsViewModel.initUpdateAndDelete(subscriber)
}
}
ProductsViewModel
class ProductsViewModel (
private val repository: ProductRepository,
private val context: Context
): ViewModel() {
val products = repository.products
}
Products_list_item
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="#color/colorPrimary"
app:cardCornerRadius="10dp"
app:cardElevation="10dp" >
<LinearLayout
android:id="#+id/product_list_item_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/product_name_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="name"
android:textColor="#FFFFFF"
android:textSize="30sp"
android:textStyle="bold" />
<TextView
android:id="#+id/product_catagory_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="catagory"
android:textColor="#FFFFFF"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</layout>
Thank you Parag Pawar for the answer
For reference made the followingn changes
private fun initRecyclerView() {
binding.productsRecyclerView.layoutManager = LinearLayoutManager(context)
adapter = ProductsRecyclerViewAdapter ({ selectedItem: Product -> listItemClicked(selectedItem)})
binding.productsRecyclerView.adapter = adapter //ADDED THIS LINE
displayProductssList()
}
In productsRecyclerView, had the wrong binding it should be ProductsListItemBinding
val binding: ProductsListItemBinding =
DataBindingUtil.inflate(layoutInflater, R.layout.products_list_item, parent, false)
ProductsMyViewHolder(val binding: ProductsListItemBinding):
You've not set the adapter to recycler view. In your initRecyclerView() set the adapter after initializing it.
private fun initRecyclerView() {
binding.productsRecyclerView.layoutManager = LinearLayoutManager(context)
adapter = ProductsRecyclerViewAdapter ({ selectedItem: Product -> listItemClicked(selectedItem)})
//notice this
binding.productsRecyclerView.adapter = adapter
displayProductssList()
}
add notifyDataSetChanged() method in your Adapter's setList() method
I don't understand why are you clearing your list and adding them at the same time.
fun setList(products: List<Product>) {
productsList.clear()
productsList.addAll(products)
}
if you want to first clear your list you should do it outside the loop
I want to bind data to my recylerview adapter. This is my current code following the MVVM pattern
Fragment
class NotificationFragment : Fragment() {
var customeProgressDialog: CustomeProgressDialog? = null
private val appPreferences: AppPreference by inject()
private val notificationViewModel: NotificationViewModel by viewModel()
private lateinit var binding: FragmentNotificationBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentNotificationBinding.inflate(inflater, container, false)
return binding.getRoot()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.notification.layoutManager=LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
customeProgressDialog = CustomeProgressDialog(activity)
notificationViewModel.notifications(
appPreferences.getUsername(),
appPreferences.getPassword(),
appPreferences.getUserId()
)
initObservables()
}
private fun initObservables() {
notificationViewModel.progressDialog?.observe(this, Observer {
if (it!!) customeProgressDialog?.show() else customeProgressDialog?.dismiss()
})
notificationViewModel.apiResponse?.observe(
viewLifecycleOwner,
androidx.lifecycle.Observer { response ->
if (response.dataList != null) {
val notificationAdapter = NotificationAdapter(response.dataList as List<Data>)
notificationAdapter.notifyDataSetChanged()
binding.notification.adapter = notificationAdapter
}
})
}
}
View model
class NotificationViewModel(networkCall: NetworkCall) : ViewModel(),
Callback<ApiResponse> {
var progressDialog: SingleLiveEvent<Boolean>? = null
var apiResponse: MutableLiveData<ApiResponse>? = null
var networkCall: NetworkCall;
init {
progressDialog = SingleLiveEvent<Boolean>()
apiResponse = MutableLiveData<ApiResponse>()
this.networkCall = networkCall
}
fun notifications(username: String?, password: String?, userId: String?) {
progressDialog?.value = true
val apiPost = ApiPost()
apiPost.userName = username
apiPost.password = password
apiPost.UserId = userId
apiPost.FileType = NetworkConstant.FILE_TYPE_NOT
networkCall.getPDF(apiPost).enqueue(this)
}
override fun onFailure(call: Call<ApiResponse>, t: Throwable) {
progressDialog?.value = false
}
override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
progressDialog?.value = false
apiResponse?.value = response.body()
}
}
The adapter
class NotificationAdapter(private val list: List<Data>) :
RecyclerView.Adapter<NotificationAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ElementListBinding.inflate(inflater)
// val view = inflater.inflate(R.layout.element_list, parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val movie: Data = list[position]
holder.bind(movie)
holder.itemView.setOnClickListener {
if (!TextUtils.isEmpty(movie.filePath)) {
try {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(movie.filePath))
holder.itemView.context.startActivity(browserIntent)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
override fun getItemCount(): Int = list.size
inner class ViewHolder(binding: ElementListBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(movie: Data) {
binding.item = movie
}
}
}
unable to find binding object
the recylerview element list xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="data"
type="com.mountmeru.model.Data" />
</data>
<androidx.cardview.widget.CardView
android:id="#+id/main_cardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/main_cardrl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<RelativeLayout
android:id="#+id/rl_newsdate"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.3">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_notifi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:maxLines="2"
android:text="#{data.displayName}"
android:textColor="#android:color/black"
android:textSize="16sp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_brief"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/tv_notifi"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:layout_marginRight="10dp"
android:textColor="#android:color/black"
android:textSize="16sp"
android:visibility="gone" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/tv_brief"
android:layout_marginLeft="10dp"
android:layout_marginTop="2dp"
android:layout_marginRight="10dp"
android:maxLines="1"
android:text="hey i am date"
android:textColor="#color/inactive_text"
android:textSize="14sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="#+id/rl_newsdate"
android:layout_weight="0.7"
android:padding="5dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/iv_notifi"
android:layout_width="match_parent"
android:layout_height="75dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="#drawable/mer" />
</RelativeLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.cardview.widget.CardView>
</layout>
Can someone confirm me is my implementation of MVVM correct or it needs some refactoring?
How do I make of data binding in my recyclerview list element xml?
You have already used <layout> as parent tag in element_list.xml. Now you can inflate it in the adapter class using DataBinding. See the example below:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ElementListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(Binding)
}
You have to modify your ViewHolder class as well as shown below:
inner class ViewHolder(val binding: ElementListBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(movie: Data) {
with(itemView) {
binding.tvNotifi.text = movie.displayName
binding.tvDate.text = movie.UpdatedDate
if (movie.description != null) {
binding.tvBrief.text = movie.description
binding.tvBrief.visibility = View.VISIBLE
}
}
}
}
I want to bind data on adapter from viewmodel in xml layout file
This is my fragment class.
class NotificationFragment : Fragment() {
var customeProgressDialog: CustomeProgressDialog? = null
private val appPreferences: AppPreference by inject()
private val notificationViewModel: NotificationViewModel by viewModel()
private lateinit var binding: FragmentNotificationBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentNotificationBinding.inflate(inflater, container, false)
return binding.getRoot()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.notification.layoutManager=LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
customeProgressDialog = CustomeProgressDialog(activity)
notificationViewModel.notifications(
appPreferences.getUsername(),
appPreferences.getPassword(),
appPreferences.getUserId()
)
initObservables()
}
private fun initObservables() {
notificationViewModel.progressDialog?.observe(this, Observer {
if (it!!) customeProgressDialog?.show() else customeProgressDialog?.dismiss()
})
notificationViewModel.apiResponse?.observe(
viewLifecycleOwner,
androidx.lifecycle.Observer { response ->
if (response.dataList != null) {
var notificationAdapter = NotificationAdapter(response.dataList as List<Data>)
notificationAdapter.notifyDataSetChanged()
binding.notification.adapter = notificationAdapter
}
})
}
}
My viewmodel
class NotificationViewModel(networkCall: NetworkCall) : ViewModel(),
Callback<ApiResponse> {
var progressDialog: SingleLiveEvent<Boolean>? = null
var apiResponse: MutableLiveData<ApiResponse>? = null
var networkCall: NetworkCall;
init {
progressDialog = SingleLiveEvent<Boolean>()
apiResponse = MutableLiveData<ApiResponse>()
this.networkCall = networkCall
}
fun notifications(username: String?, password: String?, userId: String?) {
progressDialog?.value = true
val apiPost = ApiPost()
apiPost.userName = username
apiPost.password = password
apiPost.UserId = userId
apiPost.FileType = NetworkConstant.FILE_TYPE_NOT
networkCall.getPDF(apiPost).enqueue(this)
}
override fun onFailure(call: Call<ApiResponse>, t: Throwable) {
progressDialog?.value = false
}
override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
progressDialog?.value = false
apiResponse?.value = response.body()
}
}
The adapter class
lass NotificationAdapter(private val list: List<Data>) :
RecyclerView.Adapter<NotificationAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.element_list, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val movie: Data = list[position]
holder.bind(movie)
holder.itemView.setOnClickListener {
if (!TextUtils.isEmpty(movie.filePath)) {
try {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(movie.filePath))
holder.itemView.context.startActivity(browserIntent)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
override fun getItemCount(): Int = list.size
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(movie: Data) {
with(itemView) {
tv_notifi.text = movie.displayName
tv_date.text = movie.UpdatedDate
if (movie.description != null) {
tv_brief.text = movie.description
tv_brief.visibility = View.VISIBLE
}
}
}
}
}
This is my item xml layout
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewmodel"
type="com.mountmeru.viewmodel.NotificationViewModel" />
</data>
<androidx.cardview.widget.CardView
android:id="#+id/main_cardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/main_cardrl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<RelativeLayout
android:id="#+id/rl_newsdate"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.3">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_notifi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:maxLines="2"
android:text="hey i am notification text"
android:textColor="#android:color/black"
android:textSize="16sp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_brief"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/tv_notifi"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:layout_marginRight="10dp"
android:textColor="#android:color/black"
android:textSize="16sp"
android:visibility="gone" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/tv_brief"
android:layout_marginLeft="10dp"
android:layout_marginTop="2dp"
android:layout_marginRight="10dp"
android:maxLines="1"
android:text="hey i am date"
android:textColor="#color/inactive_text"
android:textSize="14sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="#+id/rl_newsdate"
android:layout_weight="0.7"
android:padding="5dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/iv_notifi"
android:layout_width="match_parent"
android:layout_height="75dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="#drawable/mer" />
</RelativeLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.cardview.widget.CardView>
</layout>
I was able to do the binding for the fragment but for adapter I am unsure how to proceed.
If I understand your question correctly, you want to pass data to your adapter inside xml. For this, you will need to write custom binding adapter for your RecyclerView.
This link has all you need.
https://android.jlelse.eu/how-to-bind-a-list-of-items-to-a-recyclerview-with-android-data-binding-1bd08b4796b4
I see the problem with a RecyclerView in viewpager fragment and I use bottom naviagation bar. When I tab on the navigation bar to change a screen and then pressed on back button it will go to the first screen which have the viewpager but the recyclerview doesn't appear and when I tab the naviagtion bar to reselect this screen it appear.
The recyclerview is show the list from firestore database and using ViewModel.
I try to set retainInstance to true but it doesn't work. And try to setup the recyclerview in onCreate and onCreateView it doesn't work too.
Edit: this is my code.
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
navView.setupWithNavController(navController)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId){
android.R.id.home -> {
onBackPressed()
return true
}
}
return super.onOptionsItemSelected(item)
}
}
activity_main.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"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="#drawable/bg_nav"
app:itemIconTint="#color/navbar_color"
app:itemTextColor="#color/navbar_color"
app:itemTextAppearanceActive="#style/navbar_font"
app:itemTextAppearanceInactive="#style/navbar_font"
app:itemBackground="#drawable/nav_ripple"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_nav_menu"/>
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="#id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
HomeFragement.kt (it contain tablayout and viewpager)
class HomeFragment : Fragment() {
private lateinit var homeViewModel: HomeViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
homeViewModel =
ViewModelProviders.of(this).get(HomeViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_home, container, false)
return root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupTab()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
private fun setupTab(){
Log.d("SET UP TAB", "SETTING UP")
val pageTabAdapter = PageTabAdapter(activity!!.supportFragmentManager, activity_tab.tabCount)
view_activity.adapter = pageTabAdapter
view_activity.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(activity_tab))
activity_tab.setOnTabSelectedListener(object : TabLayout.OnTabSelectedListener{
override fun onTabReselected(p0: TabLayout.Tab?) {
}
override fun onTabUnselected(p0: TabLayout.Tab?) {
}
override fun onTabSelected(p0: TabLayout.Tab?) {
view_activity.currentItem = p0!!.position
}
})
}
}
fragment_home.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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
android:orientation="vertical">
<TextView
android:id="#+id/home_header"
style="#style/page_header"
android:text="#string/title_home"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:id="#+id/calendar_btn"
android:layout_width="60dp"
android:layout_height="40dp"
android:background="#drawable/btn_calendar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#id/home_header"
app:layout_constraintBottom_toBottomOf="#id/home_header"
android:layout_marginEnd="10dp"/>
<com.google.android.material.tabs.TabLayout
android:id="#+id/activity_tab"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabRippleColor="#color/colorPrimary"
app:tabTextAppearance="#style/tab_font"
app:tabIndicatorHeight="2.5dp"
app:tabGravity="fill"
app:layout_constraintTop_toBottomOf="#id/home_header"
app:layout_constraintStart_toStartOf="parent"
android:background="#android:color/white">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="กิจกรรมในเดือนนี้" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="กิจกรรมที่ผ่านไปแล้ว" />
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager.widget.ViewPager
android:id="#+id/view_activity"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#id/activity_tab"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
PageAdapter.kt
class PageTabAdapter(private val fragmentManager: FragmentManager, private val anInt: Int) :
FragmentStatePagerAdapter(fragmentManager) {
override fun getItem(position: Int): Fragment {
return when (position) {
0 -> {
NewActivity.newInstance()
}
1 -> {
PastActivity.newInstance()
}
else -> {
null!!
}
}
}
override fun getCount(): Int {
return anInt
}
}
ActivityAdapter.kt (this is a recyclerview adapter)
class ActivityAdapter(
private val mContext: Context, private val mItems: List<ActivityModel>
): RecyclerView.Adapter<ActivityAdapter.Holder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val inflater = LayoutInflater.from(this.mContext)
val view = inflater.inflate(R.layout.activity_list, parent, false)
return Holder(view)
}
override fun getItemCount(): Int {
return this.mItems.size
}
override fun onBindViewHolder(holder: Holder, position: Int) {
val item = this.mItems[position]
holder.setName(item.name)
val date = item.date?.toDate()
val dateFormat: DateFormat = SimpleDateFormat("dd-MMM-yyyy")
val newDate = dateFormat.format(date).toString()
holder.setDate(newDate)
val period = item.start + " - " +item.end
holder.setPeriod(period)
holder.setButton(item)
}
inner class Holder(view: View): RecyclerView.ViewHolder(view){
private var activityName: TextView = view.activity_list_name
private var activityDate: TextView = view.activity_list_date
private var activityPeriod: TextView = view.activity_list_period
private var activityPicture: ImageView = view.activity_list_pic
private var activityButton: Button = view.activity_list_btn
fun setName(name: String?){
this.activityName.text = name
}
fun setDate(date: String?){
this.activityDate.text = date
}
fun setPeriod(period: String?){
this.activityPeriod.text = period
}
fun setPicture(picpath: String?){
}
fun setButton(data: ActivityModel){
}
}
}
}
PS.: I'm using Kotlin in my project
Thank you