I'm using MVVC pattern and I'm populating a recyclerView with data from database using Room. At the Logcat, data return correctly and is looped correctly, but recyclerview display seven elements and in the eighth starts to overwrite it with the nineth and tenth elements e after that create 2 more elements with the first 2 elements from list.
I'm coudn't find what is wrong with my code.
So, I'm asking for some help.
AvaliacaoFragment.kt:
class AvaliacaoFragment : Fragment() {
private lateinit var ctx: Context
private var _binding: FragmentAvaliacaoBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
private lateinit var textoSemSecoes: TextView
private lateinit var nomeAvaliacao: TextView
private lateinit var dataAvaliacao: TextView
private val args by navArgs<AvaliacaoFragmentArgs>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
_binding = FragmentAvaliacaoBinding.inflate(inflater, container, false)
return binding.root
// return inflater.inflate(R.layout.fragment_avaliacoes, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ctx = view.context
// dialogNovaAvaliacao = MaterialAlertDialogBuilder(ctx, android.R.style.Theme_DeviceDefault_Light_NoActionBar_Fullscreen)
// dialogNovaAvaliacao = MaterialAlertDialogBuilder(ctx,R.style.AlertDialogTheme)
// val builder = MaterialDatePicker.Builder.datePicker()
textoSemSecoes = view.findViewById(R.id.texto_sem_secoes)
nomeAvaliacao = view.findViewById(R.id.nome_avaliacao)
dataAvaliacao = view.findViewById(R.id.data_avaliacao)
nomeAvaliacao.text = args.currentAvaliacao.nome
dataAvaliacao.text = args.currentAvaliacao.dataCriacao
/*
btnAddAvaliacao = view.findViewById(R.id.btn_add_avaliacao)
btnAddAvaliacao.setOnClickListener {
findNavController().navigate(R.id.action_navigation_avaliacoes_to_addAvaliacaoFragment)
}
*/
// Recycler
val recyclerAdapter = SecaoAdapter()
val recyclerView = binding.secaoRecyclerView
recyclerView.adapter = recyclerAdapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
// ViewModelFactory para passar argumentos para a ViewModel
val factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return SecaoViewModel(Application(), args.currentAvaliacao.id) as T
}
}
// ViewModel
mSecaoViewModel = ViewModelProvider(this, factory).get(SecaoViewModel::class.java)
mSecaoViewModel.readAllData.observe(viewLifecycleOwner, Observer { secaoList ->
if(secaoList.isNotEmpty()){
Log.d(TAG, "secaoList: ${secaoList.toString()}")
Log.d(TAG, "secaoList.size: ${secaoList.size}")
textoSemSecoes.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
recyclerAdapter.setData(secaoList.sortedBy { it.codigo.toInt() })
} else {
textoSemSecoes.visibility = View.VISIBLE
recyclerView.visibility = View.GONE
}
})
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
private val TAG: String = AvaliacaoFragment::class.java.name
lateinit var mSecaoViewModel: SecaoViewModel
private lateinit var btnAddSecao: FloatingActionButton
private lateinit var dialogNovaSecao: MaterialAlertDialogBuilder
}
}
SecaoAdapter.kt:
class SecaoAdapter: RecyclerView.Adapter<SecaoAdapter.SecaoViewHolder>() {
private val TAG: String = SecaoAdapter::class.java.name
private var secaoList = emptyList<Secao>()
private lateinit var binding: ItemSecaoBinding
class SecaoViewHolder(itemBinding: ItemSecaoBinding): RecyclerView.ViewHolder(itemBinding.root) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SecaoViewHolder {
binding = ItemSecaoBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SecaoViewHolder(binding)
}
override fun getItemCount(): Int {
return secaoList.size
}
override fun onBindViewHolder(holder: SecaoViewHolder, position: Int) {
Log.d(TAG, "position: $position")
val currentItem = secaoList[position]
Log.d(TAG, "currentItem: ${currentItem.toString()}")
binding.secaoCodigo.text = currentItem.codigo
binding.secaoNome.text = currentItem.nome
binding.secaoMediaTotal.text = currentItem.mediaPositivo.toString()
binding.secaoPerguntasNaoAplicaveis.text = currentItem.qdePerguntasNaoAplicaveis.toString()
binding.secaoPerguntasRespondidas.text = currentItem.qdePerguntasRespondidas.toString()
binding.secaoPerguntasTotais.text = currentItem.qdePerguntas.toString()
binding.cardSecao.setOnClickListener {
val action = AvaliacaoFragmentDirections.actionAvaliacaoFragmentToSecaoFragment(currentItem)
holder.itemView.findNavController().navigate(action)
}
}
fun setData(secao: List<Secao>){
this.secaoList = secao
notifyDataSetChanged()
}
}
fragment_avaliacao.xml:
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimaryDark"
tools:context=".ui.secoes.AvaliacaoFragment">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/container_titulo_avaliacao"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
>
<TextView
android:id="#+id/nome_avaliacao"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="#string/padrao_avaliacao_sem_nome"
android:textSize="24sp"
android:textColor="#color/colorIcons"
android:textAlignment="center"
/>
<TextView
android:id="#+id/data_avaliacao"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="#string/padrao_formato_data_hora"
android:textSize="14sp"
android:textColor="#color/colorPrimaryLight"
android:textAlignment="center"
/>
</LinearLayout>
<TextView
android:id="#+id/texto_sem_secoes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#+id/container_titulo_avaliacao"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="16sp"
android:text="#string/nenhuma_secao_criada"
android:textColor="#color/colorPrimaryLight"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/secaoRecyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#+id/container_titulo_avaliacao"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout></androidx.constraintlayout.widget.ConstraintLayout>
Logcat loop from secaoList at the SecaoAdapter (is looping correctly to all elements):
I create a gif to show what is happening with the elements on recyclerView:
Here you can see that it displays seventh element then eighth is replaced by nineth and subsequently replaced by tenth. And final two elements (that should be ninth and tenth) is constructed with first and second list elements.
Thanks for the help in advance.
So the mistake here is that you are referencing only one binding in your adapter which is getting overwritten. Every time you call onCreateViewHolder you are changing the binding reference. The reason this looks okay to start with is that the onCreateViewHolder calls are followed by the onBindViewHolder calls for items visible on the screen. However as you scroll, just onBindViewHolder is called in order to rebind the recycled views.
What you should be doing is using your ViewHolder to store the individual bindings and then obtaining a reference in onBindViewHolder with something like holder.binding.
I would recommend you have a read into the view holder pattern and how to implement it!
Related
I show a list of items using adapter and recyclerview. Each item shows only 4 lines of biography-textView. In order to see whole biography, user clicks 'expand' textView. My code functions but there is a problem because sometimes (somehow randomly) one 'expand' click (f.e. in item No 1) causes expanding 2-3 biography textView more in another items(f.e. items No 7 and 12). What is wrong with my code? How to check it/identify why items textView are extra expand?
Can anyone help?
class ShowMastersAdapter (
private val masterList: ArrayList<Master>,
private val itemListener: OnItemClickListener
) : RecyclerView.Adapter<ShowMastersAdapter.ShowMasterViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShowMasterViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_master, parent, false)
return ShowMasterViewHolder(view)
}
override fun onBindViewHolder(holder: ShowMasterViewHolder, position: Int) {
holder.bindData(position)
}
override fun getItemCount(): Int {
return masterList.size
}
inner class ShowMasterViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bindData(position: Int) {
val masterBiography = itemView.findViewById<TextView>(R.id.item_master_biography)
val masterExpandBiography =itemView.findViewById<TextView>(R.id.item_master_expand_text)
masterBiography.text = masterList[position].biography
masterExpandBiography.setOnClickListener {
itemListener.onReadMoreTextClick(masterExpandBiography, masterBiography)
}
}
}
interface OnItemClickListener {
fun onReadMoreTextClick(expandText: TextView, biographyText: TextView)
}
}
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/layout_top_part">
<TextView
android:id="#+id/item_master_biography"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/item_master_margin_start"
android:layout_marginEnd="#dimen/item_master_margin_end"
android:ellipsize="end"
android:maxLines="4"
android:text="unknown"
android:textSize="#dimen/font_main_size_item_master"
app:layout_constraintBottom_toTopOf="#+id/item_master_expand_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/item_master_biography_label" />
<TextView
android:id="#+id/item_master_expand_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="#string/it_master_expand_text"
android:textColor="#color/text_as_link"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
class FragmentShowMasters() : Fragment(), ShowMastersAdapter.OnItemClickListener{
private var listOfMasters: ArrayList<Master> = ArrayList()
private lateinit var myRecycler: RecyclerView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState:Bundle?): View {
myView = inflater.inflate(R.layout.fragment_show_masters, container, false)
myRecycler = myView.findViewById(R.id.recycler_show_masters)
myRecycler.setHasFixedSize(true)
myRecycler.adapter = ShowMastersAdapter(listOfMasters, this, highlightSearchText)
myRecycler.adapter?.notifyDataSetChanged()
myRecycler.layoutManager = LinearLayoutManager(this.context)
return myView
}
override fun onReadMoreTextClick(expandText: TextView, biographyText: TextView) {
if (biographyText.maxLines == 4) {
biographyText.maxLines = Int.MAX_VALUE
expandText.text = getString(R.string.it_master_shrink_text)
} else {
biographyText.maxLines = 4
expandText.text = getString(R.string.it_master_expand_text)
}
}
}
I am new to mobile development with kotlin and I am trying to add a new item to the recyclerview.
The item is added to the recycler view but the text is not visible. I don't get what it's wrong.
I am using Kotlin in Android Studio 4.2
The class AgentAttributes only contains two properties:val Name:String ,val Value:String
**activity_main.xml**
<EditText
android:id="#+id/etSearch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="No emp"
app:layout_constraintEnd_toStartOf="#+id/btnSearch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/btnSearch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#android:string/search_go"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvAgentAttributes"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/etSearch" />
**MainActivity.kt**
private lateinit var agentAttributesAdapter:AgentAttributesAdapter
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//setContentView(R.layout.activity_main)
//constructor
agentAttributesAdapter = AgentAttributesAdapter(mutableListOf())
//Definition of Recycler view adapter
binding.rvAgentAttributes.adapter = agentAttributesAdapter
binding.rvAgentAttributes.layoutManager = LinearLayoutManager(this)
//THIS IS THE WAY THAT I AM TRYING TO ADD A NEW ITEM
val agentAttr = AgentAttributes("Nombre","JOEL ROMUALDO LOPEZ SALIDO")
agentAttributesAdapter.addAttributes(agentAttr)
**AgentAttributesAdapter**
class AgentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AgentViewHolder {
return AgentViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.agent_details,
parent,
false
)
)
override fun onBindViewHolder(holder: AgentViewHolder, position: Int) {
val layoutInflater = LayoutInflater.from(holder.itemView.context)
binding = AgentDetailsBinding.inflate(layoutInflater)
//val view = binding.root
val currentAgent = agent_attributes[position]
holder.itemView.apply {
binding.tvAttribute.text = currentAgent.Name.toString()
}
}
override fun getItemCount(): Int {
return agent_attributes.size
}
fun addAttributes(attr:AgentAttributes){
agent_attributes.add(attr)
notifyItemInserted(agent_attributes.size - 1)
}
There's no need to inflate your layout in the "binding" step:
override fun onBindViewHolder(viewHolder: ChoicesViewHolder, pos: Int) {
AgentDetailsBinding.bind(viewHolder.itemView).apply {
val currentAgent = agent_attributes[position]
tvAttribute.text = currentAgent.Name.toString()
}
}
PS: variables should be lower case, so currentAgent should have a name, no Name
I am brand new in Android/kotlin development. I created my very first app with a recyclerView to display the folders and files of the phone. I put the recyclerView into a segment, created my data structure and adapter. Assembled together. It works - or seems to work. But the problem is that when I try to scroll the list, the initial state remains "ther" and the list content starts to scroll. Like it was two different layers. I have no clue where to find the problem. Never heard about such a bug like this. Please give me advice, some keywords where to dig and find the solution. Thanks!
class BrowseFileFragment : Fragment() {
private lateinit var attachedCtx : Context
override fun onAttach(context: Context) {
super.onAttach(context)
this.attachedCtx = context
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View?
{
var view : View = inflater.inflate(R.layout.fragment_browse_file, container, false)
val modelFactory = FileBrowseModelFactory()
val data = modelFactory.create()
bindModelToView(data, view)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
private fun bindModelToView(data: BrowseSettings, view: View)
{
var adapter = MyFileAdapter(data.FoldersAndFiles, this.attachedCtx)
var recyclerView = view.findViewById<RecyclerView>(R.id.fileItemView)
var linearLayoutManager = LinearLayoutManager(this.attachedCtx)
recyclerView.layoutManager = linearLayoutManager
recyclerView.adapter = adapter
var folderLabel = view.findViewById<TextView>(R.id.folderName)
folderLabel.text = data.CurrentFolder
}
}
The segment layout
<?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"
tools:context=".BrowseFileFragment">
<TextView
android:id="#+id/folderName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="#string/hello_first_fragment"
android:textAlignment="viewStart"
android:textAppearance="#style/TextAppearance.AppCompat.Display1"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/fileItemView"
android:layout_width="match_parent"
app:layout_constrainedHeight="true"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/folderName"
app:layout_constraintStart_toStartOf="#+id/folderName"
app:layout_constraintTop_toBottomOf="#+id/folderName" />
</androidx.constraintlayout.widget.ConstraintLayout>
The list line layout
<?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="60dp">
<TextView
android:background="#color/cardview_shadow_start_color"
android:id="#+id/itemName"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toTopOf="parent"
android:padding="5dp"
android:textSize="30dp"
android:textStyle="bold" />
</androidx.constraintlayout.widget.ConstraintLayout>
And finally my adapter:
class MyFileAdapter(private val items: List<FileItem>, private val context: Context)
: RecyclerView.Adapter<MyFileAdapter.MyViewHolder>()
{
class MyViewHolder (itemView: View) :RecyclerView.ViewHolder(itemView), View.OnClickListener {
init {
itemView.setOnClickListener(this)
}
fun bindItem(f: FileItem) {
var name: TextView = itemView.findViewById(R.id.itemName) as TextView
name.text = f.Name
}
}
override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater
.from(context)
.inflate(R.layout.recyclerview_filteitem_row, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
var item = items\[position\]
holder!!.bindItem( item )
}
}
It found out that as the whole list view is saved as a segment, this segment loads "automatically" and was loaded programatically also, and the two "loads" causes this thing. :( Thanks for your time guys.
I want to make UI like this:
as you can see, there is a vertical recycler view there. that recycler view is inside scroll view. so I want to make the screen can be scrolled down as much as item data in the recycler view. here is the xml I use:
<ScrollView
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="#android:color/white"
android:id="#+id/scrollView_user_control"
tools:context=".Fragments.UserControl.UserControlFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/constraintLayout_profile">
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/imageView_profile_image_user_control"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="#drawable/user"
app:civ_border_width="0.5dp"
app:civ_border_color="#FF000000"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginStart="8dp"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="32dp"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:text="User Fullname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textView_user_fullname_user_control"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="#+id/imageView_profile_image_user_control"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp" android:textSize="18sp"/>
<TextView
android:text="user#email.com"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textView_user_email_user_control"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp"
app:layout_constraintTop_toBottomOf="#+id/textView_user_fullname_user_control"
android:layout_marginTop="8dp"/>
<TextView
android:text="AKAN DIHADIRI"
android:layout_width="0dp"
android:layout_height="35dp"
android:id="#+id/textView_will_come_user_control"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="32dp"
app:layout_constraintTop_toBottomOf="#+id/textView_user_email_user_control"
android:gravity="center|left"
android:paddingStart="16dp" android:background="#D2D1D6"/>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/textView_will_come_user_control"
tools:listitem="#layout/item_general_event"
android:id="#+id/recyclerView_attended_event_user_control"/>
<ImageView
android:src="#drawable/ic_settings"
android:layout_width="30dp"
android:layout_height="30dp"
android:id="#+id/imageView_gear_setting_user_control"
app:layout_constraintTop_toTopOf="#+id/imageView_profile_image_user_control"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="24dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
if I fetch 20 data from server and set it those 20 items to the recycler view, it will make my app slow, I mean the fragment will appear around 2 seconds after fragment transaction.
If I limit the data to be only one data that fetched from server that need to be shown to recycler view. it will make my app faster.
so it clears that, the more items in the recycler view inside scroll view, the more slower it takes to show the fragment.
here is my code :
class UserControlFragment : Fragment() {
lateinit var progressBar : ProgressBar
lateinit var gearSettingImageView: ImageView
lateinit var emailTextView: TextView
lateinit var userFullNameTextView :TextView
lateinit var profilePictureImageView : ImageView
lateinit var fragmentView : View
lateinit var recyclerView : RecyclerView
lateinit var sharedPrefManager : SharedPreferenceManager
lateinit var eventAdapter : GeneralEventRecyclerViewAdapter
private var attendedEvents = ArrayList<Event>()
private var userData : User? = null
lateinit var mContext : Context
lateinit var mActivity : FragmentActivity
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context
activity?.let { mActivity = it }
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
fragmentView = inflater.inflate(R.layout.fragment_user_control, container, false)
sharedPrefManager = SharedPreferenceManager.getInstance(mContext)
setUpViewsDeclaration()
getUserData()
updateUI()
initRecyclerView()
return fragmentView
}
private fun setUpViewsDeclaration() {
progressBar = mActivity.progressBar_main_activity
gearSettingImageView = fragmentView.findViewById(R.id.imageView_gear_setting_user_control)
emailTextView = fragmentView.findViewById(R.id.textView_user_email_user_control)
profilePictureImageView = fragmentView.findViewById(R.id.imageView_profile_image_user_control)
userFullNameTextView = fragmentView.findViewById(R.id.textView_user_fullname_user_control)
recyclerView = fragmentView.findViewById(R.id.recyclerView_attended_event_user_control)
}
private fun getUserData() {
userData = sharedPrefManager.loadUserData()
if (userData == null) {
performLogOut()
return
}
getAttendedEvents()
}
private fun updateUI() {
userData?.let {
userFullNameTextView.text = it.fullname
emailTextView.text = it.email
Picasso.get()
.load(it.profilePicturePath)
.into(profilePictureImageView)
}
}
private fun initRecyclerView() {
eventAdapter = GeneralEventRecyclerViewAdapter(mContext)
val layoutManager = LinearLayoutManager(mContext, RecyclerView.VERTICAL,false)
recyclerView.adapter = eventAdapter
recyclerView.layoutManager = layoutManager
recyclerView.isNestedScrollingEnabled = false
eventAdapter.setOnItemClickListener(object: OnEventKMListener {
override fun eventKMClicked(position: Int) {
val selectedEvent = attendedEvents[position]
val eventDetailDestination = UserControlFragmentDirections.actionGlobalDestinationEventDetail(selectedEvent)
Navigation.findNavController(fragmentView).navigate(eventDetailDestination)
}
})
}
private fun getAttendedEvents() {
showLoadingState(true)
FirestoreKMClient.getRecommendedEvents(userData!!.domicile) { errorMessage, events ->
errorMessage?.let {
activity?.toast(it)
} ?: run {
val eventList = events ?: ArrayList()
attendedEvents = eventList
eventAdapter.submitList(eventList)
showLoadingState(false)
}
}
}
private fun showLoadingState(enabled: Boolean) {
if (enabled) {
progressBar.visibility = View.VISIBLE
recyclerView.visibility = View.GONE
} else {
progressBar.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
}
}
}
I have tried to move the background processing from onCreateView to onResume but it doesn't make any effect.
so how to solve this ? Java is OK
I am trying to develop social networking app which uses Android Jetpack Libraries but while using Navigation Component to use bottom navigation to navigate through fragments inside an activity , This Adapter throws an error at LayoutInflator() which causes app to crash
Can anyone help me through this:
My Adapter Class:
class FeedAdapter : PagedListAdapter<feed,FeedAdapter.ViewHolder>(FeedDiffCallBack()){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val userPost = LayoutInflater.from(parent.context)
.inflate(R.layout.feedrow,parent,false)
return ViewHolder(userPost)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val feedItem = getItem(position)
if(feedItem != null){
holder.bind(feedItem)
}
}
class ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView) {
//Retrieve data
private val username:TextView = itemView.post_name
private val userPic:ImageView = itemView.feedImage1
private val location:TextView = itemView.postLocation
private val time:TextView = itemView.postTime
private val post:ImageView = itemView.postImage
fun bind(feed: feed) = with(itemView){
//TODO:Bind Data with View
showFeedData(feed)
}
private fun showFeedData(feed: feed) {
username.text = feed.username
userPic.setImageURI(null)
userPic.visibility = View.GONE
location.text = feed.location
time.text = feed.timeStamp.toString()
post.setImageURI(Uri.parse(feed.mUrl))
}
}
}
class FeedDiffCallBack : DiffUtil.ItemCallback<feed>() {
override fun areItemsTheSame(oldItem:feed, newItem: feed): Boolean {
return oldItem?.id == newItem?.id
}
override fun areContentsTheSame(oldItem: feed, newItem: feed): Boolean {
return oldItem == newItem
}
}
Fragment Class:
class FeedFragment : Fragment() {
companion object {
fun newInstance() = FeedFragment()
}
private lateinit var viewModel: FeedViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.feed_fragment, container, false)
val context = getContext() ?: return view
val factory = InjectorUtils.provideViewModelFactory(context)
viewModel =
ViewModelProviders.of(this,factory).get(FeedViewModel::class.java)
val adapter = FeedAdapter()
view.findViewById<RecyclerView>(R.id.feedView).adapter = adapter
view.findViewById<RecyclerView>(R.id.feedView).layoutManager =
LinearLayoutManager(MyApplication.getContext())
subscribeUI(adapter)
return view
}
private fun subscribeUI(adapter: FeedAdapter) {
viewModel.showFeed().observe(this, object:Observer<PagedList<feed>>{
override fun onChanged(t: PagedList<feed>?) {
adapter.submitList(t)
adapter.notifyDataSetChanged()
}
})
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
}
}
feed_row.xml
Individual item for recycler view -->
<RelativeLayout
android:id="#+id/postContainer"
android:layout_margin="10dp"
android:elevation="2dp"
android:background="#drawable/bg_parent_rounded_corner"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--TODO:Change to Circle Image View-->
<ImageView
android:id="#+id/profileImage"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="5dp"
android:layout_marginLeft="5dp"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
/>
<LinearLayout
android:id="#+id/postDetail_1"
android:orientation="vertical"
android:layout_marginTop="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="20dp"
android:layout_alignParentRight="true">
<TextView
android:id="#+id/post_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/LabelStyle"
android:textSize="15sp"
android:fontFamily="#font/sf_pro_display_semibold" />
<LinearLayout
android:id="#+id/postDetail_2"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/postLocation"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/postTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="120dp" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="#+id/postImage"
android:layout_height="200dp"
android:layout_width="match_parent"
android:layout_below="#+id/postDetail_1"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:layout_marginTop="6dp"
/>
</RelativeLayout>
Your issue might be in this method,
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val feedItem = getItem(position)
if(feedItem != null){
holder.bind(feedItem)
}
}
Your ViewHolder is using poisition from method parameter which might be inconsistent
See from here,
So, you should change line to this:
val feedItem = getItem(holder.adapterPosition)
instead of
val feedItem = getItem(position)
I hope it resolves the issue.
This might help some one looking for the same Exception to be cleared :
What I did is that my feed_row.xml above is included inside < layout > tags and I changed it in this way , So the exception got cleared:
Before Exception:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
After changing to this Exception cleared:
<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">
I don't know how it works but it did work!!! So Anyone who knows what happens there can explain please!