I am trying to implement onEditPost and onDeletePost inside of my RecyclerViewFragment:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.MainActivity
import com.example.projectdrivemark.R
import com.example.projectdrivemark.databinding.FragmentListBinding
import com.example.projectdrivemark.databinding.PostBinding
class RecyclerViewFragment: Fragment(R.layout.fragment_list), PostAdapter.OnPostClickListener {
private lateinit var binding: FragmentListBinding
val dummyList = MockDatabase.createMockData()
val adapter = PostAdapter(dummyList, this)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreate(savedInstanceState)
binding = FragmentListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<RecyclerView>(R.id.recycleViewMain).apply{
layoutManager = LinearLayoutManager(requireActivity())
adapter = PostAdapter(dummyList, this#RecyclerViewFragment)
}
}
override fun onEditPost(position: Int){
val clickedPost = dummyList[position]
clickedPost.title = "Updated title"
clickedPost.body = "Updated body"
adapter.notifyItemChanged(position)
}
override fun onDeletePost(position: Int) {
dummyList.removeAt(position)
adapter.notifyItemRemoved(position)
}
}
Here is how my Adapter looks like:
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.MainActivity
import com.example.projectdrivemark.R
class PostAdapter(val dummyData: ArrayList<Post>, val myListener: OnPostClickListener) : RecyclerView.Adapter<PostAdapter.PostViewHolder>() {
inner class PostViewHolder(postView: View) : RecyclerView.ViewHolder(postView), View.OnClickListener{
val iconImage: ImageView = postView.findViewById(R.id.icon_image_view)
val title: TextView = postView.findViewById(R.id.title)
val body: TextView = postView.findViewById(R.id.body)
val deleteIcon: ImageView = postView.findViewById(R.id.delete_post_image)
val editIcon: ImageView = postView.findViewById(R.id.edit_post_image)
init {
deleteIcon.setOnClickListener(this)
editIcon.setOnClickListener(this)
}
override fun onClick(v: View?){
val position = adapterPosition
if(v?.id == editIcon.id){
myListener.onEditPost(position)
}else{
myListener.onDeletePost(position)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder {
val postView = LayoutInflater.from(parent.context).inflate(R.layout.post, parent, false)
return PostViewHolder(postView)
}
override fun onBindViewHolder(holder: PostViewHolder, position: Int) {
val currentPost = dummyData[position]
holder.iconImage.setImageResource(currentPost.image)
holder.title.text = currentPost.title
holder.body.text = currentPost.body
}
override fun getItemCount(): Int {
return dummyData.size
}
interface OnPostClickListener{
fun onEditPost(position: Int)
fun onDeletePost(position: Int)
}
}
And here is how my XML File Post looks like:
<?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:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:padding="5dp">
<ImageView
android:id="#+id/icon_image_view"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:src="#drawable/ic_baseline_ac_unit"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginBottom="8dp"
android:text="Title of blog post"
android:textColor="#android:color/black"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="#+id/textView2"
app:layout_constraintStart_toEndOf="#+id/icon_image_view"
app:layout_constraintTop_toTopOf="#+id/icon_image_view" />
<TextView
android:id="#+id/body"
android:layout_width="285dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="Body of first blog post"
app:layout_constraintBottom_toBottomOf="#+id/icon_image_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.050"
app:layout_constraintStart_toEndOf="#+id/icon_image_view" />
<LinearLayout
android:id="#+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="end">
<ImageView
android:id="#+id/edit_post_image"
android:layout_width="60dp"
android:layout_height="50dp"
android:src="#drawable/ic_baseline_edit" />
<ImageView
android:id="#+id/delete_post_image"
android:layout_width="60dp"
android:layout_height="50dp"
android:src="#drawable/ic_baseline_delete_forever" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
As you see in the RecyclerViewFragment, I have 2 functions that is onEditPost and onDeletePost. I want that functionality to work when I press it. Right now, it only works when I press and change the fragment view. I want it to work instantly. If anyone see my mistake or Am I missing something? please do tell me. All help is appreciate
You are creating Adapter twice, you already have an Adapter initialised globally use it in onViewCreated instead of assign again. because you are using global adapter to notify changes.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<RecyclerView>(R.id.recycleViewMain).apply{
layoutManager = LinearLayoutManager(requireActivity())
adapter = this#RecyclerViewFragment.adapter
}
}
Related
I'm hoping to create a checkbox for items that I have in a list.
I was looking at applying this by using a boolean value for my items i.e if attribute 'favourite' = true, then checkbox is highlighted and vice versa if false.
Unfortunately, so far I am unsure of the logic for this and how to connect it to my view (Checkbox).
My view is as follows:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
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:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:elevation="24dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/relativeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<ImageView
android:id="#+id/imageIcon"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:contentDescription="#string/change_hike_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/hikeName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textAlignment="viewStart"
android:textSize="30sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.048"
app:layout_constraintStart_toEndOf="#+id/imageIcon"
app:layout_constraintTop_toTopOf="parent"
tools:text="A Title" />
<TextView
android:id="#+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textAlignment="viewStart"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.052"
app:layout_constraintStart_toEndOf="#+id/imageIcon"
app:layout_constraintTop_toBottomOf="#id/hikeName"
tools:text="A Description" />
<CheckBox
android:id="#+id/favourite"
style="?android:attr/starStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
Adapter:
package org.wit.hikingtrails.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import org.wit.hikingtrails.databinding.CardHikeBinding
import org.wit.hikingtrails.models.HikeModel
interface HikeListener {
fun onHikeClick(hike: HikeModel)
}
class HikeAdapter constructor(private var hikes: List<HikeModel>,
private val listener: HikeListener) :
RecyclerView.Adapter<HikeAdapter.MainHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainHolder {
val binding = CardHikeBinding
.inflate(LayoutInflater.from(parent.context), parent, false)
return MainHolder(binding)
}
override fun onBindViewHolder(holder: MainHolder, position: Int) {
val hike = hikes[holder.adapterPosition]
holder.bind(hike, listener)
}
override fun getItemCount(): Int = hikes.size
class MainHolder(private val binding : CardHikeBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(hike: HikeModel, listener: HikeListener) {
binding.hikeName.text = hike.name
binding.description.text = hike.description
binding.favourite.text = hike.favourite.toString()
if (hike.image != ""){
Picasso.get()
.load(hike.image)
.resize(200, 200)
.into(binding.imageIcon)
}
binding.root.setOnClickListener { listener.onHikeClick(hike) }
}
}
}
View:
package org.wit.hikingtrails.views.hikeList
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import org.wit.hikingtrails.R
import org.wit.hikingtrails.adapters.HikeAdapter
import org.wit.hikingtrails.adapters.HikeListener
//import org.wit.hikingtrails.databinding.ActivityHikeListBinding
import org.wit.hikingtrails.main.MainApp
import org.wit.hikingtrails.models.HikeModel
import android.content.Intent
import android.view.*
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.wit.hikingtrails.databinding.ActivityHikeListBinding
import org.wit.hikingtrails.views.BaseView
import timber.log.Timber
class HikeListView : BaseView(), HikeListener {
lateinit var app: MainApp
lateinit var binding: ActivityHikeListBinding
lateinit var presenter: HikeListPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHikeListBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.toolbar.title = title
setSupportActionBar(binding.toolbar)
presenter = HikeListPresenter(this)
val layoutManager = LinearLayoutManager(this)
binding.recyclerView.layoutManager = layoutManager
updateRecyclerView()
}
// override fun showHikes(hikes: List<HikeModel>) {
// recyclerView.adapter = HikeAdapter(hikes, this)
// recyclerView.adapter?.notifyDataSetChanged()
// }
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onResume() {
//update the view
super.onResume()
updateRecyclerView()
binding.recyclerView.adapter?.notifyDataSetChanged()
Timber.i("recyclerView onResume")
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item?.itemId) {
R.id.item_add -> presenter.doAddHike()
R.id.item_map -> presenter.doShowHikesMap()
R.id.logout -> presenter.doLogout()
}
return super.onOptionsItemSelected(item)
}
override fun onHikeClick(hike: HikeModel) {
presenter.doEditHike(hike)
}
private fun updateRecyclerView(){
GlobalScope.launch(Dispatchers.Main){
binding.recyclerView.adapter =
HikeAdapter(presenter.getHikes(), this#HikeListView)
}
}
}
Add favorite property to your HikeModel like this
data class HikeModel(
... code
// use mutable property bcs you will update it later when checkbox is checked or uncheck.
var favorite: Boolean = false // false by default
)
and then
fun bind(hike: HikeModel, listener: HikeListener) {
...
binding.favourite.isChecked = hike.favorite
binding.favourite.setOnClickListener {
hike.favorite = binding.favourite.isChecked
listener.onHikeClick(hike)
}
...
}
I am currently trying to make a app that calculates a loan plan. User plots in data like loan amount, downpayment years, and how much rent the loan should have. I have implemented edit text for the loan amount, two seekbars for years and rent number.
Since I am fairly new to android programing I am nautraly struggling with implementing MVVM to my app. I implemented databinding, viewmodel and wrote the code to link them togehter. I also get the value out if I use Init in viewmodel, but I dont seem to transfer Int value from seekbar to the next fragment. Do anyone has suggestion to my code?
I am gratefull for any help.
In AnnuitetFragment.kt fragment I have only implemented modelView to RenterAnnSeekBar since I use it for testing.
package com.example.lnognedbetalingsplan.annuitet
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.example.lnognedbetalingsplan.R
import com.example.lnognedbetalingsplan.databinding.FragmentAnnuitetBinding
import com.example.lnognedbetalingsplan.viewmodel.AnnuitetViewModel
class AnnuitetFragment : Fragment() {
private var _binding: FragmentAnnuitetBinding? = null
private val binding get() = _binding!!
private lateinit var viewModel: AnnuitetViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
_binding = FragmentAnnuitetBinding.inflate(inflater, container, false)
return binding.root
}
var startPoint = 0
var endPoint = 0
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = this
viewModel = ViewModelProvider(this)[AnnuitetViewModel::class.java]
binding.viewmodel = viewModel
binding.tilbakeTilVelkommen.setOnClickListener {
findNavController().navigate(R.id.action_annuitetFragment_to_velkommenFragment)
}
binding.BeregnLaanAnnuitet.setOnClickListener {
findNavController().navigate(R.id.action_annuitetFragment_to_annuitetPlanFragment)
}
binding.RenterAnnSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
binding.RenterTall.text = progress.toString()
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
if (seekBar != null){
startPoint = seekBar.progress
}
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
if (seekBar != null){
endPoint = seekBar.progress
}
}
})
binding.AarSerieSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
binding.AarTall.text = progress.toString()
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
if (seekBar != null){
startPoint = seekBar.progress
}
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
if (seekBar != null){
endPoint = seekBar.progress
}
}
})
}
}
AnnuitetViewModel.kt is the viewmodel class I am going to use to store livedata so it can share it between AnnuitetFragment.kt and AnnuitetPlanFragment.kt. If this code miss any please help me with some pointers.
package com.example.lnognedbetalingsplan.viewmodel
import android.widget.SeekBar
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
open class AnnuitetViewModel : ViewModel() {
val progress: MutableLiveData<Int> by lazy { MutableLiveData<Int>() }
val renterProgress: LiveData<Int>
get() = progress
init {
progress.value = 1
}
}
My plan AnnuitetPlanFragment.kt is to get data from AnnuitetFragment.kt and calculate the loan plan from user. But when I try to get the data from AnnuitViewModel.kt I get the mock data from Init but there is not data from val progress.
package com.example.lnognedbetalingsplan.annuitet
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.example.lnognedbetalingsplan.R
import com.example.lnognedbetalingsplan.databinding.FragmentAnnuitetBinding
import com.example.lnognedbetalingsplan.databinding.FragmentAnnuitetPlanBinding
import com.example.lnognedbetalingsplan.viewmodel.AnnuitetViewModel
import kotlin.math.pow
class AnnuitetPlanFragment : Fragment() {
private var _binding: FragmentAnnuitetPlanBinding? = null
private val binding get() = _binding!!
private lateinit var viewModel: AnnuitetViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
_binding = FragmentAnnuitetPlanBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.lifecycleOwner = this
viewModel = ViewModelProvider(this).get(AnnuitetViewModel::class.java)
binding.viewmodel = viewModel
viewModel.renterProgress.observe(viewLifecycleOwner, Observer { renteTall->
binding.tvTest.text = renteTall.toString()
})
binding.tilbakeTilAnnuitet.setOnClickListener {
findNavController().navigate(R.id.action_annuitetPlanFragment_to_annuitetFragment)
}
}
}
Last one is my fragment_annuitet.xml where I implemented databinding and seekbar gets progress data and show it in viewCreate.
<layout
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">
<data>
<import type="android.view.View"/>
<variable
name="viewmodel"
type="com.example.lnognedbetalingsplan.viewmodel.AnnuitetViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/annuitet_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".annuitet.AnnuitetFragment">
<SeekBar
android:id="#+id/RenterAnnSeekBar"
android:layout_width="300dp"
android:layout_height="30dp"
android:max="50"
android:min="0"
android:progress="#={viewmodel.progress}"
app:layout_constraintBottom_toTopOf="#+id/AarSerieSeekBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.306"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.84" />
<SeekBar
android:id="#+id/AarSerieSeekBar"
android:layout_width="300dp"
android:layout_height="30dp"
android:max="50"
android:min="0"
android:progress="5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.306"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
<EditText
android:id="#+id/laanebelop"
android:layout_width="196dp"
android:layout_height="56dp"
android:autofillHints=""
android:ems="10"
android:hint="#string/hint"
android:inputType="numberDecimal"
android:textColorHint="#757575"
android:visibility="visible"
android:text=""
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.641"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.248" />
<TextView
android:id="#+id/Laanebelop"
android:layout_width="103dp"
android:layout_height="40dp"
android:layout_marginTop="184dp"
android:text="#string/laaneBeløp"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.11"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/AarTall"
android:layout_width="35dp"
android:layout_height="30dp"
android:text="#string/_5"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.619"
app:layout_constraintStart_toEndOf="#+id/AarSerieSeekBar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
<TextView
android:id="#+id/RenterTall"
android:layout_width="35dp"
android:layout_height="30dp"
android:text="#={`` + viewmodel.progress}"
android:textSize="20sp"
android:visibility="#{viewmodel.progress != null? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.633"
app:layout_constraintStart_toEndOf="#+id/RenterAnnSeekBar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.383" />
<TextView
android:id="#+id/Rentenummer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/velg_rente"
app:layout_constraintBottom_toTopOf="#+id/RenterAnnSeekBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.097"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Laanebelop"
app:layout_constraintVertical_bias="0.692" />
<TextView
android:id="#+id/antallAar"
android:layout_width="62dp"
android:layout_height="21dp"
android:text="#string/antall_aar"
app:layout_constraintBottom_toTopOf="#+id/AarSerieSeekBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.097"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/RenterAnnSeekBar"
app:layout_constraintVertical_bias="0.666" />
<Button
android:id="#+id/BeregnLaanAnnuitet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="264dp"
android:text="#string/bergen_l_net"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.757"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/AarSerieSeekBar"
app:layout_constraintVertical_bias="1.0" />
<Button
android:id="#+id/tilbakeTilVelkommen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="68dp"
android:text="#string/tilbake"
app:layout_constraintBottom_toTopOf="#+id/laanebelop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="#+id/textView2"
android:layout_width="180dp"
android:layout_height="40dp"
android:layout_marginStart="32dp"
android:layout_marginTop="24dp"
android:text="#string/annuitet_l_n"
android:textStyle="bold"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="#+id/Laanebelop"
app:layout_constraintEnd_toStartOf="#+id/tilbakeTilVelkommen"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.066" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
I figured my own question out.
If found out if you share your modelview with the two fragments you want to share data bewteen, and with observer this is possible.
Added this code:
private val AnnuitetViewModel: AnnuitetViewModel by activityViewModels()
Also made observers for every val I wanted to transfer to the next fragment:
AnnuitetViewModel.loanNumber.observe(viewLifecycleOwner){loanNumber->
binding.laanebelop.setText(loanNumber)
}
AnnuitetViewModel.progressRenter.observe(viewLifecycleOwner){progressRenter->
binding.RenterTall.text = progressRenter
}
AnnuitetViewModel.progressAar.observe(viewLifecycleOwner){progressAar->
binding.AarTall.text = progressAar
}
A button listner trigger the transfer to livedata in the viewmodel:
binding.BeregnLaanAnnuitet.setOnClickListener {
AnnuitetViewModel.saveLoanNumber(binding.laanebelop.text.toString())
AnnuitetViewModel.saveProgressRenter(binding.RenterTall.text.toString())
AnnuitetViewModel.saveProgressAar(binding.AarTall.text.toString())
findNavController().navigate(R.id.action_annuitetFragment_to_annuitetPlanFragment)
}
In the viewModel I made some var and val that takes inn the data:
open class AnnuitetViewModel : ViewModel() {
var startPoint = 0
var endPoint = 0
val seekbarRenterProgress: MutableLiveData<Int> by lazy { MutableLiveData<Int>() }
val seekbarAarProgress: MutableLiveData<Int> by lazy { MutableLiveData<Int>() }
private var _loanNumber = MutableLiveData<String>()
val loanNumber: MutableLiveData<String> = _loanNumber
fun saveLoanNumber(newLoanNumber: String){
_loanNumber.value = newLoanNumber
}
private var _progressRenter = MutableLiveData<String>()
val progressRenter: MutableLiveData<String> = _progressRenter
fun saveProgressRenter(newProgressRenter: String){
_progressRenter.value = newProgressRenter
}
private var _progressAar = MutableLiveData<String>()
val progressAar: MutableLiveData<String> = _progressAar
fun saveProgressAar(newProgressAar: String){
_progressAar.value = newProgressAar
}
init {
seekbarRenterProgress.value = 5
seekbarAarProgress.value = 5
}
}
The seekBar and textview look like this in my xml:
<SeekBar
android:id="#+id/AarSerieSeekBar"
android:layout_width="300dp"
android:layout_height="30dp"
android:max="50"
android:min="0"
android:progress="#={viewmodel.seekbarAarProgress}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.306"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
<TextView
android:id="#+id/AarTall"
android:layout_width="35dp"
android:layout_height="30dp"
android:text="#={`` + viewmodel.seekbarAarProgress}"
android:textSize="20sp"
android:visibility="#{viewmodel.seekbarAarProgress != null? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.619"
app:layout_constraintStart_toEndOf="#+id/AarSerieSeekBar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
For test if the second fragment can read the data I wrote this code that observe the data and sends it to a textview that comes up when you run the app in a emulator:
AnnuitetViewModel.loanNumber.observe(viewLifecycleOwner, binding.tvTest::setText)
AnnuitetViewModel.progressRenter.observe(viewLifecycleOwner, binding.tvTest2::setText)
AnnuitetViewModel.progressRenter.observe(viewLifecycleOwner, binding.tvTest3::setText)
I wanted to implement searchview inside bottomsheetdialog fragment but i don't know how. I have this full screen bottom sheet dialog layout with a SearchView and Recyclerview in it:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="17dp"
android:layout_marginEnd="19dp"
android:src="#drawable/ic_close_green"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tv_institution_name"
style="#style/bo_texto_regular"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="21dp"
android:layout_marginTop="66dp"
android:letterSpacing="0.01"
android:text="Nome"
android:textColor="#757575"
android:textSize="14sp"
android:textStyle="normal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.SearchView
android:id="#+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginStart="21dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="27dp"
app:layout_constraintEnd_toEndOf="#id/tv_institution_name"
app:layout_constraintStart_toStartOf="#id/tv_institution_name"
app:layout_constraintTop_toBottomOf="#id/tv_institution_name"
app:searchIcon="#drawable/magnifying_glass" />
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="#ececec"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/searchView"
tools:listitem="#layout/search_item_list" />
</androidx.constraintlayout.widget.ConstraintLayout>
And this is my full screen BottomSheetDialogFragment :
import android.view.ViewGroup
import android.view.WindowManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.koin.android.ext.android.inject
class SearchDialog() : BottomSheetDialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = BottomSheetDialog(requireContext(), theme)
dialog.setOnShowListener {
val bottomSheetDialog = it as BottomSheetDialog
val parentLayout =
bottomSheetDialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
parentLayout?.let { it ->
val behaviour = BottomSheetBehavior.from(it)
setupFullHeight(it)
behaviour.state = BottomSheetBehavior.STATE_EXPANDED
}
}
return dialog
}
private fun setupFullHeight(bottomSheet: View) {
val layoutParams = bottomSheet.layoutParams
layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT
bottomSheet.layoutParams = layoutParams
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
var view = inflater.inflate(R.layout.dialog_search, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}
I wanted to make the data come from an array and that i could search it using searchview
My adapter :
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable
import androidx.recyclerview.widget.RecyclerView
class SearchAdapter(private val list: List<InstitutionModel>) : RecyclerView.Adapter<SearchViewHolder>() , Filterable{
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
val inflater = LayoutInflater.from(parent.context)
return SearchViewHolder(inflater, parent)
}
override fun getItemCount(): Int = list.size
override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
val dialogData: InstitutionModel = list[position]
holder.bind(dialogData)
}
override fun getFilter(): Filter {
TODO("Not yet implemented")
}
}
And this is the setOnQueryListener:
searchViewInstitutions.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
// searchView.clearFocus()
// if (data.toString().contains(query)) {
// mAdapter.filter.filter(query)
// }
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
mAdapter.filter.filter(newText)
return false
}
})
}
I am trying to display JSON data from a dummy server (json-server) using Retrofit2 and display it in a RecyclerView placed inside a fragment. So far the following events are taking place (found out using Log infos):
Main fragment CharactersListFragment.kt creation is initiated.
Only getItemCount() is being called. It is returning 0.
OnCreateViewHolder() and onBindViewHolder() are not being called.
Data is successfully fetched from the server and passed to the adapter (from CharactersListFragment.kt)
There are no compile time errors nor does my app crash, just the RecyclerView is empty.
Here are my code files:
Layout for fragment with RecyclerView: fragment_characters_list.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".CharactersListFragment">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/recycler_view"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Layout for RecyclerView row: fragment_character_recyclerview_list.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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/character_card_view"
android:layout_width="match_parent"
android:layout_height="300dp"
android:elevation="15dp"
app:cardBackgroundColor="#color/violet_forcard"
app:cardCornerRadius="3dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/char_thumnail_img"
android:layout_width="143dp"
android:layout_height="150dp"
android:layout_marginStart="24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.106"
tools:srcCompat="#tools:sample/avatars" />
<TextView
android:id="#+id/char_id_txt"
android:layout_width="190dp"
android:layout_height="53dp"
android:layout_marginTop="16dp"
android:fontFamily="#font/roboto"
android:text="Character ID: "
android:textAlignment="viewStart"
android:textColor="#color/white"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/char_thumnail_img"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/char_name_txt"
android:layout_width="190dp"
android:layout_height="53dp"
android:layout_marginTop="24dp"
android:fontFamily="#font/roboto"
android:text="Name: "
android:textAlignment="viewStart"
android:textColor="#color/white"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/char_thumnail_img"
app:layout_constraintTop_toBottomOf="#+id/char_id_txt" />
<TextView
android:id="#+id/char_descp_txt"
android:layout_width="359dp"
android:layout_height="wrap_content"
android:fontFamily="#font/roboto"
android:textAlignment="viewStart"
android:textColor="#color/white"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.461"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.918" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
Kotlin class for fragment: CharactersListFragment.kt
package com.example.marvelapp
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class CharactersListFragment : Fragment() {
private lateinit var charsAdapter: CharacterListAdapter
private lateinit var apiService: APIService
private var characters: MutableList<Character> = ArrayList()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
Log.i("INSIDE_FRAGMENT_ONCREATEVIEW", "Fragment creation initiated")
val v = inflater.inflate(R.layout.fragment_characters_list, container, false)
apiService = RestClient.client.create(APIService::class.java)
getCharacterList()
val recycler_view = v.findViewById(R.id.recycler_view) as RecyclerView
recycler_view.layoutManager = LinearLayoutManager(activity)
charsAdapter = CharacterListAdapter()
recycler_view.adapter = charsAdapter
return v
}
private fun getCharacterList(){
val call = apiService!!.get_characters()
call.enqueue(object: Callback<List<Character>> {
override fun onResponse(
call: Call<List<Character>>,
response: Response<List<Character>>
) {
val chars = response.body()
Log.i("get_character_succeeded_FOR_CALL", chars.toString())
if (chars != null){
characters.addAll(chars!!)
Log.i("character_ADD_CHECK_INSIDE_FRAGMENT", characters[0].toString())
charsAdapter.submitDataforcharList(characters)
}
}
override fun onFailure(call: Call<List<Character>>, t: Throwable) {
Log.e("get_character_failed", t.localizedMessage)
}
})
}
}
Kotlin class for adapter: CharacterListAdapter.kt
package com.example.marvelapp
import android.text.Layout
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.fragment_character_recyclerview_list.view.*
class CharacterListAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var charItems: List<Character> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
Log.i("INSIDE_ONCREATE_VIEWHOLDER", "reached")
return CharacterListViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.fragment_character_recyclerview_list,
parent,
false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when(holder){
is CharacterListViewHolder ->{
Log.i("VIEWHOLDER_FOUND", "proceeding to bind")
holder.bind(charItems.get(position))
}
}
}
override fun getItemCount(): Int {
Log.i("INSIDE_ADAPTER_GET_ITEM_COUNT", charItems.size.toString())
return charItems.size
}
fun submitDataforcharList (characterlist: List<Character>){
Log.i("INSIDE_ADAPTER_SUBMIT_DATA", characterlist[0].toString())
charItems = characterlist
}
class CharacterListViewHolder
constructor(view: View): RecyclerView.ViewHolder(view){
val char_id = view.findViewById<TextView>(R.id.char_id_txt)
val char_name = view.findViewById<TextView>(R.id.char_name_txt)
val char_descp = view.findViewById<TextView>(R.id.char_descp_txt)
fun bind(character: Character){
Log.i("INSIDE_VIEWHOLDER_BIND", character.toString())
val id = Integer.toString(character.charId)
val id_mess = "Character ID: $id"
char_id.setText(id_mess)
val char_name_mess = "Name: ${character.charName}"
char_name.setText(char_name_mess)
char_descp.setText(character.charDescp)
}
}
}
the Character model class:
package com.example.marvelapp
data class Character(
val charId: Int,
val charName: String,
val charDescp: String,
){
}
I am an android beginner and have been stuck at this for quite some time, any help will be highly appreciated.
Thanks in advance!
In the onCreateView of CharactersListFragment you are calling getCharacterList() and then you move on with your initialization of the recycler view. After the getCharacterList call completes, you might be getting some data and you are simply updating the data list in your adapter class but you are not informing your recyclerview to refresh the view to display the new data. So I think if you just call notifyDataSetChanged() on your adapter after setting the new data it should work.
The other way to solve the issue could be if you initialize your recyclerview after you've received the data from the getCharacterList() call and just show some loading icon in place of the recycler view while the data is being fetched.
getCharacterList() is called on UI thread (main thread) but communicating (requests and responses) between device and API server is happening in another thread, it means there is no guarantee that onResponse() will come earlier than recycler_view.adapter = charsAdapter and in reality, it mostly does not happens. Therefor, you must call charsAdapter.notifyDataSetChanged() after charsAdapter.submitDataforcharList(characters) to have the recyclerview represent data. Another more elegent way to do that is adding notifyDatasetChanged() inside submitDataforcharList():
fun submitDataforcharList (characterlist: List<Character>){
charItems = characterlist
notifyDatasetChanged()
}
I'm using Tmdb API and displaying it in a RecyclerView. The RecyclerView is not scrolling smoothly initially but then is working fine. I've tried to change Recyclerview height to 0dp or match_parent but it's still the same. I've tried also android:nestedScrollingEnabled="true". Please help me to fix this problem in the RecyclerView. Thank you in advance.
fragment_add.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.AddFragment">
<androidx.appcompat.widget.AppCompatAutoCompleteTextView
android:id="#+id/fragment_add_movieSeriesACT"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:background="#drawable/edit_text_background"
android:hint="#string/movie_or_series"
android:imeOptions="actionDone"
android:inputType="textEmailAddress"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:textColor="#color/colorPrimary"
android:textColorHint="#color/colorEight"
android:textCursorDrawable="#null"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/fragment_add_mainRV"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/fragment_add_movieSeriesACT" />
<ProgressBar
android:id="#+id/fragment_add_mainPB"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:indeterminateTint="#color/colorOne"
android:indeterminateTintMode="src_atop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
AddFragment.kt
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import com.martiandeveloper.muuvi.R
import com.martiandeveloper.muuvi.adapter.RecyclerViewMovieAdapter
import com.martiandeveloper.muuvi.viewmodel.AddViewModel
import kotlinx.android.synthetic.main.fragment_add.*
class AddFragment : Fragment() {
private lateinit var viewModel: AddViewModel
private val adapter = RecyclerViewMovieAdapter(arrayListOf())
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_add, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initUI()
}
private fun initUI() {
viewModel = ViewModelProviders.of(this).get(AddViewModel::class.java)
setRecyclerView()
observe()
viewModel.refreshData()
}
private fun setRecyclerView() {
fragment_add_mainRV.layoutManager = LinearLayoutManager(context)
fragment_add_mainRV.adapter = adapter
}
private fun observe() {
viewModel.movieList.observe(viewLifecycleOwner, Observer { movieList ->
movieList?.let {
adapter.updateMovieList(it)
}
})
viewModel.isError.observe(viewLifecycleOwner, Observer { isError ->
isError?.let {
setProgress(1F, true, View.GONE)
if (it) {
setToast(resources.getString(R.string.something_went_wrong_please_try_again_later))
}
}
})
viewModel.isLoading.observe(viewLifecycleOwner, Observer { isLoading ->
isLoading?.let {
if (it) {
setProgress(.5F, false, View.VISIBLE)
} else {
setProgress(1F, true, View.GONE)
}
}
})
}
private fun setProgress(alpha: Float, enable: Boolean, visible: Int) {
fragment_add_movieSeriesACT.alpha = alpha
fragment_add_mainRV.alpha = alpha
fragment_add_movieSeriesACT.isEnabled = enable
fragment_add_mainPB.visibility = visible
}
private fun setToast(text: String) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
}
}
AddViewModel.kt
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.martiandeveloper.muuvi.model.MovieResult
import com.martiandeveloper.muuvi.model.Movie
import com.martiandeveloper.muuvi.service.MovieService
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.observers.DisposableSingleObserver
import io.reactivex.schedulers.Schedulers
class AddViewModel : ViewModel() {
private val movieService = MovieService()
private val disposable = CompositeDisposable()
val movieList = MutableLiveData<List<Movie>>()
val isError = MutableLiveData<Boolean>()
val isLoading = MutableLiveData<Boolean>()
fun refreshData() {
isLoading.value = true
disposable.add(
movieService.getData().subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<MovieResult>() {
override fun onSuccess(t: MovieResult) {
movieList.value = t.results
isError.value = false
isLoading.value = false
}
override fun onError(e: Throwable) {
isError.value = true
isLoading.value = false
}
})
)
}
}
RecyclerViewMovieAdapter.kt
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.martiandeveloper.muuvi.R
import com.martiandeveloper.muuvi.model.Movie
import kotlinx.android.synthetic.main.recyclerview_movie_item.view.*
class RecyclerViewMovieAdapter(private val movieList: ArrayList<Movie>) :
RecyclerView.Adapter<RecyclerViewMovieAdapter.RecyclerViewMovieViewHolder>() {
lateinit var context: Context
class RecyclerViewMovieViewHolder(var view: View) : RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RecyclerViewMovieViewHolder {
context = parent.context
val view = LayoutInflater.from(context)
.inflate(R.layout.recyclerview_movie_item, parent, false)
return RecyclerViewMovieViewHolder(view)
}
override fun getItemCount(): Int {
return movieList.size
}
override fun onBindViewHolder(holder: RecyclerViewMovieViewHolder, position: Int) {
holder.view.recyclerview_movie_item_movieTitleMTV.text =
movieList[position].movieTitle
holder.view.recyclerview_movie_item_movieVoteAverageMTV.text =
movieList[position].movieVoteAverage.toString()
Glide.with(context)
.load("https://image.tmdb.org/t/p/w300${movieList[position].moviePosterPath}")
.into(holder.view.recyclerview_movie_item_moviePosterIV)
}
fun updateMovieList(newMovieList: List<Movie>) {
movieList.clear()
movieList.addAll(newMovieList)
notifyDataSetChanged()
}
}
recyclerview_movie_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="8dp"
android:paddingBottom="8dp">
<androidx.cardview.widget.CardView
android:layout_width="80dp"
android:layout_height="80dp"
app:cardBackgroundColor="#android:color/transparent"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<ImageView
android:id="#+id/recyclerview_movie_item_moviePosterIV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/app_name"
android:scaleType="centerCrop"
app:srcCompat="#drawable/default_user_image" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="#+id/recyclerview_movie_item_movieTitleMTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/colorPrimary"
android:textSize="18sp"
android:textStyle="bold" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/recyclerview_movie_item_movieVoteAverageMTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/colorPrimaryDark" />
</LinearLayout>
</LinearLayout>
I believe this is due to size of the image , I had similar issue try to fit the image you obtain using glide into imageview, I have added centerCrop():
Glide.with(context).load("https://image.tmdb.org/t/p/w300${movieList[position].moviePosterPath}") .centerCrop().into(holder.view.recyclerview_movie_item_moviePosterIV);
If this does not work try to change imageview scaletype or centerCrop() to fitCenter()