I am using MVVM with Room persistence and livedata. I am fetching the data from local database and want to show in the form of list in the recyclerView and my recyclerView is not showing anything.
My adapter is like any other regular adapter
RecyclerView Code
class MyInformationAdapter : RecyclerView.Adapter<MyInformationAdapter.ViewHolder>() {
private var myList: List<PersonInformation> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater.inflate(R.layout.my_adapter_data, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int = myList.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
var myDataClass: PersonInformation = myList[position]
holder.name.text = myDataClass.name
holder.fName.text = myDataClass.fatherName
holder.email.text = myDataClass.email
holder.contact.text = myDataClass.contactNo.toString()
}
fun updateTheState(myList: List<PersonInformation>){
this.myList = myList
notifyDataSetChanged()
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var name: TextView = itemView.findViewById(R.id.yourName)
var fName: TextView = itemView.findViewById(R.id.yourFatherName)
var email: TextView = itemView.findViewById(R.id.yourEmail)
var contact: TextView = itemView.findViewById(R.id.yourContact)
}
}
RecyclerView Activity Code
class FinalActivity : AppCompatActivity() {
private var myDataList : List<PersonInformation> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_final)
setAdapter()
}
private fun setAdapter() {
val adapter = MyInformationAdapter()
val layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
recyclerView.adapter = adapter
adapter.updateTheState(myDataList)
}
}
*Fragment as a View of MVVM *
class PersonalInformationFragment : Fragment() {
private var viewModel: PersonInformationViewModel? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_personal_information, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
btn.setOnClickListener {
viewModel = ViewModelProviders.of(this)[PersonInformationViewModel::class.java]
viewModel?.getAllData()?.observe(this, Observer {
this.setAllData(it)
})
val intent = Intent(activity, FinalActivity::class.java)
startActivity(intent)
}
}
private fun setAllData(personInformation: List<PersonInformation>) {
val setData = PersonInformation(
name.text.toString(),
fName.text.toString(),
email.text.toString(),
contact.text.toString().toInt()
)
viewModel?.setTheData(setData)
}
}
Related
I made an app from which I get data from TMDB API, everything seems to work but when I start the app, it displays only hint text, after scrolling the View get's updated with the data from the API
This is how it looks before scrolling
And this is how it looks after scrolling down a a bit
This is how I implemented it:
HomeFragment.kt
class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private lateinit var popularMovies: RecyclerView
private lateinit var popularMoviesAdapter: MoviesAdapter
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val homeViewModel = ViewModelProvider(this)[HomeViewModel::class.java]
_binding = FragmentHomeBinding.inflate(inflater, container, false)
popularMovies = binding.popularMovies
popularMovies.layoutManager = LinearLayoutManager(context,LinearLayoutManager.VERTICAL,false)
popularMoviesAdapter = MoviesAdapter(listOf())
popularMovies.addItemDecoration(DividerItemDecoration(context,DividerItemDecoration.VERTICAL))
popularMovies.adapter = popularMoviesAdapter
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
MoviesRepository.getPopularMovies(page = 1,onSuccess = ::onPopularMoviesFetched,onError = ::onError)
}
private fun onPopularMoviesFetched(movies: List<Movie>) {
popularMoviesAdapter.updateMovies(movies)
}
private fun onError() {
Toast.makeText(context, getString(R.string.error_fetch_movies), Toast.LENGTH_SHORT).show()
}
MovieAdapter.kt
class MoviesAdapter(
private var movies: List<Movie>
) : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
val view = LayoutInflater
.from(parent.context)
val binding = MovieItemBinding.inflate(view)
return MovieViewHolder(binding)
}
override fun getItemCount(): Int = movies.size
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bind(movies[position])
}
fun updateMovies(movies: List<Movie>) {
this.movies = movies
notifyDataSetChanged()
}
inner class MovieViewHolder(private val binding: MovieItemBinding) : RecyclerView.ViewHolder(binding.root) {
private val poster: ImageView = itemView.findViewById(R.id.item_movie_poster)
fun bind(movie: Movie) {
binding.movieTitle.text =movie.title
binding.movieReleaseDate.text = movie.releaseDate
binding.movieOverview.text = movie.overview
binding.movieReleaseDate.text = movie.releaseDate
Glide.with(itemView)
.load("https://image.tmdb.org/t/p/w342${movie.posterPath}")
.transform(CenterCrop())
.into(poster)
}
}
make adapter initialization on onViewCreated instead of onCreateView
i have defined a val in my fragment like so
private var selectionMode: Boolean = false
and a property to change it
fun enableSelectionMode() {
selectionMode = true
}
and i passed both to Adapter and want to call enableSelectionMode after clicking on each recycler item but nothing happen
here is my fragment:
class PostsFragment : Fragment() {
private val model: PostsViewModel by activityViewModels()
private var selectionMode: Boolean = false
fun enableSelectionMode() {
selectionMode = true
}
private lateinit var recyclerView: RecyclerView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_posts, container, false)
recyclerView = view.findViewById(R.id.posts_recycler)
recyclerView.layoutManager = LinearLayoutManager(view.context)
model.posts.observe(viewLifecycleOwner, { items ->
recyclerView.adapter = PostsAdapter(items, model, selectionMode,
::enableSelectionMode)
})
return view
}
}
and my adapter
class PostsAdapter(
private val posts: List<Post>,
private val model: PostsViewModel,
private val selectionMode: Boolean,
private val enableIt: () -> Unit
) :
RecyclerView.Adapter<PostsAdapter.ViewHolder>() {
companion object {
private const val TYPE_FROM = 0
private const val TYPE_TO = 1
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var postContent: TextView = itemView.findViewById(R.id.post_content)
var selectPostCheckBox: ImageView = itemView.findViewById(R.id.post_select_checkbox)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(
if (viewType == TYPE_TO) R.layout.to_post else R.layout.from_post,
parent,
false
)
return ViewHolder(view)
}
#SuppressLint("CheckResult")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.selectPostCheckBox.visibility = if (selectionMode) View.VISIBLE else View.GONE
holder.postContent.text = selectionMode.toString()
holder.selectPostCheckBox.setImageResource(R.drawable.cursor_color)
holder.itemView.setOnClickListener() {
enableIt()
notifyDataSetChanged()
}
}
override fun getItemCount() = posts.size
override fun getItemViewType(position: Int): Int {
return posts[position].selfPost
}
}
I'm using a RecyclerView to show some info from Firebase, with kotlin in Android Studio.
But when I click on the RecyclerView, it return the position as -1, so the page that opens that item is always empty...
This is my code from setOnClickListener on the ViewHolder:
class CustomViewHolder(view: View): RecyclerView.ViewHolder(view){
val titulo = view.findViewById<TextView>(R.id.txtTitle)
val localizacao = view.findViewById<TextView>(R.id.txtDescription)
//val urlPag = view.findViewById<TextView>(R.id.record_amount)
//val descricao = view.findViewById<TextView>(R.id.record_date)
val img = view.findViewById<ImageView>(R.id.imageItem)
init {
view.setOnClickListener{
val position: String = CustomViewHolder(it).bindingAdapterPosition.toString()
val intent = Intent(view.context, NotificationActivity::class.java).apply {
putExtra("id", position)
println(position)
}
view.context.startActivity(intent)
}
}
}
This is the fragment were the recyclerView is:
class InboxFragment : Fragment() {
private var listData: List<Notifications>? = null
private var adapter: MainAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_inbox, container, false)
val rv = view.findViewById<RecyclerView>(R.id.recyclerView)
rv.setHasFixedSize(true)
rv.layoutManager = LinearLayoutManager(context)
listData = ArrayList()
val nm = FirebaseDatabase.getInstance().getReference("notifications")
nm.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists()) {
for (npsnapshot in dataSnapshot.children) {
val l: Notifications? = npsnapshot.getValue(Notifications::class.java)
if (l != null) {
(listData as ArrayList<Notifications>).add(l)
}
}
adapter = MainAdapter(listData)
rv.adapter = adapter
}
}
override fun onCancelled(databaseError: DatabaseError) {}
})
return view
}
}
This is the adapter:
class MainAdapter(var listData: List<Notifications>?) : RecyclerView.Adapter<CustomViewHolder>(){
override fun getItemCount(): Int {
return listData!!.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
//create view
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.inforow, parent, false)
return CustomViewHolder(cellForRow)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
val ld: Notifications = listData!![position]
holder.titulo.text = ld.titulo
holder.localizacao.text = ld.localizacao
Picasso.get().load(ld.img).into(holder.img)
}
}
Can someone please help?
The idea of my project is to click in one recyclerView in MainActivity and after one dialog with other recyclerView is opened.
The error is that my recyclerView isn't updating in the Dialog
MainActivity:
private fun openDialog(postModel: PostModel){
CommentDialogFragment(postModel).show(supportFragmentManager, "dialog")
}
CommentDialogFragment:
class CommentDialogFragment(postSelected: PostModel): AppCompatDialogFragment() {
private var mCommentsList: MutableList<CommentModel> = arrayListOf()
private val mCommentServiceRequest = CommentServiceRequest()
private lateinit var mContext: Context
private lateinit var mRecyclerView: RecyclerView
private var postModel: PostModel = postSelected
private lateinit var mCommentListAdapter: CommentListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NORMAL, R.style.DialogStyle)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val viewGroup = inflater.inflate(R.layout.comment, container)
mContext = inflater.context
mCommentListAdapter = CommentListAdapter(mCommentsList)
mRecyclerView = viewGroup.findViewById(R.id.recyclerViewComments)
setupCommentObserver(postModel.id)
loadCommentRecyclerView()
dialog!!.setTitle("${mContext.getString(R.string.comments)} ${postModel.title}")
return viewGroup
}
private fun setupCommentObserver(postId: Int){
mCommentServiceRequest.searchCommentsFromAPI(postId)
.observe(this, Observer { comments ->
if (comments != null){
mCommentsList = comments.toMutableList()
mCommentListAdapter.setData(mCommentsList)
}
})
}
private fun loadCommentRecyclerView() {
mCommentsList.clear()
mRecyclerView.setHasFixedSize(true)
mRecyclerView.adapter = mCommentListAdapter
mRecyclerView.layoutManager = LinearLayoutManager(mContext)
}}
CommentListAdapter:
class CommentListAdapter(private var commentList: MutableList<CommentModel>):
RecyclerView.Adapter<CommentViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommentViewHolder {
val inflate = LayoutInflater.from(parent.context)
val view = inflate.inflate(R.layout.comment_list , parent, false)
return CommentViewHolder(view)
}
override fun getItemCount(): Int {
return commentList.count()
}
override fun onBindViewHolder(holder: CommentViewHolder, position: Int) {
holder.bindTask(commentList[position])
}
fun setData(list: MutableList<CommentModel>){
this.commentList = list
this.notifyDataSetChanged()
}}
I did the same thing in other project without use notifyDataSetChanged() only using callbacks and worked, so I tried here too but didn't work, I think the dialog is the problem
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.