Multichecklist Recyclerview in kotlin - kotlin

I'm lost, I need some orientation.
I need insert one checklist with many checkoption (boolean) (1 to n)
(one checklist have many checkoption, many checkoption have one checklist)
What do i need to insert checklist with list checkoptions?
data class Checklist (model)
#Parcelize
#Entity(tableName = "checklist")
data class Checklist(
#PrimaryKey(autoGenerate = true)
var id: Int?,
var data: LocalDate? = LocalDate.now(),
var turno: String = "",
var equipamento: String = "",
var operacional: String = "",
var utilizador: String = ""
) : Parcelable
Object Checkoption (model)
#Entity(tableName = "checkoption")
data class Checkoption (
#PrimaryKey
var codOption: String = "",
val descricao: String,
val checked: Boolean = true
)
relatation room 1 checklist with list checkoptions
data class ChecklistWithOptions (
#Embedded val checklist: Checklist,
#Relation(
parentColumn = "id",
entityColumn = "codOption"
)
val checkoptionsx: List<Checkoption>
)
daoChecklist
/../
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addChecklist(checklist : Checklist)
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addCheckoption(checkoptions : List<Checkoption>)
/.../
fragment add
/.../
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
checklistViewModel = ViewModelProvider(this).get(ChecklistViewModel::class.java)
/.../
val adapter = ListAdapter()
val recyclerView = binding.recyclerview2
recyclerView.adapter = adapter
checklistViewModel = ViewModelProvider(this).get(ChecklistViewModel::class.java)
checkOptions.add(Checkoption("1", "Seleção/Utilidade", false))
/.../
adapter.setData(checkOptions)
binding.addBtn.setOnClickListener {
insertDatatoDatabase()
}
return binding.root
}
private fun insertDatatoDatabase() {
val data = org.threeten.bp.LocalDate.now()
val utilizador = "jconduto"
if (inputCheck(utilizador)) {
checklistViewModel.checklist.data = data
checklistViewModel.checklist.utilizador = utilizador
checklistViewModel.addChecklist(checklistViewModel.checklist)
Toast.makeText(requireContext(), "Sucesso", Toast.LENGTH_LONG).show()
findNavController().navigate(R.id.action_addFragment_to_listFragment)
} else {
Toast.makeText(
requireContext(),
"Por favor, preencha todos os campos.",
Toast.LENGTH_LONG
).show()
}
}
adapter
class ListAdapter() :
RecyclerView.Adapter<ListAdapter.ViewHolder>() {
private var checkoptionList = emptyList<Checkoption>()
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var textView1: TextView
var rb1 : RadioButton
var rb2 : RadioButton
init {
textView1 = view.findViewById(R.id.descricao_text)
rb1 = view.findViewById(R.id.yes)
rb2 = view.findViewById(R.id.no)
}
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.custom_row2, viewGroup, false)
return ViewHolder(view)
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
val currentItem = checkoptionList[position]
viewHolder.textView1.text = currentItem.descricao.toString()
viewHolder.rb1.isChecked = currentItem.checked
viewHolder.rb2.isChecked = currentItem.checked
//viewHolder.textView2.isChecked = currentItem.checked
}
override fun getItemCount(): Int {
return checkoptionList.size
}
fun setData(checkoption: ArrayList<Checkoption>) {
this.checkoptionList = checkoption
notifyDataSetChanged()
}
}
my fragment_add have recyclier with linearlayout
/../
<RadioGroup
android:id="#+id/radio_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:orientation="horizontal">
<TextView
android:id="#+id/descricao_text"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:text="1"
android:textSize="20sp"
android:textStyle="bold" />
<RadioButton
android:id="#+id/yes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onRadioButtonClicked" />
<RadioButton
android:id="#+id/no"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onRadioButtonClicked" />
</RadioGroup>
/.../

Related

No Adapter Attached RecyclerView

I want to implement my app. My app went smoothly, but I also got an error with 2022-05-31 14:14:32.663 8626-8626/id.kotlin.belajar E/RecyclerView: No adapter attached; skipping layout; skipping layout.
E/RecyclerView: No adapter attached; skipping layout; skipping layout
Home Activity:
class HomeActivity : DaggerAppCompatActivity(), HomeView {
#Inject
lateinit var presenter: HomePresenter
private lateinit var progressBar: ProgressBar
private lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
progressBar = findViewById(R.id.pb_home)
recyclerView = findViewById(R.id.rv_home)
presenter.discoverMovie()
}
override fun onShowLoading() {
progressBar.visibility = View.VISIBLE
}
override fun onHideLoading() {
progressBar.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
}
override fun onResponse(results: List<Result<String>>) {
recyclerView.addItemDecoration(DividerItemDecoration(this#HomeActivity,
DividerItemDecoration.VERTICAL))
recyclerView.adapter = HomeAdapter(results)
}
override fun onFailure(t:Throwable) {
Log.e(HomeActivity::class.java.simpleName, "${t.printStackTrace()}")
}
}
Home Adapter:
class HomeAdapter (private val results: List<Result>):
RecyclerView.Adapter<HomeAdapter.HomeViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeViewHolder {
return HomeViewHolder(
LayoutInflater
.from(parent.context).inflate(
R.layout.item_home,
parent,
false
)
)
}
override fun onBindViewHolder(holder: HomeViewHolder, position: Int){
holder.bind()
}
override fun getItemCount(): Int{
return results.count()
}
inner class HomeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind() {
with(itemView) {
val title = findViewById<TextView>(R.id.original_title)
title.text
val overview = findViewById<TextView>(R.id.overview)
overview.text
}
}
}
}
Home Presenter:
class HomePresenter(private val view: HomeView, datasource: HomeDatasource) {
fun discoverMovie(){
view.onShowLoading()
val dataSource = Networkmodule.providesHttpAdapter(client =
OkHttpClient()).create(HomeDatasource::class.java)
dataSource.discoverMovie().enqueue(object : Callback<HomeResponse> {
override fun onResponse(call: Call<HomeResponse>, response: Response<HomeResponse>){
view.onHideLoading()
view.onResponse(((response.body()?.results ?: emptyList()) as List<Result<String>>))
}
override fun onFailure(call: Call<HomeResponse>, t:Throwable){
view.onHideLoading()
view.onFailure(t)
}
})
}
}
I need your help.
This is my .xml files:
activity_home:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".presentation.HomeActivity">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:elevation="4dp"
app:layout_constraintTop_toTopOf="parent"
app:title="Belajar"
app:titleTextColor="#color/black"/>
<ProgressBar
android:id="#+id/pb_home"
style="#style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:indeterminateTint="#color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_home"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?android:attr/actionBarSize"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="#layout/item_home" />
</androidx.constraintlayout.widget.ConstraintLayout>
I can see two major problems here :
Adapter instantiation
You do not need to re-instantiate the adapter each time you receive data.
Just instanciate it in the onCreate method with an empty list and update the list each time you have new data (don't forget to call notify method).
And attach the adapter to the recyclerView in the onCreate method as well
Exemple
In your activity you need to :
Declare and instantiate your Adapter as a class attribute.
In the onCreate method attach it to the recyclerView
On the onResponse method call a new method of your adapter called " update "
class HomeActivity : DaggerAppCompatActivity(), HomeView {
#Inject
lateinit var presenter: HomePresenter
private lateinit var progressBar: ProgressBar
private lateinit var recyclerView: RecyclerView
private val homeAdapter = HomeAdapter(emptyList())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
progressBar = findViewById(R.id.pb_home)
recyclerView = findViewById(R.id.rv_home)
recyclerView.adapter = homeAdapter
presenter.discoverMovie()
}
override fun onShowLoading() {
progressBar.visibility = View.VISIBLE
}
override fun onHideLoading() {
progressBar.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
}
override fun onResponse(results: List<Result<String>>) {
recyclerView.addItemDecoration(DividerItemDecoration(this#HomeActivity,
DividerItemDecoration.VERTICAL))
homeAdapter.update(results)
}
override fun onFailure(t:Throwable) {
Log.e(HomeActivity::class.java.simpleName, "${t.printStackTrace()}")
}
}
In your adapter :
Make results a mutableList
Create the update method
Clear the results list
Add your newResults list to your list
notifyDataSetChanged to redraw the list with new values
class HomeAdapter (private val results: MutableList<Result>):
RecyclerView.Adapter<HomeAdapter.HomeViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeViewHolder {
return HomeViewHolder(
LayoutInflater
.from(parent.context).inflate(
R.layout.item_home,
parent,
false
)
)
}
override fun onBindViewHolder(holder: HomeViewHolder, position: Int){
holder.bind()
}
override fun getItemCount(): Int{
return results.count()
}
fun update(newResults: List<Result>) {
results.apply {
clear()
addAll(newResults)
}
notifyDataSetChanged()
}
inner class HomeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind() {
with(itemView) {
val title = findViewById<TextView>(R.id.original_title)
title.text
val overview = findViewById<TextView>(R.id.overview)
overview.text
}
}
}
}
Layout manager missing ?
We don't have your xml code so I don't know if you added the layoutManager on the xml like this (assuming you want a linearManager) :
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
You can do it programaticaly using the layoutManager attribute on the recyclerView :
recyclerView.layoutManager = LinearLayoutManager()
You can set it in the onCreate method (if it is an activity) or onViewCreated (if it's a fragment) before attaching your adapter to the recyclerView
To avoid errors like that, the best way is to setup your recyclerView as soon as possible in the activity (onCreate) or fragment (onViewCreated) lifecycle.

How to implement nested paging in android kotlin?

I have a recyclerview , for i using pagin 3 with coroutine live data .
but child recyclerview also have paginate data so how can i perform this nested pagination in android kotlin.
So , I done this with pagination for child recyclerview inside recyclerview .
Firstly of all call api for fetch dates and perform api for every date at same time when date are fetching.
so structure like this...
lateinit var adapter: WeeklyAdapter
lateinit var dayAdapter: DayDetailsPagingAdapter
private fun observeData() {
viewModel.weeklyAcquiringDetails(AcquiringVM.WEEKLY, "").observe(this){
wsWithLoader(it){
binding.rvAcquiringWeeklyDetails.adapter = WeeklyAdapter(it.data!!.data, object : WeeklyAdapter.OnPerformApi{
override fun performApi(date: String, rvMonthly: RecyclerView) {
dayAdapter = DayDetailsPagingAdapter(object : DayDetailsPagingAdapter.OnClick{
override fun onClick(result: DayAcquiring.Data) {}
})
rvMonthly.adapter = dayAdapter
lifecycleScope.launchWhenCreated {
viewModel.getDayAcquiringDetails(date).collectLatest { data ->
dayAdapter.submitData(data)
}
}
}
})
}
}
}
Weekly Adapter
class WeeklyAdapter (
val list: List<WeeklyAcquiring.Data>,
val action: OnPerformApi
) : RecyclerView.Adapter<WeeklyAdapter.ViewHolder>() {
#NonNull
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.list_monthly, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val result = list[position]
holder.binding.apply {
tvDate.text = changeDateFormat(result.doctorScheduleDate,"yyyy-MM-dd","E, dd MMM yyyy")
action.performApi( result.doctorScheduleDate , rvMonthly)
}
}
override fun getItemCount(): Int {
return list.size
}
inner class ViewHolder(binding: ListMonthlyBinding) : BaseViewHolder<ListMonthlyBinding>(binding) {}
interface OnPerformApi{
fun performApi(date : String, rvMonthly: RecyclerView)
}
}
list_monthly_layout
<?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>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tvDate"
style="#style/TVBold"
android:layout_marginTop="#dimen/_5sdp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="#dimen/_12sdp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="May 2022" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvMonthly"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/_10sdp"
app:layout_constraintEnd_toEndOf="parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintStart_toStartOf="parent"
tools:listitem="#layout/list_acquiring_details"
app:layout_constraintTop_toBottomOf="#+id/tvDate" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
DayDetailsPagingAdapter
class DayDetailsPagingAdapter (private val onClick: OnClick) : PagingDataAdapter<DayAcquiring.Data, DayDetailsPagingAdapter.ViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(ListAcquiringDetailsBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
#SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val result = getItem(position)!!
holder.binding.apply {
data = result
tvName.text = result.userName
tvAmount.text = "€ ${result.amount}"
}
}
class ViewHolder(val binding: ListAcquiringDetailsBinding) : RecyclerView.ViewHolder(binding.root) {}
private class DiffCallback : DiffUtil.ItemCallback<DayAcquiring.Data>() {
override fun areItemsTheSame(
oldItem: DayAcquiring.Data,
newItem: DayAcquiring.Data
): Boolean = oldItem == newItem
override fun areContentsTheSame(
oldItem: DayAcquiring.Data,
newItem: DayAcquiring.Data
): Boolean = oldItem == newItem
}
interface OnClick {
fun onClick(result: DayAcquiring.Data )
}
}
**in viewmodel **
fun getDayAcquiringDetails( date: String ) = apiHelper.getDayAcquiringDetails( date ).cachedIn(viewModelScope)
fun weeklyAcquiringDetails( filter: String , date: String) = apiHelper.weeklyAcquiringDetails( filter , date)
hope you like this .

Why did my button activity cannot run me to the other page?

This is my button.
I not sure if that my button have the problem or the kotlin files have the problem. The Home fragment is using binding to bind with the home fragment. Maybe is the intent in the fragment have the problem?
<Button
android:id="#+id/registerbutton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="14dp"
android:text="#string/register"
android:textAllCaps="false"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/editText3"
app:layout_constraintWidth_percent=".8"
android:background="#drawable/btn_bg_design"
app:layout_constraintVertical_bias="0.6"
/>
This is my registration activity
class RegistrationActivity : AppCompatActivity() {
private lateinit var binding:ActivityRegistrationBinding
private lateinit var db: FirebaseFirestore
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityRegistrationBinding.inflate(layoutInflater)
setContentView(R.layout.activity_registration)
db = FirebaseFirestore.getInstance() // singleton
binding.registerbutton.setOnClickListener{
val fullName = binding.editText1.text.toString()
val email = binding.editText2.text.toString()
val password = binding.editText3.text.toString()
val register = Register(fullName,email, password)
// pass the User data obj to the firestore
db.collection("Register").document("$email").set(register)
intent = Intent(this, MainActivity:: class.java)
Intent(this, HomeFragment:: class.java).putExtra("email",email)
startActivity(intent)
}
}
fun login(view: View) {
intent = Intent(this, LoginActivity:: class.java)
startActivity(intent)
}
}
This is my homefragment
class HomeFragment : Fragment() {
//private lateinit var homeViewModel: HomeViewModel (delete)
private var _binding: FragmentHomeBinding? = null
private lateinit var db: FirebaseFirestore
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
val root: View = binding.root
db = FirebaseFirestore.getInstance()
val intent = Intent(activity,RegistrationActivity::class.java)
val email = intent.getStringExtra("email")
db.collection("Register").document("$email").get()
.addOnSuccessListener { doc->
binding.textViewName.text = doc.get("fullName").toString()
}
.addOnFailureListener {
Log.e("Firestore", "Error in loading file: ${it.toString()}")
}
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Try to change the code in RegistrationActivity like this:
binding.registerbutton.setOnClickListener{
val fullName = binding.editText1.text.toString()
val email = binding.editText2.text.toString()
val password = binding.editText3.text.toString()
val register = Register(fullName,email, password)
// pass the User data obj to the firestore
db.collection("Register").document("$email").set(register)
val intent = Intent(this, MainActivity:: class.java)
intent.putExtra("email", email)
startActivity(intent)
}
Also, you should pass the data to the fragment using a Bundle:
val bundle = Bundle()
bundle.putString("email", email)
homeFragment.arguments = bundle
And retrieve it in the fragment using the arguments object:
val email = arguments?.getString("email") ?: ""

Saving checkbox states in RecyclerView inside Fragment using SharedPreferences or any other method

I'm trying to save checkbox states in RecyclerView inside Fragment to restore these preferences after exit from the app and loading it again.
I have a ConfigActivity for AppWidget in which there are fragments.
Inside of one of the fragments I have a RecyclerView which loads calendars available for the user from Calendar Provider. Based on selected calendars the appwidget will be loading the events from them. Selected calendars should be passed into the appwidget.
I've made saving states of the checkboxes while scrolling of the RecyclerView.
But I don't know how to save selected checkboxes in RecyclerView inside Fragment using SharedPreferences (saving for relaunching of the app).
My data class for calendar items:
data class CalendarItem(
val idCalendar: Long,
val displayNameCalendar: String?,
val accountNameCalendar: String?,
val colorCalendar: Int?
)
Item with checkbox in xml:
<RelativeLayout 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_marginTop="8dp"
android:layout_marginBottom="8dp">
<ImageView
android:id="#+id/calendar_color"
android:layout_width="10dp"
android:layout_height="10dp"
android:src="#drawable/color_label_circle"
app:tint="#color/accent_color"
android:layout_alignParentStart="true"
android:layout_alignTop="#+id/text_display_name_calendar"
android:layout_alignBottom="#+id/text_display_name_calendar"/>
<com.google.android.material.checkbox.MaterialCheckBox
android:id="#+id/text_display_name_calendar"
style="#style/basicText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="24dp"
android:layout_marginEnd="4dp"
android:maxLines="1"
android:ellipsize="end"
android:gravity="start|center_vertical"
android:layoutDirection="rtl"
android:text="Display Name" />
<TextView
android:id="#+id/text_account_name"
style="#style/commentText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Account Name"
android:layout_marginEnd="4dp"
android:maxLines="1"
android:ellipsize="end"
android:layout_alignStart="#+id/text_display_name_calendar"
android:layout_below="#+id/text_display_name_calendar" />
</RelativeLayout>
My Fragment getting calendars:
class CalendarsEventsFragment : Fragment() {
// For permissions
private val PERMISSION_REQUEST_CODE = 101
// For RecyclerView - Calendars
private lateinit var calendarItemAdapter: CalendarItemAdapter
private lateinit var recyclerViewCalendars: RecyclerView
// Values for the calendars from the calendar content provider
private val EVENT_PROJECTION = arrayOf(
CalendarContract.Calendars._ID,
CalendarContract.Calendars.CALENDAR_DISPLAY_NAME,
CalendarContract.Calendars.ACCOUNT_NAME,
CalendarContract.Calendars.CALENDAR_COLOR
)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_calendars_events, container, false)
recyclerViewCalendars = view.findViewById(R.id.recyclerview_calendars)
// Setup permissions + start getCalendars
setupPermissionsGetCalendars()
return view
}
// Function to get and show calendars
private fun getCalendars() {
// Getting calendars from CalendarProvider
// In practice, should be done in an asynchronous thread instead of on the main thread
calendarItemAdapter = CalendarItemAdapter()
calendarItemAdapter.clearData()
val uri = CalendarContract.Calendars.CONTENT_URI
val cur: Cursor? = context?.contentResolver?.query(
uri,
EVENT_PROJECTION,
null,
null,
null
)
while (cur?.moveToNext() == true) {
val calId = cur.getLong(PROJECTION_ID_INDEX)
val displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX)
val accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX)
val color = cur.getInt(PROJECTION_CALENDAR_COLOR_INDEX)
calendarItemAdapter.pushData(
CalendarItem(
idCalendar = calId,
displayNameCalendar = displayName,
accountNameCalendar = accountName,
colorCalendar = color
)
)
}
cur?.close()
// Setup RecyclerView adapter
recyclerViewCalendars.let {
it.layoutManager = LinearLayoutManager(context)
it.adapter = calendarItemAdapter
}
}
// Function to check permission and make request for permission + start getCalendars
private fun setupPermissionsGetCalendars() {
if (checkSelfPermission(requireContext(), Manifest.permission.READ_CALENDAR) !=
PackageManager.PERMISSION_GRANTED
) {
requestPermissions(
arrayOf(Manifest.permission.READ_CALENDAR),
PERMISSION_REQUEST_CODE
)
} else {
getCalendars()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
PERMISSION_REQUEST_CODE -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(
requireActivity(),
getText((R.string.toast_permission_granted)),
Toast.LENGTH_SHORT
).show()
getCalendars()
} else {
if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CALENDAR)) {
Toast.makeText(
requireActivity(),
getText((R.string.toast_permission_denied)),
Toast.LENGTH_SHORT
).show()
showUserRationale()
} else {
askUserOpenAppInfo()
}
}
}
}
}
private fun showUserRationale() {
AlertDialog.Builder(requireContext())
.setTitle(getString(R.string.request_permission_rationale_title))
.setMessage(getString(R.string.request_permission_rationale_message))
.setPositiveButton("OK") { dialog, id ->
requestPermissions(
arrayOf(Manifest.permission.READ_CALENDAR),
PERMISSION_REQUEST_CODE
)
}
.create()
.show()
}
private fun askUserOpenAppInfo() {
val appSettingsIntent = Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", activity?.packageName, null)
)
if (activity?.packageManager?.resolveActivity(
appSettingsIntent,
PackageManager.MATCH_DEFAULT_ONLY
) == null
) {
Toast.makeText(
requireContext(),
getText(R.string.toast_permission_denied_forever),
Toast.LENGTH_SHORT
).show()
} else {
AlertDialog.Builder(requireContext())
.setTitle(getString(R.string.request_permission_denied_forever_title))
.setMessage(getString(R.string.request_permission_denied_forever_message))
.setPositiveButton(getString(R.string.open_app_info_dialog_positive_button_text)) { dialog, id ->
startActivity(appSettingsIntent)
requireActivity().finish()
}
.setNegativeButton(getString(R.string.open_app_info_dialog_negative_button_text)) { dialog, id ->
requireActivity().finish()
}
.create()
.show()
}
}
}
My RecyclerView Adapter:
class CalendarItemAdapter() : RecyclerView.Adapter<CalendarItemAdapter.ViewHolder>() {
var data: MutableList<CalendarItem> = mutableListOf()
var checkedCalendarItems = SparseBooleanArray()
fun clearData() {
data.clear()
notifyDataSetChanged()
}
fun pushData(calendarItem: CalendarItem) {
data.add(calendarItem)
notifyDataSetChanged()
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val imageViewColor: ImageView = view.findViewById(R.id.calendar_color)
val displayNameOfCalendar: CheckBox = view.findViewById(R.id.text_display_name_calendar)
val accountName: TextView = view.findViewById(R.id.text_account_name)
init {
displayNameOfCalendar.setOnClickListener {
if(!checkedCalendarItems.get(adapterPosition, false)) {
displayNameOfCalendar.isChecked = true
checkedCalendarItems.put(adapterPosition, true)
} else {
displayNameOfCalendar.isChecked = false
checkedCalendarItems.put(adapterPosition, false)
}
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_calendar, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val datum = data[position]
datum.colorCalendar?.let {
holder.imageViewColor.setColorFilter(it)
}
holder.displayNameOfCalendar.text = datum.displayNameCalendar
holder.displayNameOfCalendar.isChecked = checkedCalendarItems.get(position, false)
holder.accountName.text = datum.accountNameCalendar
}
override fun getItemCount(): Int {
return data.size
}
}
Could you help me, please?
SharedPreferences can only store primitives and String arrays so you'll have to serialise your array somehow. Probably the easiest way is to just get all the indices of the checked items and throw them in a string. And when you pull that back out, split them up and set those to true.
You should probably handle this in the adapter, since really it's an internal implementation detail that only the adapter needs to know about. Something like this maybe:
class CalendarItemAdapter() : RecyclerView.Adapter<CalendarItemAdapter.ViewHolder>() {
var checkedCalendarItems = SparseBooleanArray()
fun saveState(prefs: SharedPreferences) {
// make a list of all the indices that are set to true, join them as a string
val checkedIndices = checkedCalendarItems
.mapIndexedNotNull {index, checked -> if (checked) index else null }
.joinToString(SEPARATOR)
prefs.edit { putString(KEY_CHECKED_INDICES, checkedIndices) }
}
fun restoreState(prefs: SharedPreferences) {
// reset the array - we're clearing the current state
// whether there's anything stored or not
checkedCalendarItems = SparseBooleanArray()
// grab the checked indices and set them - using null as a "do nothing" fallback
val checkedIndices = prefs.getString(KEY_CHECKED_INDICES, null)
?.split(SEPARATOR)
?.map(String::toInt) // or mapNotNull(String::toIntOrNull) to be super safe
?.forEach { checkedCalendarItems[it] = true }
// update the display - onBindViewHolder should be setting/clearing checkboxes
// by referring to the checked array
notifyDataSetChanged()
}
...
companion object {
// making these constants that both functions refer to avoids future bugs
// e.g. someone changing the separator in one function but not the other
const val SEPARATOR = ","
const val KEY_CHECKED_INDICES = "checked indices"
}
}
Then you can call these save/restore state functions on the adapter as appropriate, e.g. in onStop and onStart, passing in your SharedPreferences state object

Spiner into RecyclerView item - kotlin

i have problem with adding simple Spiner to RecyclerView item. I saw a lot of examples but i cannot add it to my project. All i want is the same Spiner with list of Strings in every item of RecyclerView. I have no problem with implement this without RecycylerView, but with it, i dont know where to pass Adapter for Spiner.
RecyclerViewAdapter:
class MealPlanerAdapter(val mealList: MutableList<String>) : RecyclerView.Adapter<CustomViewHolder>() {
val mealCalories = mutableListOf<Int>()
override fun getItemCount(): Int {
return mealList.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val cellForRow = layoutInflater.inflate(R.layout.meal_row, parent, false)
mealList.forEach {
mealCalories.add(100 / mealList.size)
}
return CustomViewHolder(cellForRow)
}
#SuppressLint("SetTextI18n", "CutPasteId", "ClickableViewAccessibility")
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
holder.view.findViewById<TextView>(R.id.meal_name_text_view).text = "Meal name"
holder.view.findViewById<TextView>(R.id.calosies_slider_name_text_view).text = "Daily calories: "
holder.view.findViewById<TextView>(R.id.calories_value_text_view).text = holder.view.findViewById<SeekBar>(
R.id.calories_seek_bar
).progress.toString()+"%"
holder.view.findViewById<TextView>(R.id.protein_value_text_view).text = holder.view.findViewById<SeekBar>(
R.id.protein_seek_bar
).progress.toString()+"%"
holder.view.findViewById<TextView>(R.id.fat_value_text_view).text = holder.view.findViewById<SeekBar>(
R.id.fat_seek_bar
).progress.toString()+"%"
holder.view.findViewById<TextView>(R.id.carbs_value_text_view).text = holder.view.findViewById<SeekBar>(
R.id.calories_seek_bar
).progress.toString()+"%"
holder.view.findViewById<SeekBar>(R.id.calories_seek_bar).progress = mealCalories[position]
var proteinValue: Int = 20
var fatValue: Int = 25
holder.view.findViewById<SeekBar>(R.id.calories_seek_bar).setOnSeekBarChangeListener(object :
SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
seekBar: SeekBar?,
progress: Int,
fromUser: Boolean
) {
holder.view.findViewById<TextView>(R.id.calories_value_text_view).text =
"$progress%"
println(position)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
})
holder.view.findViewById<SeekBar>(R.id.protein_seek_bar).setOnSeekBarChangeListener(object :
SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
holder.view.findViewById<TextView>(R.id.protein_value_text_view).text = "$progress%"
proteinValue = progress
holder.view.findViewById<SeekBar>(R.id.carbs_seek_bar).progress =
100 - proteinValue - fatValue
holder.view.findViewById<SeekBar>(R.id.carbs_seek_bar).refreshDrawableState()
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
})
holder.view.findViewById<SeekBar>(R.id.fat_seek_bar).setOnSeekBarChangeListener(object :
SeekBar.OnSeekBarChangeListener {
#SuppressLint("ResourceType")
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
holder.view.findViewById<TextView>(R.id.fat_value_text_view).text = "$progress%"
fatValue = progress
holder.view.findViewById<SeekBar>(R.id.carbs_seek_bar).progress =
100 - proteinValue - fatValue
holder.view.findViewById<View>(R.id.carbs_seek_bar).refreshDrawableState()
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
})
holder.view.findViewById<SeekBar>(R.id.carbs_seek_bar).setOnTouchListener { v, event -> true }
}
}
class CustomViewHolder(val view: View): RecyclerView.ViewHolder(view) {
}
Fragment:
class MealPlanerFragment : Fragment(R.layout.fragment_meal_planer) {
private var param1: String? = null
private var param2: String? = null
private val mealList = mutableListOf<String>("1", "2", "3")
private lateinit var mAdapter: MealPlanerAdapter
private lateinit var mLinearLayoutManager: LinearLayoutManager
private lateinit var mRecyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
mAdapter = MealPlanerAdapter(mealList)
}
#SuppressLint("CutPasteId", "ResourceType")
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val inflater = inflater.inflate(R.layout.fragment_meal_planer, container, false)
mLinearLayoutManager = LinearLayoutManager(activity)
mRecyclerView = inflater.findViewById(recycler_view_meal_planer)
mRecyclerView.layoutManager = mLinearLayoutManager
mRecyclerView.adapter = mAdapter
return inflater
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}