error: No value passed for parameter 'priority - java

error: No value passed for parameter 'priority
i went through my code and cant see why im getting the error , i passed the value for it but not sure why its not recognizing it.
error in EditNotesFragment.kt
No value passed for parameter 'priority'
error in CreateNotesFragment.kt
No value passed for parameter 'priority'
error in NotesAdapter.kt
Unresolved reference: viewPriority
EditNotesFragment.kt
package com.cameron.armymaintenance.ui.fragments
import android.os.Bundle
import android.text.format.DateFormat
import android.view.*
import android.widget.TextView
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.Navigation
import androidx.navigation.fragment.navArgs
import com.cameron.armymaintenance.R
import com.cameron.armymaintenance.ViewModel.NotesViewModel
import com.cameron.armymaintenance.databinding.FragmentEditNotesBinding
import com.cameron.armymaintenance.model.Notes
import com.google.android.material.bottomsheet.BottomSheetDialog
import java.util.*
class EditNotesFragment : Fragment() {
val oldNotes by navArgs<EditNotesFragmentArgs>()
lateinit var binding: FragmentEditNotesBinding
val viewModel : NotesViewModel by viewModels()
var priority:String = "1"
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
binding= FragmentEditNotesBinding.inflate(layoutInflater,container,false)
setHasOptionsMenu(true)
binding.edtBumperNumber.setText(oldNotes.data.bumperNumber)
binding.edtEquipmentType.setText(oldNotes.data.equipmentType)
binding.edtModelType.setText(oldNotes.data.modelType)
binding.edtSerialNumber.setText(oldNotes.data.serialNumber)
binding.edtFaultDescription.setText(oldNotes.data.faultDescription)
binding.edtServiceDate.setText(oldNotes.data.serviceDate)
if(oldNotes.data.priority == "1"){
priority="1"
binding.fmc.setImageResource(R.drawable.baseline_check_24)
binding.nmc.setImageResource(0)
binding.fault.setImageResource(0)
}else if(oldNotes.data.priority == "2"){
priority="2"
binding.fault.setImageResource(R.drawable.baseline_check_24)
binding.nmc.setImageResource(0)
binding.fmc.setImageResource(0)
}else{
priority="3"
binding.nmc.setImageResource(R.drawable.baseline_check_24)
binding.fmc.setImageResource(0)
binding.fault.setImageResource(0)
}
binding.fmc.setOnClickListener{
binding.fmc.setImageResource(R.drawable.baseline_check_24)
binding.nmc.setImageResource(0)
binding.fault.setImageResource(0)
priority="1"
}
binding.fault.setOnClickListener{
binding.fault.setImageResource(R.drawable.baseline_check_24)
binding.nmc.setImageResource(0)
binding.fmc.setImageResource(0)
priority="2"
}
binding.nmc.setOnClickListener{
binding.nmc.setImageResource(R.drawable.baseline_check_24)
binding.fmc.setImageResource(0)
binding.fault.setImageResource(0)
priority="3"
}
binding.btnEditSaveNotes.setOnClickListener {
updateNotes(it)
}
return binding.root
}
private fun updateNotes(it: View?) {
val bumperNumber = binding.edtBumperNumber.text.toString()
val equipmentType = binding.edtEquipmentType.text.toString()
val modelType = binding.edtModelType.text.toString()
val serialNumber = binding.edtSerialNumber.text.toString()
val serviceDate = binding.edtServiceDate.text.toString()
val faultDescription = binding.edtFaultDescription.text.toString()
val d = Date()
val notesDate: CharSequence = DateFormat.format("MMMM d, yyyy ", d.time)
// we gotta update id on clicking
val data = Notes(oldNotes.data.id,bumperNumber,equipmentType,modelType,serialNumber,serviceDate,faultDescription,notesDate.toString(),priority)
viewModel.updateNotes(data)
Toast.makeText(requireContext(),"Note updated successfully", Toast.LENGTH_SHORT).show()
Navigation.findNavController(it!!).navigate(R.id.action_editNotesFragment_to_homeFragment)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.delete_menu,menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if(item.itemId==R.id.menu_delete){
val bottomSheet: BottomSheetDialog = BottomSheetDialog(requireContext(),R.style.BottomSheetStyle)
bottomSheet.setContentView(R.layout.dialog_delete)
val textViewYes=bottomSheet.findViewById<TextView>(R.id.dialog_yes)
val textViewNo=bottomSheet.findViewById<TextView>(R.id.dialog_no)
textViewYes?.setOnClickListener {
viewModel.deleteNotes(oldNotes.data.id!!)
bottomSheet.dismiss()
Navigation.findNavController(binding.btnEditSaveNotes).navigate(R.id.action_editNotesFragment_to_homeFragment)
}
textViewNo?.setOnClickListener {
bottomSheet.dismiss()
}
bottomSheet.show()
}
return super.onOptionsItemSelected(item)
}
}
CreateNotesFragment.kt
package com.cameron.armymaintenance.ui.fragments
import android.os.Bundle
import android.text.format.DateFormat
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.viewModels
import androidx.navigation.Navigation
import com.cameron.armymaintenance.model.Notes
import com.cameron.armymaintenance.R
import com.cameron.armymaintenance.ViewModel.NotesViewModel
import com.cameron.armymaintenance.databinding.FragmentCreateNotesBinding
import java.util.*
class CreateNotesFragment : Fragment() {
private lateinit var binding: FragmentCreateNotesBinding
private var priority : String = "1"
private val viewModel : NotesViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
binding = FragmentCreateNotesBinding.inflate(layoutInflater,container,false)
binding.fmc.setImageResource(R.drawable.baseline_check_24)
binding.fmc.setOnClickListener{
binding.fmc.setImageResource(R.drawable.baseline_check_24)
binding.fault.setImageResource(0)
binding.nmc.setImageResource(0)
priority="1"
}
binding.fault.setOnClickListener{
binding.fault.setImageResource(R.drawable.baseline_check_24)
binding.fmc.setImageResource(0)
binding.nmc.setImageResource(0)
priority="2"
}
binding.nmc.setOnClickListener{
binding.nmc.setImageResource(R.drawable.baseline_check_24)
binding.fmc.setImageResource(0)
binding.fault.setImageResource(0)
priority="3"
}
binding.btnSaveNotes.setOnClickListener {
createNotes(it)
}
return binding.root
}
private fun createNotes(it: View?) {
val bumperNumber = binding.edtBumperNumber.text.toString()
val equipmentType = binding.edtEquipmentType.text.toString()
val modelType = binding.edtModel.text.toString()
val serialNumber = binding.edtSerialNumber.text.toString()
val serviceDate = binding.edtServiceDate.text.toString()
val faultDescription = binding.edtFaultDescription.text.toString()
val d = Date()
val notesDate: CharSequence = DateFormat.format("MMMM d, yyyy ", d.time)
val data = Notes(null,bumperNumber,equipmentType,modelType,serialNumber,serviceDate,faultDescription,notesDate.toString(),priority)
viewModel.addNotes(data)
Toast.makeText(requireContext(),"Notes created successfully",Toast.LENGTH_SHORT).show()
Navigation.findNavController(it!!).navigate(R.id.action_createNotesFragment_to_homeFragment)
}
}
NotesAdapter.kt
package com.cameron.armymaintenance.ui.Adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.navigation.Navigation
import androidx.recyclerview.widget.RecyclerView
import com.cameron.armymaintenance.R
import com.cameron.armymaintenance.databinding.ItemNotesBinding
import com.cameron.armymaintenance.model.Notes
import com.cameron.armymaintenance.ui.fragments.HomeFragmentDirections
class NotesAdapter(val requireContext: Context, var notesList: List<Notes>) : RecyclerView.Adapter<NotesAdapter.notesViewHolder>() {
//Contructor
//search ke liye filter
fun filtering(newFilteredList:ArrayList<Notes>){
notesList=newFilteredList
notifyDataSetChanged()
}
class notesViewHolder(val binding : ItemNotesBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): notesViewHolder {
return notesViewHolder(
ItemNotesBinding.inflate(
LayoutInflater.from(parent.context),parent,false))
}
override fun onBindViewHolder(holder: notesViewHolder, position: Int) {
holder.binding.notesBumperNumber.text = notesList[position].bumperNumber
holder.binding.notesEquipmentType.text = notesList[position].equipmentType
holder.binding.notesModelType.text = notesList[position].modelType
holder.binding.notesSerialNumber.text = notesList[position].serialNumber
holder.binding.notesFaultDescription.text = notesList[position].faultsDescription
holder.binding.notesServiceDate.text = notesList[position].serviceDate
holder.binding.notesDate.text = notesList[position].date
if(notesList[position].priority == "1"){
holder.binding.viewPriority.setBackgroundResource(R.drawable.green_dot)
}else if(notesList[position].priority == "2"){
holder.binding.viewPriority.setBackgroundResource(R.drawable.yellow_dot)
}else{
holder.binding.viewPriority.setBackgroundResource(R.drawable.red_dot)
}
holder.binding.root.setOnClickListener {
val action = HomeFragmentDirections.actionHomeFragmentToEditNotesFragment(notesList[position])
Navigation.findNavController(it).navigate(action)
}
}
override fun getItemCount(): Int {
return notesList.size
}
}
Notes.kt
package com.cameron.armymaintenance.model
import android.os.Parcelable
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.parcelize.Parcelize
#Parcelize
#Entity(tableName = "Notes")
class Notes(
#PrimaryKey(autoGenerate = true)
var id:Int?=null,
var bumperNumber: String,
var equipmentType: String,
var modelType: String,
var serialNumber: String,
var faultsDescription: String,
var serviceDate: String,
var notes: String,
var date: String,
var priority: String
): Parcelable

Related

How to casting this Generic class for two layouts in Kotlin?

package com.hypermarket.ui.main.adapter
import android.graphics.Paint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialog
class ProductsGridAdapter(
fragment: Fragment,
val context: MainActivity,
private val myPref: PreferencesManager,
screenName: ScreenType,
productInterface: ProductInterface,
rv: RecyclerView,
isFromDetails: Boolean,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var items = arrayListOf<ProductModel>()
var parentFragment: Fragment = fragment
var bottomSheetDialog: BottomSheetDialog? = null
var mAdapter: ProductsGridAdapter? = null
var screenType: ScreenType
var productInterface: ProductInterface = productInterface
private var loading = false
private var onLoadMoreListener: OnLoadMoreListener? = null
private var recyclerView: RecyclerView
private var isFromDetails: Boolean? = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
if (isFromDetails == false) {
val itemBinding: RowItemProductGridBinding =
RowItemProductGridBinding.inflate(layoutInflater, parent, false)
//itemBinding.productInterface = productInterface
return ViewHolder(itemBinding)
} else {
val itemBinding: RowItemSimilarProductBinding =
RowItemSimilarProductBinding.inflate(layoutInflater, parent, false)
//itemBinding.productInterface = productInterface
return ViewHolder(itemBinding)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (isFromDetails == true) {
(holder as ViewHolder<RowItemSimilarProductBinding>).bind(position)
} else {
(holder as ViewHolder<RowItemProductGridBinding>).bind(position)
}
}
fun <T> getCurrentView(binding: T): View {
if (isFromDetails == true) {
return (binding as RowItemSimilarProductBinding).root
} else {
return (binding as RowItemProductGridBinding).root
}
}
fun <T> getItemSimilarProductBinding(binding: T): RowItemSimilarProductBinding {
return binding as RowItemSimilarProductBinding
}
fun <T> getItemProductGridBinding(binding: T): RowItemProductGridBinding {
return binding as RowItemProductGridBinding
}
inner class ViewHolder<T>(bindings: T) : RecyclerView.ViewHolder(getCurrentView(bindings)) {
private val binding: T = bindings
fun bind(position: Int) {
val model: ProductModel = items[position]
if (isFromDetails == true) {
getItemSimilarProductBinding(binding).item = model
} else {
getItemProductGridBinding(binding).item = model
}
binding.tvMRP.paintFlags = binding.tvMRP.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
binding.executePendingBindings()
binding.ivProduct.setBackgroundResource(0)
if (model.weight_based == Constants.WEIGHT_BASED) {
binding.parentView.disabled =
AppValidator.toFloat(model.available_qty_weight_based) < AppValidator.toFloat(
model.gram_variant_en)
} else {
binding.parentView.disabled = AppValidator.toInt(model.available_qty) == 0
}
binding.ivType.setGone()
}
}
Here I want to use two different layout (Which you can see on onCreateViewHolder() method). two different layout has all the property named with one another. only design is different. now I want to use the layout based on isFromDetails (Which is boolean value). based on that, I want access the both layout bindings with single variable. is it possible?
private val binding: T = bindings
fun bind(position: Int) {
val model: ProductModel = items[position]
if (isFromDetails == true) {
getItemSimilarProductBinding(binding).item = model
} else {
getItemProductGridBinding(binding).item = model
}
binding.tvMRP.paintFlags = binding.tvMRP.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
binding.ivType.setGone()
}
here both layout has tvMRP and ivType. Is it possible without adding boiler plate code?

kotlin function is not recognized in java

I need to transform this and some other code made in kotlin to java, but it is not working. Here is one of them.
BackgroundActivity.kt (this I need to transform to java):
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun surfaceChanged(holder: SurfaceHolder, p1: Int, p2: Int, p3: Int) {
RtpService.setView(surfaceView)
RtpService.startPreview()
}
This function is inside a kotlin class, that I will keep
RtpService.kt
package com.wifi.service.backgroundexample
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import com.wifi.rtplibrary.base.Camera2Base
import com.wifi.rtplibrary.rtmp.RtmpCamera2
import com.wifi.rtplibrary.rtsp.RtspCamera2
import com.wifi.rtplibrary.view.OpenGlView
import com.wifi.service.R
/**
* Basic RTMP/RTSP service streaming implementation with camera2
*/
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
class RtpService : Service() {
private var endpoint: String? = null
override fun onCreate() {
super.onCreate()
Log.e(TAG, "RTP service create")
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH)
notificationManager?.createNotificationChannel(channel)
}
keepAliveTrick()
}
private fun keepAliveTrick() {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
val notification = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_baseline_settings_24_branco)
.setSilent(true)
.setOngoing(false)
//.setContentTitle("")
.build()
startForeground(1, notification)
} else {
startForeground(1, Notification())
}
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.e(TAG, "RTP service started")
endpoint = intent?.extras?.getString("endpoint")
if (endpoint != null) {
prepareStreamRtp()
startStreamRtp(endpoint!!)
}
return START_STICKY
}
companion object {
private const val TAG = "RtpService"
//private const val channelId = "rtpStreamChannel"
private const val channelId = "Android"
private const val notifyId = 123456
private var notificationManager: NotificationManager? = null
private var camera2Base: Camera2Base? = null
private var openGlView: OpenGlView? = null
private var contextApp: Context? = null
fun setView(openGlView: OpenGlView) {
this.openGlView = openGlView
camera2Base?.replaceView(openGlView)
}
fun setView(context: Context) {
contextApp = context
this.openGlView = null
camera2Base?.replaceView(context)
}
fun startPreview() {
camera2Base?.startPreview()
}
fun init(context: Context) {
contextApp = context
if (camera2Base == null) camera2Base = RtmpCamera2(context, true, connectCheckerRtp)
}
fun stopStream() {
if (camera2Base != null) {
if (camera2Base!!.isStreaming) camera2Base!!.stopStream()
}
}
fun stopPreview() {
if (camera2Base != null) {
if (camera2Base!!.isOnPreview) camera2Base!!.stopPreview()
}
}
private val connectCheckerRtp = object : ConnectCheckerRtp {
override fun onConnectionStartedRtp(rtpUrl: String) {
//showNotification("Stream connection started")
}
override fun onConnectionSuccessRtp() {
//showNotification("Stream started")
Log.e(TAG, "RTP service destroy")
}
override fun onNewBitrateRtp(bitrate: Long) {
}
override fun onConnectionFailedRtp(reason: String) {
//showNotification("Stream connection failed")
Log.e(TAG, "RTP service destroy")
}
override fun onDisconnectRtp() {
//showNotification("Stream stopped")
}
override fun onAuthErrorRtp() {
//showNotification("Stream auth error")
}
override fun onAuthSuccessRtp() {
//showNotification("Stream auth success")
}
}
private fun showNotification(text: String) {
contextApp?.let {
val notification = NotificationCompat.Builder(it, channelId)
//.setSmallIcon(R.mipmap.ic_launcher)
.setSmallIcon(R.drawable.ic_baseline_settings_24_branco)
.setContentTitle("RTP Stream")
.setSilent(true)
.setOngoing(false)
.setContentText(text).build()
notificationManager?.notify(notifyId, notification)
}
}
}
override fun onDestroy() {
super.onDestroy()
Log.e(TAG, "RTP service destroy")
stopStream()
}
private fun prepareStreamRtp() {
stopStream()
stopPreview()
if (endpoint!!.startsWith("rtmp")) {
camera2Base = if (openGlView == null) {
RtmpCamera2(baseContext, true, connectCheckerRtp)
} else {
RtmpCamera2(openGlView, connectCheckerRtp)
}
} else {
camera2Base = if (openGlView == null) {
RtspCamera2(baseContext, true, connectCheckerRtp)
} else {
RtspCamera2(openGlView, connectCheckerRtp)
}
}
}
private fun startStreamRtp(endpoint: String) {
if (!camera2Base!!.isStreaming) {
if (camera2Base!!.prepareVideo() && camera2Base!!.prepareAudio()) {
camera2Base!!.startStream(endpoint)
}
} else {
//showNotification("You are already streaming :(")
}
}
}
I tried this with java:
JavaVersion.java
RtpService rtpService = new RtpService();
rtpService.init(this);
init is not recognized, I don't work well with kotlin yet
can you try this
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public void surfaceChanged(SurfaceHolder holder, int p1, int p2, int p3) {
Intrinsics.checkNotNullParameter(holder, "holder");
RtpService.setView(surfaceView);
RtpService.startPreview();
}
public void setView(OpenGlView openGlView) {
this.openGlView = openGlView;
camera2Base.replaceView(openGlView);
}

Kotlin: my logs showing java.lang.ClassCastException: com.example.playlist.model.PlaylistResponse cannot be cast to java.util.List error

my model response is an object that contains another object and list in it. Example:
{
"name": " ",
"tracks": {
"title": " "
"artists": {
"name": " "
}
"images": {
"poster": {
"url": " "
}
}
}
}
therefore this is my model data class:
package com.example.playlist.model
import com.google.gson.annotations.SerializedName
data class PlaylistResponse(
#SerializedName("name")
val name: String,
#SerializedName("tracks")
val tracks: List<Track>,
)
i used adapter to bind my class with my data:
package com.example.playlist.Adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.playlist.R
import com.example.playlist.model.PlaylistResponse
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.playlist_item.view.*
class PlaylistAdapter(private val context: Context, private val playList: List<PlaylistResponse>):
RecyclerView.Adapter<PlaylistAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var trackImage : ImageView
var trackName : TextView
var artistName : TextView
init{
trackImage = itemView.image_track
trackName = itemView.name_track
artistName = itemView.name_artist
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
var itemView = LayoutInflater.from(context).inflate(R.layout.playlist_item, parent, false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Picasso.get().load(playList[position].tracks[position].images.poster.url).into(holder.trackImage)
holder.trackName.text = playList[position].tracks[position].title
holder.artistName.text = playList[position].tracks[position].artists[position].name
}
override fun getItemCount(): Int {
return playList.size
}
}
and i used the adapter in my playListActivity class:
package com.example.playlist
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.playlist.Adapter.PlaylistAdapter
import com.example.playlist.model.PlaylistResponse
import com.example.playlist.network.ApiClient
import com.example.playlist.network.PrefService
import kotlinx.android.synthetic.main.activity_playlist.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class PlaylistActivity : AppCompatActivity() {
private lateinit var prefService: PrefService
private lateinit var apiClient: ApiClient
lateinit var layoutManager: LinearLayoutManager
lateinit var adapter : PlaylistAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_playlist)
apiClient = ApiClient()
prefService = PrefService(this)
recyclerPlaylist.setHasFixedSize(true)
layoutManager = LinearLayoutManager(this)
recyclerPlaylist.layoutManager = layoutManager
fetchPlaylist()
}
private fun fetchPlaylist() {
apiClient.getApiService(this).fetchPlaylist(token = "Bearer ${prefService.fetchAuthToken()}")
.enqueue(object : Callback<PlaylistResponse> {
override fun onFailure(call: Call<PlaylistResponse>, t: Throwable) {
}
override fun onResponse(call: Call<PlaylistResponse>, response: Response<PlaylistResponse>) {
adapter = PlaylistAdapter(baseContext, response.body() as List<PlaylistResponse>)
recyclerPlaylist.adapter = adapter
}
})
}
}
i knew the error is caused by this line :
adapter = PlaylistAdapter(baseContext, response.body() as List<PlaylistResponse>)
but i didn't know how to turn the object into a list. i have watch & read countless of videos and articles but still failed to understand how to solved my problem. i'm very new to kotlin therefore any solutions and helps are highly appreciated.
data class LoginResponse(
var status: Boolean = false,
var data: LoginData? = null,
var message: String = ""
)
data class LoginData(
var id: Int? = null,
var uuid: String = "",
var name: String = "",
var email: String = "",
var email_verified_at: String = "",
var api_token: String = "",
var created_at: String = "",
var updated_at: String = ""
)
Create Class Like This

Kotlin: How to set text in textview from a row of data from SQLite

Intent: Retrieve a random single row of data from SQLite table and display in activity english word on that row of data.
Case: Lack of knowledge on how to display it in the activity
Expected Result: TextView displaying a random word using "SELECT * FROM tbl_word ORDER BY RANDOM() LIMIT 1"
Note: RANDOM doesnt work in kotlin android studio
Main activity where I want word to show using text view ENWord
package com.example.ankiapp
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import kotlin.system.exitProcess
private lateinit var enWord: TextView
private lateinit var sqliteHelper: SQLiteHelper
private var adapter: WordAdapter?=null
class ENWordScreen : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_enword_screen)
initView()
sqliteHelper= SQLiteHelper(this)
val word = sqliteHelper.getRandomWord()
enWord.setText("")//stuck here <<<<<
//ホーム画面に遷移ボタン
val button = findViewById<Button>(R.id.backBtn1)//ホーム画面に戻る
button.setOnClickListener{
val intent = Intent(this,HomeScreen::class.java)
startActivity(intent)
}
//アプリを終了するメソッド
val quitButton = findViewById<Button>(R.id.quitBtn)
quitButton.setOnClickListener{
finish()
exitProcess(0);
}
}
private fun initView(){
enWord = findViewById(R.id.etEngWord)
}
}
SQLiteHelper class
fun getRandomWord():ArrayList<WordModel>{//1つのランダムデータを取得メソッド
val randomWordModel: ArrayList<WordModel> = ArrayList()
val selectQuery = "SELECT * FROM " + TBL_WORD+ " ORDER BY RANDOM() LIMIT 1"
val db = this.readableDatabase
val cursor: Cursor = db.rawQuery(selectQuery, null)
var id: Int
var date: String
var engword: String
var jpword: String
var time: Int
var answer: Int
if (cursor.moveToFirst())
do {
id = cursor.getInt(cursor.getColumnIndexOrThrow("id"))
date = cursor.getString(cursor.getColumnIndexOrThrow("date"))
engword = cursor.getString(cursor.getColumnIndexOrThrow("englishWord"))
jpword = cursor.getString(cursor.getColumnIndexOrThrow("jpnWord"))
time = cursor.getInt(cursor.getColumnIndexOrThrow("time"))
answer = cursor.getInt(cursor.getColumnIndexOrThrow("answer"))
val randomWord = WordModel(id, date, engword, jpword, time, answer)
randomWordModel.add(randomWord)
} while (cursor.moveToNext())
cursor.close()
db.close()
return randomWordModel
}
WordModel Class
package com.example.ankiapp
import android.os.Parcelable
import java.util.*
import kotlinx.parcelize.Parcelize
#Parcelize data class WordModel(
var id: Int? = null,
var date: String = "",
var engword: String = "",
var jpword: String = "",
var time: Int = 0,
var answer: Int = 0
) : Parcelable
WordAdapter class
class WordAdapter:RecyclerView.Adapter<WordAdapter.WordViewHolder>() {
private var wordList: ArrayList<WordModel> = ArrayList()
private var onClickItem: ((WordModel) -> Unit)?=null
private var onClickDeleteItem: ((WordModel) -> Unit)?=null
fun addItems(items:ArrayList<WordModel>){
this.wordList = items
notifyDataSetChanged()
}
//レコードを削除メソッドを呼び出す
fun setOnClickDeleteItem(callback: (WordModel) -> Unit){
this.onClickDeleteItem = callback
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = WordViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.card_items_word,parent,false)
)
override fun onBindViewHolder(holder: WordViewHolder, position: Int) {
val word = wordList[position]
holder.bindView(word)
holder.itemView.setOnClickListener{onClickItem?.invoke(word)}
holder.btnDelete.setOnClickListener{onClickDeleteItem?.invoke(word)}
}
override fun getItemCount(): Int {
return wordList.size
}
class WordViewHolder(var view: View):RecyclerView.ViewHolder(view){
var id = view.findViewById<TextView>(R.id.tvID)
var date = view.findViewById<TextView>(R.id.tvDate)
var jpword = view.findViewById<TextView>(R.id.tvJpWord)
var engword = view.findViewById<TextView>(R.id.tvEngWord)
var btnDelete = view.findViewById<Button>(R.id.deleteBtn)
fun bindView(word:WordModel){
id.text = word.id.toString()
date.text = word.date
jpword.text = word.jpword
engword.text = word.engword
}
}
}
So you want something like this
sqliteHelper= SQLiteHelper(this)
val word = sqliteHelper.getRandomWord()
enWord.setText(word[0].engword)

findContainingViewHolder returns null

In the following application, you have basically 2 fragments: 1) a food database, 2) a consumed food list
1) the user can manually add foods with the corresponding macronutrient information (kcal, carbs, protein, etc.). The data is stored in a SQL database via Room and is being observed via LiveData
2) a fragment (FoodDiaryFragment.kt) that shows the "consumed" food via a recyclerView. The user can add foods by tapping on a FAB which sends the user to another fragment (AddConsumedFoodFragment.kt).
this shows a spinner and a form. The spinner shows a list of the foods from the food database (from 1). In the form the user can just enter one value (how much gram of the selected food has been consumed).
This data is also added to the same SQL database in a table with: id (autogenerated), amount (entered by the user), consumedFood (selected from the spinner and connected to the other table via a foreign key) and consumedDate (autogenerated date at the moment when the item is added).
The recycler view (2) shows the items with additional information which are calculated in the RecyclerViewAdapter (basically multiplying the amount and the corresponding macronutrient info [kcal, carbs, etc.], see getDailyValues() in the adapter) and also "groups" the items by date. This means, for each day a separate viewHolder (DateViewHolder) which displays the day (and the sum of each macro) is being added to the recyclerView (see sortAndGroupFood() in FoodDiaryFragment.kt)
As I am using RecyclerView Selection setStableIds() (see init block) is set to true. StableIdKeyProvider.java adds an ChildAttachStateChangeListener with the onChilViewDetachedFromWindows() function.
I am not sure exactly when this function (I would love an explanation) is called but this seems to happen sometimes, when either an Item is deleted (items can be deleted via RecyclerView.Selection) or an item is added. The app crashes with the below shown NullPointerException. When the app is restarted the action that eventually ended in the crash has been conducted successfully.
I am really desperate and have tried to figure out why a null view is passed to this function for several hours now, but obviously I have not been succesful.
According to the docs findContainingViewHolder returns null if the provided view is not a descendant of that RecyclerView
Question: What problem causes the return of null and how can I precent it?
GitHub Depo Link
FoodDiaryFragment.kt
import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.selection.*
import androidx.recyclerview.widget.RecyclerView
import com.hooni.macrotracker.R
import com.hooni.macrotracker.adapter.ConsumedFoodRecyclerViewAdapter
import com.hooni.macrotracker.data.ConsumedFood
import com.hooni.macrotracker.recyclerviewselector.ConsumedFoodItemDetailsLookup
import com.hooni.macrotracker.viewmodels.FoodViewModel
import kotlinx.android.synthetic.main.fragment_food_diary.view.*
import java.text.DateFormat
import java.text.SimpleDateFormat
class FoodDiaryFragment : Fragment() {
private lateinit var foodViewModel: FoodViewModel
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: ConsumedFoodRecyclerViewAdapter
private val dateFormat = DateFormat.getDateInstance()
var tracker: SelectionTracker<Long>? = null
var actionMode: ActionMode? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val v = inflater.inflate(R.layout.fragment_food_diary, container, false)
initRecyclerView(v)
initViewModel()
initButtons(v)
// this makes sure, that in case the last destination was addConsumedFoodFragment
// the 'back' button doesn't bring you back to the addConsumedFoodFragment, but to the one that
// has been visited before
findNavController().popBackStack(R.id.addConsumedFoodFragment, true)
return v
}
private fun initButtons(v: View) {
val addNewFood = v.addNewConsumedFood
addNewFood.setOnClickListener {
findNavController().navigate(R.id.action_diaryFragment_to_addConsumedFoodFragment)
}
}
private fun initRecyclerView(view: View) {
recyclerView = view.food_diary_recyclerView
adapter = ConsumedFoodRecyclerViewAdapter()
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(activity)
tracker = SelectionTracker.Builder<Long>(
"selectedItemsConsumedFoo",
recyclerView,
StableIdKeyProvider(recyclerView),
ConsumedFoodItemDetailsLookup(recyclerView),
StorageStrategy.createLongStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectAnything()
).build()
val actionModeCallBack = object : ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
val inflater = mode?.menuInflater
actionMode = mode
actionMode?.title = getString(R.string.delete)
inflater?.inflate(R.menu.action_menu, menu)
return true
}
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
return when (item?.itemId) {
R.id.action_menu_delete -> {
removeItems(tracker?.selection!!)
tracker?.clearSelection()
actionMode?.finish()
actionMode = null
return true
}
else -> false
}
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
return false
}
override fun onDestroyActionMode(mode: ActionMode?) {
tracker?.clearSelection()
actionMode?.finish()
actionMode = null
}
}
tracker?.addObserver(
object : SelectionTracker.SelectionObserver<Long>() {
override fun onSelectionChanged() {
if (tracker?.selection!!.size() > 0) {
if (actionMode == null) activity?.startActionMode(actionModeCallBack)
} else {
actionMode?.finish()
actionMode = null
}
}
}
)
adapter.tracker = tracker
}
private fun removeItems(selection: Selection<Long>) {
val foodsToDelete = adapter.getFood(selection)
foodsToDelete.forEach {
foodViewModel.deleteConsumedFood(it)
}
}
private fun initViewModel() {
foodViewModel = ViewModelProvider(this).get(FoodViewModel::class.java)
foodViewModel.allConsumedFood.observe(viewLifecycleOwner, Observer { consumedFoodList ->
consumedFoodList?.let { adapter.setConsumedFood(sortAndGroupFood(consumedFoodList)) }
})
foodViewModel.allFood.observe(viewLifecycleOwner, Observer { food ->
food?.let { adapter.setFood(food.sortedBy {it.foodName}) }
})
}
private fun sortAndGroupFood(consumedFoodList: List<ConsumedFood>): List<ConsumedFood> {
val sortedList = consumedFoodList.sortedBy{it.consumedDate}
val groupedMap: Map<String,List<ConsumedFood>> = sortedList.groupBy { dateFormat.format(it.consumedDate)}
val finalizedList = mutableListOf<ConsumedFood>()
groupedMap.forEach {
finalizedList.add(createDateHeader(it.key))
finalizedList.addAll(it.value)
}
return finalizedList.toList()
}
private fun createDateHeader(dateString: String): ConsumedFood {
val date = dateFormat.parse(dateString)
return ConsumedFood(null,-1,"DATE_HEADER",date!!)
}
}
AddFoodFragment.kt
package com.hooni.macrotracker.fragments
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import com.google.android.material.textfield.TextInputLayout
import com.hooni.macrotracker.R
import com.hooni.macrotracker.data.Food
import com.hooni.macrotracker.util.Tools
import com.hooni.macrotracker.viewmodels.FoodViewModel
import kotlinx.android.synthetic.main.fragment_add_food.view.*
import java.text.DecimalFormatSymbols
import java.util.*
class AddFoodFragment : Fragment() {
private lateinit var foodViewModel: FoodViewModel
private lateinit var enterTextFoodName: TextInputLayout
private lateinit var enterTextKcal: TextInputLayout
private lateinit var enterTextCarbs: TextInputLayout
private lateinit var enterTextProtein: TextInputLayout
private lateinit var enterTextFat: TextInputLayout
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val v = inflater.inflate(R.layout.fragment_add_food, container, false)
initButtons(v)
initViewModel()
initTextInPutLayouts(v)
return v
}
private fun initTextInPutLayouts(v: View) {
enterTextFoodName = v.enter_food_name
enterTextKcal = v.enter_kcal
enterTextCarbs = v.enter_carbs
enterTextProtein = v.enter_protein
enterTextFat = v.enter_fat
enterTextCarbs.setNumberDecimalInputOnly()
enterTextProtein.setNumberDecimalInputOnly()
enterTextFat.setNumberDecimalInputOnly()
}
private fun initButtons(view: View) {
view.cancel_add_food.setOnClickListener {
findNavController().navigate(R.id.action_addFoodFragment_to_foodListFragment)
Tools.hideSoftKeyboard(view, context)
}
view.add_food.setOnClickListener {
if (validateValues()) {
val newFood = createNewFood()
foodViewModel.insertFood(newFood)
showSuccessfulAdd()
view.cancel_add_food.performClick()
}
}
}
private fun validateValues(): Boolean {
val isFoodNameValid = enterTextFoodName.validateInput { it != null }
val isKcalValid = enterTextKcal.validateInput { it?.toIntOrNull() != null }
val isCarbsValid = enterTextCarbs.validateInput { it?.toDoubleOrNull() != null }
val isProteinValid = enterTextProtein.validateInput { it?.toDoubleOrNull() != null }
val isFatValid = enterTextFat.validateInput { it?.toDoubleOrNull() != null }
return (isFoodNameValid
&& isKcalValid
&& isCarbsValid
&& isProteinValid
&& isFatValid)
}
private fun initViewModel() {
foodViewModel = ViewModelProvider(this).get(FoodViewModel::class.java)
}
private fun createNewFood(): Food {
val foodName =
enterTextFoodName.editText?.text.toString().trim()
val kcal =
enterTextKcal.editText?.text.toString().trim().toInt()
val carbs =
enterTextCarbs.editText?.text.toString().replaceDecimalSeparator().toDouble()
val protein =
enterTextProtein.editText?.text.toString().replaceDecimalSeparator().toDouble()
val fat = enterTextFat.editText?.text.toString().replaceDecimalSeparator().toDouble()
return Food(foodName, kcal, carbs.round(), fat.round(), protein.round())
}
private fun showSuccessfulAdd() {
Toast.makeText(
activity,
"${enterTextFoodName.editText?.text.toString().trim()} added to database",
Toast.LENGTH_SHORT
).show()
}
private inline fun TextInputLayout.validateInput(validate: (String?) -> Boolean): Boolean {
val textToValidate = this.editText?.text.toString().replaceDecimalSeparator().trim()
when {
textToValidate.isEmpty() -> {
error = getString(R.string.cant_be_empty)
}
!validate(textToValidate) -> {
error = getString(R.string.invalid_value)
}
else -> {
error = null
return true
}
}
return false
}
private fun TextInputLayout.setNumberDecimalInputOnly() {
this.editText?.apply {
inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
}
}
private fun Double.round(decimals: Int = 1): Double = "%.${decimals}f".format(Locale.US,this).toDouble()
#SuppressLint("NewApi")
private fun String.replaceDecimalSeparator(): String {
val decimalSeparator = when(Build.VERSION.SDK_INT) {
in Int.MIN_VALUE..Build.VERSION_CODES.M -> {DecimalFormatSymbols(resources.configuration.locale).decimalSeparator}
else -> DecimalFormatSymbols(resources.configuration.locales[0]).decimalSeparator
}
return this.replace(decimalSeparator, '.')
}
}
ConsumedFoodRecyclerViewAdapter.kt
package com.hooni.macrotracker.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.selection.ItemDetailsLookup
import androidx.recyclerview.selection.Selection
import androidx.recyclerview.selection.SelectionTracker
import androidx.recyclerview.widget.RecyclerView
import com.hooni.macrotracker.R
import com.hooni.macrotracker.data.ConsumedFood
import com.hooni.macrotracker.data.Food
import kotlinx.android.synthetic.main.fragment_food_diary_date_list_item.view.*
import kotlinx.android.synthetic.main.fragment_food_diary_list_item.view.*
import java.text.DateFormat
import java.util.*
private const val DATE_VIEW_HOLDER = 0
private const val CONSUMED_FOOD_VIEW_HOLDER = 1
class ConsumedFoodRecyclerViewAdapter : RecyclerView.Adapter<ConsumedFoodRecyclerViewAdapter.BaseViewHolder<*>>() {
private var mConsumedFoodList = emptyList<ConsumedFood>()
private var mFoodList = emptyList<Food>()
var tracker: SelectionTracker<Long>? = null
init {
setHasStableIds(true)
}
abstract class BaseViewHolder<T>(itemView: View): RecyclerView.ViewHolder(itemView) {
abstract fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long>
}
inner class ConsumedFoodViewHolder(mView: View) :BaseViewHolder<ConsumedFood>(mView) {
private val mConsumedFoodName: TextView = mView.consumed_food_name
private val mConsumedKcal: TextView = mView.consumed_kcal
private val mConsumedAmount: TextView = mView.consumed_amount
private val mConsumedCarbs: TextView = mView.consumed_carb_amount
private val mConsumedProtein: TextView = mView.consumed_protein_amount
private val mConsumedFat: TextView = mView.consumed_fat_amount
override fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getSelectionKey(): Long? = itemId
override fun getPosition(): Int = adapterPosition
}
fun bind(isActivated: Boolean = false, item: ConsumedFood) {
val consumedMacroOfFood = calculateConsumedMacro(item.amount,item.consumedFood)
itemView.isActivated = isActivated
mConsumedFoodName.text = item.consumedFood
mConsumedKcal.text = itemView.context.getString(R.string.list_item_kcal,consumedMacroOfFood[0].toInt())
mConsumedAmount.text = itemView.context.getString(R.string.list_item_amount,item.amount)
mConsumedCarbs.text = itemView.context.getString(R.string.list_item_macro,consumedMacroOfFood[1])
mConsumedProtein.text = itemView.context.getString(R.string.list_item_macro,consumedMacroOfFood[2])
mConsumedFat.text = itemView.context.getString(R.string.list_item_macro,consumedMacroOfFood[3])
}
}
inner class DateViewHolder(mView: View): BaseViewHolder<ConsumedFood>(mView) {
private val mConsumedDate: TextView = mView.food_diary_date
private val mTotalConsumedDayKcal: TextView = mView.food_diary_date_sum_kcal
private val mTotalConsumedDayCarbs: TextView = mView.food_diary_date_sum_carbs
private val mTotalConsumedDayProtein: TextView = mView.food_diary_date_sum_protein
private val mTotalConsumedDayFat: TextView = mView.food_diary_date_sum_fat
override fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
object : ItemDetailsLookup.ItemDetails<Long>() {
override fun getSelectionKey(): Long? = itemId
override fun getPosition(): Int = adapterPosition
}
fun bind(item: ConsumedFood) {
mConsumedDate.text = itemView.context
.getString(R.string.list_item_date,DateFormat.getDateInstance(DateFormat.MEDIUM,DateFormat.getAvailableLocales()[0]).format(item.consumedDate))
mTotalConsumedDayKcal.text = itemView.context.getString(R.string.list_item_kcal,getDailyValues(item.consumedDate)[0].toInt())
mTotalConsumedDayCarbs.text = itemView.context.getString(R.string.list_item_macro,getDailyValues(item.consumedDate)[1])
mTotalConsumedDayProtein.text = itemView.context.getString(R.string.list_item_macro,getDailyValues(item.consumedDate)[2])
mTotalConsumedDayFat.text = itemView.context.getString(R.string.list_item_macro,getDailyValues(item.consumedDate)[3])
}
}
override fun getItemViewType(position: Int): Int {
return if(mConsumedFoodList[position].amount < 0) {
DATE_VIEW_HOLDER
} else CONSUMED_FOOD_VIEW_HOLDER
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<ConsumedFood> {
return when(viewType) {
DATE_VIEW_HOLDER -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_food_diary_date_list_item,parent,false)
DateViewHolder(view)
}
//CONSUMED_FOOD_VIEW_HOLDER
else -> {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_food_diary_list_item,parent,false)
ConsumedFoodViewHolder(view)
}
}
}
override fun getItemCount(): Int = mConsumedFoodList.size
override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
when(holder) {
is DateViewHolder -> {
tracker?.let {
holder.bind(mConsumedFoodList[position])
}
}
is ConsumedFoodViewHolder -> {
tracker?.let {
holder.bind(it.isSelected(position.toLong()),mConsumedFoodList[position])
}
}
}
}
override fun getItemId(position: Int): Long = position.toLong()
internal fun getFood(ids: Selection<Long>): List<ConsumedFood> {
val foodsToDelete: MutableList<ConsumedFood> = mutableListOf()
ids.forEach {
foodsToDelete.add(mConsumedFoodList[it.toInt()])
}
return foodsToDelete.toList()
}
internal fun setConsumedFood(consumedFood: List<ConsumedFood>) {
mConsumedFoodList = consumedFood
notifyDataSetChanged()
}
internal fun setFood(food: List<Food>) {
mFoodList = food
notifyDataSetChanged()
}
private fun calculateConsumedMacro(amount: Int, food: String): List<Double>{
val kcalOfTheFood = mFoodList.firstOrNull{ it.foodName == food}?.kcal?.toDouble() ?: 0.0
val carbsOfTheFood = mFoodList.firstOrNull{ it.foodName == food}?.carbs ?: 0.0
val proteinOfTheFood = mFoodList.firstOrNull{ it.foodName == food}?.protein ?: 0.0
val fatOfTheFood = mFoodList.firstOrNull{ it.foodName == food}?.fat ?: 0.0
return listOf(kcalOfTheFood.getAmountOfMacro(amount),
carbsOfTheFood.getAmountOfMacro(amount),
proteinOfTheFood.getAmountOfMacro(amount),
fatOfTheFood.getAmountOfMacro(amount))
}
private fun Double.getAmountOfMacro(amount: Int): Double {
return this * amount / 100
}
private fun getDailyValues(day: Date): List<Double> {
val foodsOfSameDay = mConsumedFoodList.filter{ DateFormat.getDateInstance().format(it.consumedDate) == DateFormat.getDateInstance().format(day)}
val macrosOfSameDay = mutableListOf<List<Double>>()
foodsOfSameDay.forEach {macrosOfSameDay.add(calculateConsumedMacro(it.amount,it.consumedFood))}
val sumKcal = macrosOfSameDay.sumByDouble { it[0] }
val sumCarbs = macrosOfSameDay.sumByDouble { it[1] }
val sumProtein = macrosOfSameDay.sumByDouble { it[2] }
val sumFat = macrosOfSameDay.sumByDouble { it[3] }
return listOf(sumKcal,sumCarbs,sumProtein,sumFat)
}
}
Error
2020-02-18 18:34:07.287 28614-28614/com.hooni.macrotracker E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.hooni.macrotracker, PID: 28614
java.lang.NullPointerException: Attempt to invoke virtual method 'int androidx.recyclerview.widget.RecyclerView$ViewHolder.getAdapterPosition()' on a null object reference
at androidx.recyclerview.selection.StableIdKeyProvider.onDetached(StableIdKeyProvider.java:90)
at androidx.recyclerview.selection.StableIdKeyProvider$1.onChildViewDetachedFromWindow(StableIdKeyProvider.java:69)
at androidx.recyclerview.widget.RecyclerView.dispatchChildDetached(RecyclerView.java:7546)
at androidx.recyclerview.widget.RecyclerView.removeDetachedView(RecyclerView.java:4349)
at androidx.recyclerview.widget.RecyclerView$Recycler.getScrapOrCachedViewForId(RecyclerView.java:6738)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6189)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3851)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1812)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1656)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1565)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:753)
at android.view.View.layout(View.java:20672)
at android.view.ViewGroup.layout(ViewGroup.java:6194)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2792)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2319)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1460)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7183)
2020-02-18 18:34:07.288 28614-28614/com.hooni.macrotracker E/AndroidRuntime: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:949)
at android.view.Choreographer.doCallbacks(Choreographer.java:761)
at android.view.Choreographer.doFrame(Choreographer.java:696)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
when adding a new item to the recyclerView.
The Problem is related to the SelectrionTracker (RecyclerView Selection, which was just briefly mentioned) and it's StableIdKeyProvider.
So... apparently this crash happens, when the StableIdKeyProvider is trying to removed/replaced the stable id while the item (cardview) is no longer attached to the ViewHolder (DateViewHolder or ConsumedFoodViewHolder).
Honestly, I still do not understand the logic behind that, but once I will, I will come back and update this post.
The solution is to provide a custom KeyProvider like the following one:
class ConsumedFoodListItemKeyProvider(private val recyclerView: RecyclerView) : ItemKeyProvider<Long>(ItemKeyProvider.SCOPE_MAPPED) {
override fun getKey(position: Int): Long? {
return recyclerView.adapter?.getItemId(position)
}
override fun getPosition(key: Long): Int {
val viewHolder = recyclerView.findViewHolderForItemId(key)
return viewHolder?.layoutPosition ?: RecyclerView.NO_POSITION
}
}
and use it in the SelectionTracker.Builder

Categories