I am trying to Implement Recycler View in Activity after parsing data using Retrofit.But the problem is it shows Recycler view cannot be null even after initializing inside onCreate method before accessing.
Mainactitivty.kt
class MainActivity : AppCompatActivity() {
lateinit var myRecyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
retroInstance = RetroInstance()
val instance = retroInstance.getInstance()
val api = instance.create(RetroInterface::class.java)
val callAll=api.getAllDetail()
callAll.enqueue(object :retrofit2.Callback<ModelAll>{
override fun onFailure(call: Call<ModelAll>, t: Throwable) {
Toast.makeText(applicationContext,t.message,Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<ModelAll>, response: Response<ModelAll>) {
val allDetail=response.body()!!
myRecyclerView=findViewById(R.id.recyclerView)
myRecyclerView.layoutManager=LinearLayoutManager(this#MainActivity)
myRecyclerView.adapter=CoronaAdapter(allDetail)
}
})
}
}
Activity with recycler view included
<?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=".AllCountries">
<SearchView
android:id="#+id/searchView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="2dp"
android:background="#drawable/custom_search"
android:elevation="5dp"
android:queryHint="Search here..."
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/searchView" />
</androidx.constraintlayout.widget.ConstraintLayout>
Logcat
https://gist.github.com/devpawann/af3cef9d204a6f99cd7ed11937684fa2
EDIT:- Issue solved, the mistake was that I initializes recycler view in another activity class
You can maybe try something like this:
class MainActivity : AppCompatActivity() {
private var allDetails: MutableList<ModelAll> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
retroInstance = RetroInstance()
val instance = retroInstance.getInstance()
val api = instance.create(RetroInterface::class.java)
val callAll=api.getAllDetail()
//Init your recyclerview
val myRecyclerView = findViewById(R.id.recyclerView)
myRecyclerView.layoutManager=LinearLayoutManager(this)
val coronaAdapter = CoronaAdapter(allDetails)
myRecyclerView.adapter = adapter
callAll.enqueue(object :retrofit2.Callback<ModelAll>{
override fun onFailure(call: Call<ModelAll>, t: Throwable) {
Toast.makeText(applicationContext,t.message,Toast.LENGTH_LONG).show()
}
override fun onResponse(call: Call<ModelAll>, response: Response<ModelAll>) {
if(response.isSucessful()){
allDetails = response.body()!!
coronaAdapter.notifyDataSetChanged()
}
}
})
}
EDIT: The issue was that the recyclerView was in another layout that the one inflated.
Your recyclerView has not been instantiated(currently it is referring to null).
Add this line
recyclerview = findViewById(R.id.recyclerView)
Or if you are using Android Extensions then make sure you are using correct recyclerview
Related
I have implemented a MaterialToolbar and I want to change the title and functionality of the buttons, depending on which fragment is active. But I've been trying for two days and I can't get access to toolbar from fragment.
It doesn't work with any option:
activity?.actionBar?.title
(activity as AppCompatActivity).supportActionBar?.title
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
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=".view.MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/materialToolbar"
style="#style/Widget.MaterialComponents.Toolbar.Primary"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="#drawable/ic_menu" />
<fragment
android:id="#+id/fragmentContainer"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/materialToolbar"
app:navGraph="#navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.navigation.NavigationView
android:id="#+id/navigationView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#color/primary_ultra_light"
app:headerLayout="#layout/header_menu_drawer"
app:menu="#menu/menu_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.materialToolbar)
supportActionBar?.title = "Test"
}
}
HomeFragment.kt
class HomeFragment: Fragment() {
private lateinit var binding: HomeFragmentBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.actionBar?.title = "Home 1" /* actionBar == null */
/* supportActionBar == null */
(activity as AppCompatActivity).supportActionBar?.title = "Home 2"
}
Result of the code that does not comply with the expected
The idea is to remove the top block of the fragment (Calculators) and use the Toolbar (Test). For this I need to be able to adapt the toolbar for each fragment.
Possible solution <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
So far I have solved it with a ViewModel observer. I don't know if it's a correct solution, but it works. Any comment is welcome, both to confirm that it is a valid solution and if it is not.
ToolbarVM.kt
class ToolbarVM(app: Application) : AndroidViewModel(app) {
private val _title = MutableLiveData<String>()
var title: LiveData<String> = _title
fun setTitle(newTitle: String){
_title.value = newTitle
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val toolbarVm: ToolbarVM by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.materialToolbar)
val titleObserver = Observer<String> { newTitle ->
binding.materialToolbar.title = newTitle
}
toolbarVm.title.observe(this, titleObserver)
}
}
HomeFragment
/*You have to instantiate the ToolbarVm and call the setTitle() method*/
private lateinit var toolbarVm: ToolbarVM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
toolbarVm = activity?.let { ViewModelProvider(it)[ToolbarVM::class.java] }!!
}
// Call the method where necessary
toolbarVm.setTitle("Calculators")
From the MainActivity you can directly use:
binding.materialToolbar.title = "Test"
while when calling from a fragment, you can do the following:
activity?.findViewById<Toolbar>(R.id.materialToolbar)?.title = "Test"
I have solved it thanks to Razvan's answer. It works if I call it from the onViewCreated() function, but it doesn't work the first time the default fragment is loaded with navGraph. So I have placed it in onResume()
HomeFragment.kt
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar?.title = getString(R.string.calculators)
}
I am now developing an application where I use MVVM pattern for UI and repository interaction. In other words, I receive live data object with a list of models from my ROOM data base query via repository, then assign it to my live data variable in viewmodel. After that, this data should be populated to my xml layout recycler view via data binding, but It happens only once fragment is initialised. In other cases recycler view is void
DAO code :
#Query("SELECT * FROM note WHERE content LIKE :query ORDER BY isStarred")
fun searchAllNotes(query : String?) : Flow<List<Note>>
ViewModel code:
#HiltViewModel
class HomeViewModel #Inject constructor (
private val noteRepository : NoteRepository
) : ViewModel() {
private var _notesLiveData : LiveData<List<Note>> = noteRepository.getAllNotes().asLiveData()
val notesLiveData get()= _notesLiveData
fun searchNotes(query : String){
viewModelScope.launch(Dispatchers.IO){
_notesLiveData = noteRepository.searchAllNotes(query).asLiveData()
}
}
fun deleteNote(note : Note){
viewModelScope.launch(Dispatchers.IO){
noteRepository.deleteNote(note)
}
}
fun updateNoteChecked(note : Note){
viewModelScope.launch(Dispatchers.IO){
noteRepository.updateNoteChecked(note.id, note.isStarred)
}
}
Fragment code
#AndroidEntryPoint
class HomeFragment : Fragment(),
NoteCardAdapter.NoteTouchListener,
SearchView.OnQueryTextListener{
private var _binding : FragmentHomeBinding? = null
val binding get() = _binding!!
private val adapter by lazy {
NoteCardAdapter(this as NoteCardAdapter.NoteTouchListener)
}
private val vm : HomeViewModel by viewModels()
private val noteSharedViewModel : NoteSharedViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.adapter = adapter
binding.vm = vm
binding.apply {
lifecycleOwner = viewLifecycleOwner
homeRecyclerView.isNestedScrollingEnabled = false
homeRecyclerView.layoutManager = LinearLayoutManager(view.context)
homeRecyclerView.itemAnimator= NotesItemAnimator()
}
binding.homeSearchView.isSubmitButtonEnabled = true
binding.homeSearchView.setOnQueryTextListener(this as SearchView.OnQueryTextListener)
binding.addNoteButton.setOnClickListener{
val note = Note(
"","","",false, activity?.getDate(), folderId = -1
)
noteSharedViewModel.selectNote(note)
val action = HomeFragmentDirections.actionHomeFragmentToSingleNoteFragment(
isNew = true
)
findNavController().navigate(action)
}
binding.foldersButton.setOnClickListener{
findNavController().navigate(HomeFragmentDirections.actionHomeFragmentToFoldersFragment())
}
}
xml layout code :
<?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"
>
<data>
<variable
name="vm"
type="com.example.leonidsnotesapplication.presentation.notes_feature.viewmodels.HomeViewModel" />
<variable
name="adapter"
type="com.example.leonidsnotesapplication.presentation.notes_feature.adapters.NoteCardAdapter" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:background="#color/note_background_color_3"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".presentation.notes_feature.fragments.HomeFragment">
<androidx.appcompat.widget.SearchView
android:id="#+id/homeSearchView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:theme="#style/AppSearchView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
android:id="#+id/tvRecentTitle"
android:textStyle="bold"
android:textSize="25sp"
android:textColor="#color/button_color_2"
android:text="#string/recent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginStart="20dp"
app:layout_constraintTop_toBottomOf="#id/homeSearchView"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginTop="28dp"
android:background="#drawable/ic_circle_arrow"
app:layout_constraintStart_toEndOf="#id/tvRecentTitle"
app:layout_constraintTop_toBottomOf="#id/homeSearchView" />
<androidx.recyclerview.widget.RecyclerView
tools:listitem="#layout/note_card_view"
android:scrollbars="vertical"
android:scrollbarStyle="outsideInset"
android:id="#+id/homeRecyclerView"
android:layout_width="match_parent"
android:requiresFadingEdge="vertical"
android:fadingEdge="vertical"
android:fadingEdgeLength="15dp"
android:layout_height="500dp"
android:layout_marginTop="30dp"
app:setNoteAdapter="#{adapter}"
app:submitNoteList="#{vm.notesLiveData}"
app:layout_constraintTop_toBottomOf="#id/tvRecentTitle"
app:layout_constraintStart_toStartOf="parent"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:backgroundTint="#color/button_color_1"
android:id="#+id/add_note_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="#drawable/ic_create"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:backgroundTint="#color/button_color_1"
android:id="#+id/folders_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:src="#drawable/ic_folders_stack"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Data binding adapter :
#BindingAdapter("submitNoteList")
fun submitNoteList(recyclerView: RecyclerView, data : List<Note>?){
val adapter = recyclerView.adapter as NoteCardAdapter
adapter.setData((data as ArrayList<Note>? ?: arrayListOf()))
}
#BindingAdapter("setNoteAdapter")
fun setNoteAdapter(recyclerView: RecyclerView, adapter: NoteCardAdapter){
adapter.let {
recyclerView.adapter = it
}
}
You should observe your live data. Example code is:
vm.notesLiveData.observe(viewLifecycleOwner) { adapter.submitList(it) }
I'm trying to implement viewpager when press of a button on secondary activity for that I created a PageAdaper class which extends Recycler view.Adapter .I got viewpager must not be null error on runtime .
PageAdaper.kt
open class PageAdapter(context: Context, arrayList: ArrayList<Uri>) : RecyclerView.Adapter<PageAdapter.MyViewHolder>() {
private var mContext: Context? =null
private var galleryUri= arrayListOf<Uri>()
private var layoutInflater: LayoutInflater? =null
init {
this.mContext=context
this.galleryUri=arrayList
this.layoutInflater=context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
}
private fun onBindViewHolderr(holder: MyViewHolder, position: Int) {
holder.iView.setImageURI(galleryUri[position])
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view=LayoutInflater.from(mContext).inflate(R.layout.image_container,parent,false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return galleryUri.size
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val iView: ImageView
init {
super.itemView
iView=itemView.findViewById(R.id.imageContainer)
}
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
onBindViewHolderr(holder, position)
}
}
Image_container.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/imageContainer"
android:contentDescription="#string/gallery" />
</LinearLayout>
Activity_gallery.xml
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent">
<ImageButton
android:id="#+id/galleryBack"
android:layout_width="59dp"
android:layout_height="56dp"
android:background="#android:color/transparent"
android:contentDescription="#string/back_button"
android:src="#drawable/ic_baseline_arrow_back_24"
style="#style/Widget.AppCompat.Button.Colored"
app:layout_constraintHorizontal_bias="0.05"
app:layout_constraintVertical_bias="0.05"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewPager"
android:layout_width="409dp"
android:layout_height="729dp"
android:background="#android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</androidx.viewpager2.widget.ViewPager2>
</androidx.constraintlayout.widget.ConstraintLayout>
Creation on viewpager on button click from activity_main
gallery.setOnClickListener { // gallery button reference
if (lastImageUri!=null){
val intent= Intent(this,GalleryActivity::class.java)
startActivity(intent) // inflates activity_gallery.xml
val iAdapter=PageAdapter(this,galleryUri) // creating object with params
viewPager.adapter=iAdapter // here comes the error
}
}
Update
GalleryActivity.kt
It contains only setContentview to activity_gallery.xml
Do I need to add anything.
class GalleryActivity : AppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_gallery)
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right)
galleryBack.setOnClickListener {
finish()
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right)
}
}
}
Explain me about this as I'm new to android. Thank you!
So, startActivity will go to the GalleryActivity class. You need to put the lines after that into the GalleryActivity.
Okay, now that you've added your GalleryActivity.kt. You need to make 2 changes. First, in your activity_main:
gallery.setOnClickListener { // gallery button reference
if (lastImageUri!=null){
val intent= Intent(this,GalleryActivity::class.java)
intent.putExtra("IMAGE_URI", galleryUri)
startActivity(intent) // inflates activity_gallery.xml
}
}
Then, in your GalleryActivity.kt
class GalleryActivity : AppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_gallery)
val galleryUri = getIntent().getStringExtra("GALLERY_URI")
val iAdapter=PageAdapter(this,galleryUri) // creating object with params
viewPager.adapter=iAdapter
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right)
galleryBack.setOnClickListener {
finish()
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right)
}
}
}
Try that.
I have created a recycle view and inside that using card view for items. I have a delete button inside a card view whenever I click on that button my item is deleted from SQLite database. But to reflect it on UI, app need to restart. How can I notify adpater that item is deleted?
activity_main.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"
tools:context=".MainActivity">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="57dp"
android:layout_height="64dp"
android:layout_marginEnd="40dp"
android:layout_marginBottom="40dp"
android:clickable="true"
android:onClick="addNewCredentials"
app:backgroundTint="#270867"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="#android:drawable/ic_menu_add" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" >
</androidx.recyclerview.widget.RecyclerView>
</androidx.constraintlayout.widget.ConstraintLayout>
list_item_layout.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"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/urlView"
android:layout_width="300dp"
android:layout_height="30dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:text="url"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="#+id/userNameView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
<TextView
android:id="#+id/userNameView"
android:layout_width="300dp"
android:layout_height="25dp"
android:layout_marginBottom="16dp"
android:text="userName"
app:layout_constraintBottom_toTopOf="#+id/passwordView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/passwordView"
android:layout_width="300dp"
android:layout_height="25dp"
android:layout_marginBottom="16dp"
android:text="password"
app:layout_constraintBottom_toTopOf="#+id/noteView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/noteView"
android:layout_width="300dp"
android:layout_height="30dp"
android:layout_marginBottom="16dp"
android:text="note"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/delButton"
android:layout_width="78dp"
android:layout_height="40dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="36dp"
android:background="#E6360F"
android:text="#string/delete_credential_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
MainActivity.kt
package com.example.passwordmanager
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL,false)
val db = DataBaseHandler(this)
val detailsData = db.readCredentials()
val adapter = CredentialAdapter(detailsData,this,{credentialsModel: CredentialsModel->deleteClick(credentialsModel)})
recyclerView.adapter = adapter
}
fun deleteClick(credential: CredentialsModel){
val db = DataBaseHandler(this)
if(db.deleteData(credential.id)){
//adapter.notifyItemRemoved(position)
Toast.makeText(applicationContext,"Deleted", Toast.LENGTH_SHORT).show()
}
}
fun addNewCredentials(view : View){
print("hello world")
val intent = Intent(this, AddDetailActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
}
}
CredentialAdapter.kt
package com.example.passwordmanager
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.list_item_layout.view.*
class CredentialAdapter(
private val items: List<CredentialsModel>,
ctx: Context, val clickListener: (CredentialsModel) -> Unit
): RecyclerView.Adapter<CredentialAdapter.ViewHolder>() {
var context = ctx
class ViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
fun bind(credential: CredentialsModel,clickListener: (CredentialsModel) -> Unit){
itemView.urlView.text = credential.url
itemView.userNameView.text = credential.userName
itemView.passwordView.text = credential.password
itemView.noteView.text = credential.note
itemView.delButton.setOnClickListener{clickListener(credential)}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.list_item_layout,parent,false))
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val credential:CredentialsModel = items[position]
holder.bind(credential,clickListener)
}
}
add remove setOnClickListener in your onBindViewHolder.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.remove.setOnClickListener {
val db = DataBaseHandler(this)
if(db.deleteData(credential.id)){
notifyItemRemoved(holder.getAdapterPosition())
}
}
}
The best way to handle these kinds of situations is to use LiveData.
LiveData is basically an observable class which reads data only when there is a change.
What you can do is create a set function in your adapter like:
internal fun setData(data: List<Data>) {
this.data= dataList //this datalist is a list defined in your adapter
notifyDataSetChanged()
}
now in your main activity/fragment, create a LiveData List outside the onCreate function like this:
private lateinit var allData:LiveData<List<Data>>
Now inside your onCreate function, use can observe the livedata and set the data for recyclerview like this:
allData.observe(this, Observer { data->
data?.let { adapter.setData(it) }
})
You are deleting the item from database but not from the list inside recyclerview adapter.
class CredentialAdapter(
private val items: ArrayList<CredentialsModel>, // Change list to arraylist
ctx: Context, val clickListener: (CredentialsModel, Int) -> Unit
): RecyclerView.Adapter<CredentialAdapter.ViewHolder>() {
...
...
fun remove(position: Int) {
// Remove and notify the adapter to reload
items.removeAt(position)
notifyItemRemoved(position)
}
class ViewHolder(itemView: View):RecyclerView.ViewHolder(itemView) {
fun bind(credential: CredentialsModel,clickListener: (CredentialsModel, Int) -> Unit){
...
...
// Pass adapter item position so that we can update the list after delete
itemView.delButton.setOnClickListener{clickListener(credential, adapterPosition)
}
}
...
...
}
Inside MainActivity.kt
fun deleteClick(credential: CredentialsModel, position: Int) {
val db = DataBaseHandler(this)
if(db.deleteData(credential.id)){
adapter.remove(position)
Toast.makeText(applicationContext,"Deleted", Toast.LENGTH_SHORT).show()
}
}
use ListAdpater
class AdapterMain(var onClickListener: (Int) -> Unit) :
ListAdapter<Note, AdapterMain.NoteViewHolder>(DIFFCALBACK) {
companion object DIFFCALBACK : DiffUtil.ItemCallback<Note>() {
override fun areItemsTheSame(oldItem: Note, newItem: Note): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Note, newItem: Note): Boolean {
return oldItem.title == newItem.title &&
oldItem.description == newItem.description &&
oldItem.priority == newItem.priority
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.note_item, parent, false)
return NoteViewHolder(view)
}
override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
holder.txtTitle.text = getItem(position).title
holder.txtDesc.text = getItem(position).description
holder.txtPriority.text = getItem(position).priority.toString()
}
inner class NoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var txtTitle: TextView = itemView.txt_title
var txtDesc: TextView = itemView.txt_desc
var txtPriority: TextView = itemView.txt_priority
init {
itemView.setOnClickListener { onClickListener(adapterPosition) }
}
}
fun getNoteAt(position: Int): Note {
return getItem(position)
}
}
you can see complete code of simple NoteApp with kotlin , recyclerView , MVVM and..
class coba : AppCompatActivity() {
private lateinit var recycleView :RecyclerView
private lateinit var datalis :ArrayList
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_coba)
recycleView = findViewById(R.id.rcycoba)
datalis = ArrayList()
val dtnama = arrayOf(
"Danial Sanganus",
"Bijonia Skolin",
"Alianes Pertoli",
"Sivanian Pertici",
"Olehsan alausi"
)
for (i in dtnama.indices){
datalis.add(
dataCoba(
dtnama[i]
)
)
populateData()
}
}
private fun populateData(){
val linearManager = LinearLayoutManager(this)
linearManager.reverseLayout=true
linearManager.stackFromEnd=true
recycleView.layoutManager=linearManager
val adp =adpCoba(this,datalis)
recycleView.adapter=adp
}
}
I have an exisitng code which I integrate with Live data by using retrofit.
Now if I want to integrate databinding, where are all changes to be done to make the code looks perfect?
Here is my code.
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:background="#000000"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:id="#+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Android Versions"
android:textColor="#ffffff"
android:textSize="20sp" />
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
items.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:background="#445566"
android:layout_margin="5dp"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tvFname"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top|start"
android:layout_marginTop="8dp"
android:textColor="#ffffff"
android:layout_weight="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="#+id/tvLname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center|start"
android:padding="5dp"
android:layout_marginTop="10dp"
android:layout_weight="1"
android:ellipsize="end"
android:textColor="#ffffff"
android:maxLines="5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tvFname"/>
</android.support.constraint.ConstraintLayout>
Here is view model class which I integrated
class AndroidViewModel:ViewModel() {
private val mService = RetrofitService()
fun getAndroidData():MutableLiveData<List<AndroidData>>?{
return mService.loadAndroidData()
}
}
Pojo class generated is just a simple one:
data class AndroidData (val name:String, val apiLevel:String)
Service integration:
class RetrofitService {
val liveUserResponse:MutableLiveData<List<AndroidData>> = MutableLiveData()
companion object Factory {
var gson = GsonBuilder().setLenient().create()
fun create(): ApiInterface {
Log.e("retrofit","create")
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl("https://learn2crack-json.herokuapp.com/api/")
.build()
return retrofit.create(ApiInterface::class.java)
}
}
fun loadAndroidData(): MutableLiveData<List<AndroidData>>? {
Log.e("loadAndroidData","yes")
val retrofitCall = create().getAndroid()
retrofitCall.enqueue(object : Callback<List<AndroidData>> {
override fun onFailure(call: Call<List<AndroidData>>, t: Throwable?) {
Log.e("on Failure :", "retrofit error")
}
override fun onResponse(call: Call<List<AndroidData>>, response: retrofit2.Response<List<AndroidData>>) {
val list = response.body()
for (i in list.orEmpty()){
Log.e("on response 1:", i.name)
}
liveUserResponse.value = list
Log.e("hasActiveObservers 1", liveUserResponse.hasActiveObservers().toString()+" check")
Log.e("on response 2 :", liveUserResponse.toString()+" check")
}
})
return liveUserResponse
}
Main Activity:
class MainActivity : AppCompatActivity(){
private lateinit var linearLayoutManager: LinearLayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
linearLayoutManager = LinearLayoutManager(this)
recyclerView.setHasFixedSize(true)
getAndroidVersion()
}
private fun getAndroidVersion() {
Log.e("getAndroidVersion", "yes")
val mAndroidViewModel = ViewModelProviders.of(this#MainActivity).get(AndroidViewModel::class.java)
mAndroidViewModel.getAndroidData()?.observe(this, Observer<List<AndroidData>> { androidList ->
Log.e("list", androidList?.size.toString())
recyclerView.adapter = EmpAdapter(this#MainActivity, androidList as ArrayList<AndroidData>, object :
ItemClickListener {
override fun onItemClick(pos: Int, name:String) {
Toast.makeText(applicationContext, "item "+pos+ "clicked"+ name, Toast.LENGTH_LONG).show()
}
})
})
}
}
Adapter class:
class EmpAdapter(
var context: MainActivity,
var mEmpList: ArrayList<AndroidData>,
private val itemClick:ItemClickListener
) :
RecyclerView.Adapter<EmpAdapter.EmpHolder>() {
override fun getItemCount(): Int {
return mEmpList.size
}
companion object {
var mItemClickListener : ItemClickListener? = null
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmpHolder {
val view = LayoutInflater.from(context).inflate(R.layout.items, parent, false)
return EmpHolder(view)
}
override fun onBindViewHolder(holder: EmpHolder, position: Int) {
mItemClickListener = itemClick
holder.tvFname?.text = mEmpList[position].name
holder.tvLname?.text = mEmpList[position].apiLevel
RxView.clicks(holder.mView).subscribe {
mItemClickListener!!.onItemClick(position,mEmpList[position].name)
}
}
class EmpHolder(view: View) : RecyclerView.ViewHolder(view) {
val tvFname = view.tvFname
val tvLname = view.tvLname
val mView = view
}
}
interfaces for retrofit:
interface ApiInterface {
#GET("android")
fun getAndroid(): Call<List<AndroidData>>
}
Now my question is, if I integrate tag and in xml, which variable i need to specify for textview to integrate to connect for textview in adapter? Kindly guide me where are all I need to change to apply, if I use live data
First of all, i strongly recommend to use a Retrofit Adapter that converts the response into LiveData automatically for you. See this example
https://github.com/yasiralijaved/android-architecture-components/blob/master/component_http/src/main/java/com/yasiralijaved/android/arc/component/http/BackendService.java
Secondly there are detailed tutorials out there which can surely help you how to implement Data Bindings in RecyclerView Adapter. A few tutorials are:
https://medium.com/androiddevelopers/android-data-binding-recyclerview-db7c40d9f0e4
https://android.jlelse.eu/how-to-bind-a-list-of-items-to-a-recyclerview-with-android-data-binding-1bd08b4796b4
Do let me know if it is still not clear and i will share some more details.
Happy coding!!