AdapterItem
class AdapterItem(val context: Context, val userList: List): RecyclerView.Adapter<AdapterItem.ViewHolder>() {
class ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
var numeLista: TextView
var caloriiLista: TextView
init{
numeLista=itemView.numeLista
caloriiLista=itemView.caloriiLista
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
var itemView = LayoutInflater.from(context).inflate(R.layout.row_items, parent, false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.numeLista.text=userList[position].foodItems.get(0).foodName
holder.caloriiLista.text=userList[position].foodItems.get(0).calories.toString()
}
override fun getItemCount(): Int {
return userList.size
}
}
InformationFragment:
const val BASE_URL= "https://raw.githubusercontent.com/terrenjpeterson/caloriecounter/master/src/data/"
class InformationFragment : Fragment() {
private var binding: FragmentInformationBinding? =null
private val sharedViewModel: SharedViewModel by activityViewModels()
lateinit var adapterItem: AdapterItem
lateinit var linearLayoutManager: LinearLayoutManager
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
recyclerview_lista.setHasFixedSize(true)
linearLayoutManager = LinearLayoutManager(this.context)
recyclerview_lista.layoutManager=linearLayoutManager
getMyData()
val fragmentBinding = FragmentInformationBinding.inflate(inflater, container, false)
binding= fragmentBinding
return fragmentBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.apply {
viewModel= sharedViewModel
informationFragment=this#InformationFragment
lifecycleOwner= viewLifecycleOwner
}
}
private fun getMyData() {
val retrofitBuilder= Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
.create(ApiInterface::class.java)
val retrofitData= retrofitBuilder.getData()
retrofitData.enqueue(object : Callback<List<MyDataItem>?> {
override fun onResponse(
call: Call<List<MyDataItem>?>,
response: Response<List<MyDataItem>?>
) {
val responseBody= response.body()!!
adapterItem= AdapterItem(,responseBody ) // ** here what context should I pass ???? ... I cant use baseContext**
adapterItem.notifyDataSetChanged()
recyclerview_list.adapter=adapterItem
}
override fun onFailure(call: Call<List<MyDataItem>?>, t: Throwable) {
Log.d( "informationFragment" , "onFailure: " + t.message)
}
})
}
}
There is no need to pass the context as an arg you can use the context of the parent passed to your viewHolder
you shouldn't pass context. change:
class AdapterItem(val context: Context, val userList: List):
to:
class AdapterItem(val userList: List):
and
var itemView = LayoutInflater.from(context).inflate(R.layout.row_items, parent, false)
to:
var itemView = LayoutInflater.from(parent.context).inflate(R.layout.row_items, parent, false)
Be careful if you want to pass context to the adapter, if you assigned the view context to a variable in your adapter, it probably would lead to a memory leak. Consider using WeakReference to avoid that if happens.
Update
move
recyclerview_lista.setHasFixedSize(true)
to onViewCreated
At first I tried to pass the data to a TextView and it worked without any problems... but now I wanna pass the data to a recycleview to be able to add a checkbox for each element
This is the code where I pass the data to TextView:
const val BASE_URL= "https://raw.githubusercontent.com/terrenjpeterson/caloriecounter/master/src/data/"
class InformationFragment : Fragment() {
private var binding: FragmentInformationBinding? =null
private val sharedViewModel: SharedViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
getMyData()
val fragmentBinding = FragmentInformationBinding.inflate(inflater, container, false)
binding= fragmentBinding
return fragmentBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.apply {
viewModel= sharedViewModel
informationFragment=this#InformationFragment
lifecycleOwner= viewLifecycleOwner
}
}
private fun getMyData() {
val retrofitBuilder= Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
.create(ApiInterface::class.java)
val retrofitData= retrofitBuilder.getData()
retrofitData.enqueue(object : Callback<List<MyDataItem>?> {
override fun onResponse(call: Call<List<MyDataItem>?>, response: Response<List<MyDataItem>?>) {
val responseBody= response.body()!!
val myStringBuilder = StringBuilder()
for(myData in responseBody)
{
myStringBuilder.append(myData.foodItems.get(0).foodName)
myStringBuilder.append(" ")
myStringBuilder.append(myData.foodItems.get(0).calories)
myStringBuilder.append("\n")
myStringBuilder.append("\n")
}
txtId.text = myStringBuilder
}
override fun onFailure(call: Call<List<MyDataItem>?>, t: Throwable) {
Log.d( "informationFragment" , "onFailure: " + t.message)
}
})
}
}
You can use the context of view parameter of your viewholder.
My app still keep crashing when it reaches the InformationFragment
class AdapterItem( val userList: List): RecyclerView.Adapter<AdapterItem.ViewHolder>() {
class ViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
var numeLista: TextView
var caloriiLista: TextView
init{
numeLista=itemView.numeLista
caloriiLista=itemView.caloriiLista
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
var itemView = LayoutInflater.from(parent.context).inflate(R.layout.row_items, parent, false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.numeLista.text=userList[position].foodItems.get(0).foodName
holder.caloriiLista.text=userList[position].foodItems.get(0).calories.toString()
}
override fun getItemCount(): Int {
return userList.size
}
}
const val BASE_URL= "https://raw.githubusercontent.com/terrenjpeterson/caloriecounter/master/src/data/"
class InformationFragment : Fragment() {
private var binding: FragmentInformationBinding? = null
private val sharedViewModel: SharedViewModel by activityViewModels()
lateinit var adapterItem: AdapterItem
lateinit var linearLayoutManager: LinearLayoutManager
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
recyclerview_lista.setHasFixedSize(true)
linearLayoutManager = LinearLayoutManager(this.context)
recyclerview_lista.layoutManager = linearLayoutManager
getMyData()
val fragmentBinding = FragmentInformationBinding.inflate(inflater, container, false)
binding = fragmentBinding
return fragmentBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.apply {
viewModel = sharedViewModel
informationFragment = this#InformationFragment
lifecycleOwner = viewLifecycleOwner
}
}
private fun getMyData() {
val retrofitBuilder = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
.create(ApiInterface::class.java)
val retrofitData = retrofitBuilder.getData()
retrofitData.enqueue(object : Callback<List<MyDataItem>?> {
override fun onResponse(
call: Call<List<MyDataItem>?>,
response: Response<List<MyDataItem>?>
) {
val responseBody = response.body()!!
adapterItem = AdapterItem(responseBody)
adapterItem.notifyDataSetChanged()
recyclerview_lista.adapter=adapterItem
}
override fun onFailure(call: Call<List<MyDataItem>?>, t: Throwable) {
d("informationFragment", "onFailure: " + t.message)
}
})
}
}
Related
My App is just shows an Empty Screen and RecyclerView is not showing anything or is not working but there are no compile time or run time errors.
It would be great help if I get an answer ...
I have been making an app that uses a RecyclerView but it's not showing any thing..why contents of the recycler view have not been showing up.my codes are bellow
Here is My FeedFragment, Adapter
//Fragment
#AndroidEntryPoint
class FeedFragment : Fragment(R.layout.feed_fragment) {
private var _binding: FeedFragmentBinding? = null
private val binding get() = _binding!!
private lateinit var feedAdapter: FeedAdapter
private val viewModel: FeedViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FeedFragmentBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRv()
getCharacters()
}
private fun getCharacters() {
viewModel.listData.observe(viewLifecycleOwner, Observer { pagingData ->
feedAdapter.submitData(viewLifecycleOwner.lifecycle, pagingData)
})
}
private fun setupRv() {
feedAdapter = FeedAdapter()
binding.feedRv.layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
binding.feedRv.apply {
adapter = feedAdapter
setHasFixedSize(true)
visibility = View.VISIBLE
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
//PagingDataAdapter
class FeedAdapter : PagingDataAdapter<Characters,FeedAdapter.FeedViewHolder >(DiffCallback()) {
inner class FeedViewHolder(private val binding: FeedFragmentRvBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(characters: Characters) {
with(binding) {
Glide.with(itemView)
.load(characters.image)
.transition(DrawableTransitionOptions.withCrossFade())
.error(R.drawable.ic_launcher_background)
.into(feedRvImage)
feedRvName.text = characters.name
feedRvStatus.text = characters.status
feedRvSpecies.text = characters.species
feedRvGender.text = characters.gender
println(characters.name)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FeedViewHolder {
val binding =
FeedFragmentRvBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return FeedViewHolder(binding)
}
override fun onBindViewHolder(holder: FeedViewHolder, position: Int) {
getItem(position)?.let { holder.bind(it) }
}
class DiffCallback : DiffUtil.ItemCallback<Characters>() {
override fun areItemsTheSame(oldItem: Characters, newItem: Characters): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Characters, newItem: Characters): Boolean {
return oldItem == newItem
}
}
}
// retrofit response
Update your CharacterResponse model class to parse data accordingly.
data class CharacterResponse(
val info: Info,
val results: List<Characters>
)
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
}
}
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 a RecyclerView in MVVM project. I have to get text from editText (searchWordEt) and then pass it to the function that invokes API method in viewmodel.API works fine and returns data. But when I invoke searchDefAdapter.submitList(response) in SearchFragment nothing happens and RecyclerView data not showing.
class SearchDefAdapter(
private var infoListener: OnItemClickListener,
private var addListener: OnItemClickListener
):
ListAdapter<Def, SearchDefViewHolder>(differCallback) {
interface OnItemClickListener {
fun onItemClick(position: Int)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchDefViewHolder {
return SearchDefViewHolder(
SearchWordCardBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
),
addListener,
infoListener
)
}
override fun onBindViewHolder(holder: SearchDefViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
class SearchDefViewHolder(
private val binding: SearchWordCardBinding,
addListener: SearchDefAdapter.OnItemClickListener,
infoListener: SearchDefAdapter.OnItemClickListener
): RecyclerView.ViewHolder(binding.root) {
fun bind(data: Def) {
with (binding) {
searchCardTv.text = "${data.text} - ${data.tr[0].text}"
}
}
init {
binding.addSearchCard.setOnClickListener {
addListener.onItemClick(adapterPosition)
}
binding.infoSearchCard.setOnClickListener {
infoListener.onItemClick(adapterPosition)
}
}
}
val differCallback = object : DiffUtil.ItemCallback<Def>() {
override fun areItemsTheSame(oldItem: Def, newItem: Def): Boolean {
return oldItem.text == newItem.text
}
override fun areContentsTheSame(oldItem: Def, newItem: Def): Boolean {
return oldItem == newItem
}
}
#AndroidEntryPoint
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
private val binding get() = _binding!!
lateinit var searchDefAdapter: SearchDefAdapter
private val viewModel: DictionaryViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentSearchBinding.inflate(inflater, container, false)
val view = binding.root
searchDefAdapter = SearchDefAdapter(
object : SearchDefAdapter.OnItemClickListener {
override fun onItemClick(position: Int) {
Log.d("tag", "Item Added!")
//viewModel.saveWord(position)
}
},
object : SearchDefAdapter.OnItemClickListener {
override fun onItemClick(position: Int) {
val wordFragment = WordFragment()
fragmentManager?.beginTransaction()?.replace(
R.id.nav_host_fragment_container,
wordFragment
)?.commit()
}
}
)
setUpRecyclerView(searchDefAdapter)
var job: Job? = null
binding.searchWordEt.addTextChangedListener { editable ->
job?.cancel()
job = MainScope().launch {
delay(SEARCH_WORD_TIME_DELAY)
editable?.let {
if (editable.toString().isNotEmpty())
viewModel.getTranslation(editable.toString())
}
}
}
viewModel.def.observe(viewLifecycleOwner, Observer { response ->
binding.apply {
searchDefAdapter.submitList(response)
}
})
return view
}
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
private fun setUpRecyclerView(adapter: SearchDefAdapter){
binding.searchRv.apply {
adapter
layoutManager = LinearLayoutManager(activity)
}
}
}
You haven't actually set the adapter in setUpRecyclerView()
private fun setUpRecyclerView(myAdapter: SearchDefAdapter){
binding.searchRv.apply {
layoutManager = LinearLayoutManager(activity)
adapter = myAdapter // here
}
}
I make an api call to get a list, in the logcat I can see all the groups that came back from the api but the Recylerview doesn't show my items.
ViewModel
class MyGroupScreenViewMode(private val context: Context, private val codagramApi: CodagramApi) : ViewModel() {
#ExperimentalCoroutinesApi
private val myGroups = MutableLiveData<List<Group>>()
#ExperimentalCoroutinesApi
fun getMyGroups(): LiveData<List<Group>> = myGroups
init {
viewModelScope.launch(Dispatchers.IO){
val response = codagramApi.getAllGroups()
updateUi(response.groupList)
}
}
#ExperimentalCoroutinesApi
private fun updateUi(update: List<Group>){
viewModelScope.launch(Dispatchers.Main){
myGroups.value = update
}
}
and the fragment
class MyGroupScreen : Fragment() {
private val myGroupsAdapter: GroupAdapter by inject()
private lateinit var bindingGroup: FragmentThirdBinding
#ExperimentalCoroutinesApi
private val viewModel: MyGroupScreenViewMode by inject()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
bindingGroup = FragmentThirdBinding.inflate(layoutInflater, container, false)
return bindingGroup.root
}
#ExperimentalCoroutinesApi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bindingGroup.myGroups.apply {
adapter = this#MyGroupScreen.myGroupsAdapter
layoutManager = LinearLayoutManager(context)
}
myGroupsAdapter.currentList
bindingGroup.fabAdd.setOnClickListener {
it.findNavController().navigate(MyGroupScreenDirections.actionFirstViewToGroupscreen())
}
bindgetmyGroupToLiveData()
}
#ExperimentalCoroutinesApi
private fun bindgetmyGroupToLiveData() {
viewModel.getMyGroups().observe(viewLifecycleOwner, androidx.lifecycle.Observer {
myGroupsAdapter.submitList(it)
})
}
and the adapter
class GroupAdapter : ListAdapter<Group,GroupScreenViewHolder>(object: DiffUtil.ItemCallback<Group>(){
override fun areItemsTheSame(oldItem: Group, newItem: Group): Boolean = oldItem.creator?.id == newItem.creator?.id
override fun areContentsTheSame(oldItem: Group, newItem: Group): Boolean =
oldItem == newItem
}) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroupScreenViewHolder {
return GroupScreenViewHolder(
GroupscreenMygroupsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
}
#RequiresApi(Build.VERSION_CODES.N)
override fun onBindViewHolder(holder: GroupScreenViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
class GroupScreenViewHolder(private val binding: GroupscreenMygroupsBinding) :
RecyclerView.ViewHolder(binding.root) {
#RequiresApi(Build.VERSION_CODES.N)
fun bind(postData: Group) {
binding.textView.text = postData.id.toString()
}
}
Check if data is in the list, then assign the list to adapter and call adapter.notifyDataSetChanged(),