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
Related
Hi I have been trying to seperate my list of washing machines to make them one by one in grid view but for some reason they keep getting stuck together like in the image.[![enter image description here][1]][1]
My Fragment that has most of the work going on in the background.
class HomeFragment : Fragment() {
private lateinit var binding: FragmentHomeBinding
private val viewModel by KoinJavaComponent.inject(HomeFragmentVM::class.java)
private val sp by inject<SPManager>()
private var profile : Data? = null
private val itemList: Array<String>
get() = arrayOf(viewModel.products.toString())
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.getWashingMachines()
setObservers()
setupGridView()
}
private fun setupGridView() {
println(viewModel.products)
}
private fun setObservers() {
val strings = arrayOf(viewModel.products)
viewModel.isCorrect.observe(viewLifecycleOwner) {
if (it.isNotEmpty()){
itemList.contentEquals(arrayOf(viewModel.products))
val adapter = ImageListAdapter(context!!, R.layout.list_item, itemList)
binding.gridview.adapter = adapter
binding.gridview.onItemClickListener =
AdapterView.OnItemClickListener { parent, v, position, id ->
println("you clicked")
}
println("Not Failed")
}
else
{
println("Failed")
}
}
}
class HomeFragment : Fragment() {
private lateinit var binding: FragmentHomeBinding
private val viewModel by KoinJavaComponent.inject(HomeFragmentVM::class.java)
private val sp by inject<SPManager>()
private var profile : Data? = null
private val itemList: Array<String>
get() = arrayOf(viewModel.products.toString())
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.getWashingMachines()
setObservers()
setupGridView()
}
private fun setupGridView() {
println(viewModel.products)
}
private fun setObservers() {
val strings = arrayOf(viewModel.products)
viewModel.isCorrect.observe(viewLifecycleOwner) {
if (it.isNotEmpty()){
itemList.contentEquals(arrayOf(viewModel.products))
val adapter = ImageListAdapter(context!!, R.layout.list_item, itemList)
binding.gridview.adapter = adapter
binding.gridview.onItemClickListener =
AdapterView.OnItemClickListener { parent, v, position, id ->
println("you clicked")
}
println("Not Failed")
}
else
{
println("Failed")
}
}
}
The Adapter
internal class ImageListAdapter internal constructor(
context: Context,
private val resource: Int,
private val itemList: Array<String>?
) : ArrayAdapter<ImageListAdapter.ItemViewHolder>(context, resource) {
private val inflater: LayoutInflater = LayoutInflater.from(context)
private lateinit var itemBinding: ListItemBinding
override fun getCount(): Int {
return if (this.itemList != null) this.itemList.size else 0
}
override fun getView(position: Int, view: View?, parent: ViewGroup): View {
var convertView = view
val holder: ItemViewHolder
if (convertView == null) {
itemBinding = ListItemBinding.inflate(inflater)
convertView = itemBinding.root
holder = ItemViewHolder()
holder.name = itemBinding.textView
holder.icon = itemBinding.icon
convertView.tag = holder
} else {
holder = convertView.tag as ItemViewHolder
}
holder.name!!.text = this.itemList!![position]
holder.icon!!.setImageResource(R.mipmap.ic_launcher)
return convertView
}
internal class ItemViewHolder {
var name: TextView? = null
var icon: ImageView? = null
}
}
The Api call works its just how do I separate this one by one to make each a grid layout view many thanks
After sawing your code, I think the problem is on
private val itemList: Array<String>
get() = arrayOf(viewModel.products.toString())
Because you are converting your list of products to ONE string when you call 'toString()', and inserting that one string into one list, passing than that one list with one item (the string) to the adapter
You should replace
private val itemList: Array<String>
get() = arrayOf(viewModel.products.toString())
with
private val itemList:Array<String>
get() {
val out = viewModel.products.map { it ->
it.toString()
}
return out
}
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
}
}
class FeedRecyclerAdapter (private val postList : ArrayList<Post>) : RecyclerView.Adapter<FeedRecyclerAdapter.PostHolder>() {
class PostHolder(val binding: FragmentDataBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostHolder {
val binding = FragmentDataBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return PostHolder(binding)
}
override fun onBindViewHolder(holder: PostHolder, position: Int) {
holder.binding.verimText.text = postList.get(position).lsi
}
override fun getItemCount(): Int {
return postList.size
}
Here is the code written for recyclerView.
private lateinit var firestore: FirebaseFirestore
private lateinit var auth: FirebaseAuth
private var _binding: FragmentDataBinding? = null
private val binding get() = _binding!!
private lateinit var postArrayList : ArrayList<Post>
private lateinit var feedAdapter : FeedRecyclerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
auth = Firebase.auth
firestore = Firebase.firestore
postArrayList = ArrayList<Post>()
feedAdapter = FeedRecyclerAdapter(postArrayList)
getData()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDataBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
private fun getData(){
firestore.collection("Posts").addSnapshotListener { value, error ->
if (error!=null){
Toast.makeText(requireContext(),error.localizedMessage,Toast.LENGTH_SHORT).show()
}else{
if (value !=null){
if (!value.isEmpty){
val documents = value.documents
for (document in documents){
val araziBoyutu = document.get("Arazi Boyutu") as String
val araziEgimi = document.get("Arazi Eğimi") as String
val panelBoyutu = document.get("Panel Boyutu") as String
val panelSayisi = document.get("Panel Sayisi") as String
val sehir = document.get("Şehir") as String
val post = Post(panelSayisi,panelBoyutu,araziEgimi,araziBoyutu,sehir)
postArrayList.add(post)
recyclerView.adapter = FeedRecyclerAdapter(postArrayList)
}
feedAdapter.notifyDataSetChanged()
}
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerView.adapter = feedAdapter
recyclerView.layoutManager = LinearLayoutManager(activity)
The code here is the part where I define the RecylcerView and save the information.
I can pull the data, I can see it on firebase, when I print it with println, I can read it in the console, I can go to the page where the text should be written, but I can't see this data in the verimText TextView I'm trying to print.
Try to call notifyDataSetChanged() or notifyItemRangeInserted() after adding post.
Recyclerview should be declared inside onViewCreated Method
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val recyclerView = view.findViewById(R.id.recyclerView) as RecyclerView
recyclerView.layoutManager = LinearLayoutManager(activity)
recyclerView.adapter = FeedRecyclerAdapter(postArrayList)
}
move this line
recyclerView.adapter = FeedRecyclerAdapter(postArrayList)
to
val post = Post(panelSayisi,panelBoyutu,araziEgimi,araziBoyutu,sehir)
postArrayList.add(post)
recyclerView.adapter = FeedRecyclerAdapter(postArrayList)//move to here
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
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)
}
}