"Job was Cancelled" Android Kotlin Coroutines - android

In my app, there is an EditText that shows date picker dialog and also a spinner. The spinner item contains data from database that is filled after date is set.
It was working normal until in some occasion that I couldn't replicate the error, it shows "Job was cancelled" in Logcat. And when it happened, the spinner is empty.
Here is my fragment code
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
private var isMandatory : Boolean? = true
class AddReportFragment : Fragment(), AdapterView.OnItemSelectedListener {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
private lateinit var binding: FragmentAddReportBinding
private lateinit var viewModel: AddReportViewModel
private lateinit var sptransactionType: Spinner
private lateinit var spMandatory: Spinner
private lateinit var spNonMandatory: Spinner
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = FragmentAddReportBinding.inflate(inflater)
viewModel = ViewModelProvider(this).get(AddReportViewModel::class.java)
val spBookName : Spinner = binding.spBookName
spMandatory = binding.spMandatory
spNonMandatory = binding.spNonMandatory
sptransactionType = binding.sptransactionType
viewModel.bookName.observe(viewLifecycleOwner, Observer {
it?.let {
var string : String = ""
val listArr = arrayListOf<String>()
for (i in 0 .. it.size - 1){
string = it.get(i).bookName
listArr.add(string)
}
ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, listArr)
.also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spBookName.adapter = adapter
}
}
})
binding.etPeriode.setOnFocusChangeListener { v, hasFocus ->
if(v.hasFocus()){
val showDialog = ShowDatePickerDialog()
showDialog.show(requireFragmentManager(), "Show Date Picker")
}
}
sptransactionType.onItemSelectedListener = this
spBookName.onItemSelectedListener = this
return binding.root
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if(parent!!.id == R.id.spBookName) {
val selected = parent.selectedItem
viewModel.getBookType(selected.toString())
viewModel.bookType.observe(viewLifecycleOwner, Observer {
it?.let {
if (it.get(0).bookType == "Mandatory") {
ArrayAdapter.createFromResource(requireContext(), R.array.transactionType, android.R.layout.simple_spinner_item)
.also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
sptransactionType.adapter = adapter
}
isMandatory = true
} else if (it.get(0).bookType == "Non Mandatory") {
ArrayAdapter.createFromResource(requireContext(), R.array.transactionType, android.R.layout.simple_spinner_item)
.also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
sptransactionType.adapter = adapter
}
isMandatory = false
}
viewModel.resetBookType()
}
})
viewModel.getMandatoryName(selected.toString())
viewModel.getNonMandatoryName(selected.toString())
viewModel.mandatoryName.observe(viewLifecycleOwner, Observer {
it?.let {
var string : String = ""
val listArr = arrayListOf<String>()
for(i in 0 .. it.size -1){
string = it.get(i).name
listArr.add(string)
}
ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, listArr)
.also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spMandatory.adapter = adapter
}
}
})
viewModel.nonMandatoryName.observe(viewLifecycleOwner, Observer {
it?.let {
var string : String = ""
val listArr = arrayListOf<String>()
for(i in 0 .. it.size -1){
string = it.get(i).name
listArr.add(string)
}
ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, listArr)
.also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spNonMandatory.adapter = adapter
}
}
})
}else if (parent.id == R.id.sptransactionType){
val selected = parent.selectedItem
if(selected == "Plus" && isMandatory == true) {
binding.spMandatory.visibility = View.VISIBLE
binding.spNonMandatory.visibility = View.GONE
binding.etNameNonMandatory.visibility = View.GONE
binding.etNameNonMandatory.setText("")
}else if(selected == "Minus" && isMandatory == true){
binding.spMandatory.visibility = View.GONE
binding.spNonMandatory.visibility = View.VISIBLE
binding.etNameNonMandatory.visibility = View.GONE
binding.etNameNonMandatory.setText("")
}else if(selected == "Minus" && isMandatory == false){
binding.spMandatory.visibility = View.GONE
binding.spNonMandatory.visibility = View.GONE
binding.etNameNonMandatory.visibility = View.GONE
binding.etNameNonMandatory.setText("")
}else if(selected == "Plus" && isMandatory == false){
binding.spMandatory.visibility = View.GONE
binding.spNonMandatory.visibility = View.GONE
binding.etNameNonMandatory.visibility = View.VISIBLE
binding.etNameNonMandatory.setText("")
}else{
binding.spMandatory.visibility = View.GONE
binding.spNonMandatory.visibility = View.GONE
binding.etNameNonMandatory.visibility = View.GONE
binding.etNameNonMandatory.setText("")
}
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
class ShowDatePickerDialog() : DialogFragment(), DatePickerDialog.OnDateSetListener{
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val calendar = Calendar.getInstance()
val day = calendar.get(Calendar.DAY_OF_MONTH)
val month = calendar.get(Calendar.MONTH)
val year = calendar.get(Calendar.YEAR)
return DatePickerDialog(requireContext(), this, year, month, day)
}
override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int) {
val periode = requireActivity().findViewById<EditText>(R.id.etPeriode)
val spNonMandatory: Spinner = requireActivity().findViewById(R.id.spNonMandatory)
val spMandatory: Spinner = requireActivity().findViewById(R.id.spMandatory)
val spBookName: Spinner = requireActivity().findViewById(R.id.spBookName)
val viewModel = ViewModelProvider(this).get(AddReportViewModel::class.java)
val month = month + 1
val givenDate = "$dayOfMonth $month $year"
val givenDateFormat = SimpleDateFormat("dd MM yyyy")
val givenDateParse = givenDateFormat.parse(givenDate)
val resultGivenDate = givenDateParse.time
val givenMonth = "$month"
val givenMonthFormat = SimpleDateFormat("MM")
val givenMonthParse = givenMonthFormat.parse(givenMonth)
val resultGivenMonth = givenMonthParse.time
val monthNow = SimpleDateFormat("MM").format(Date())
val dateFormat = SimpleDateFormat("MM")
val toMil = dateFormat.parse(monthNow)
val result = toMil.time
if(resultGivenMonth == result){
viewModel.getMandatoryName(spBookName.selectedItem.toString())
viewModel.getNonMandatoryName(spBookName.selectedItem.toString())
viewModel.mandatoryName.observe(this, Observer {
it?.let {
var string : String = ""
val listArr = arrayListOf<String>()
for(i in 0 .. it.size -1){
string = it.get(i).name
listArr.add(string)
}
ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, listArr)
.also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spMandatory.adapter = adapter
adapter.notifyDataSetChanged()
}
}
})
viewModel.nonMandatoryName.observe(this, Observer {
it?.let {
var string : String = ""
val listArr = arrayListOf<String>()
for(i in 0 .. it.size -1){
string = it.get(i).name
listArr.add(string)
}
ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, listArr)
.also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spNonMandatory.adapter = adapter
adapter.notifyDataSetChanged()
}
}
})
}else if(resultGivenMonth != result){
viewModel.getAll(spBookName.selectedItem.toString())
viewModel.getAll.observe(this, Observer {
it?.let {
var string : String = ""
val listArr = arrayListOf<String>()
for(i in 0 .. it.size -1){
string = it.get(i).warga
listArr.add(string)
}
ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, listArr)
.also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spNonMandatory.adapter = adapter
adapter.notifyDataSetChanged()
}
ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, listArr)
.also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spMandatory.adapter = adapter
adapter.notifyDataSetChanged()
}
}
})
// viewModel.resetGetAll()
}
periode.setText(SimpleDateFormat("dd MMM yyyy").format(resultGivenDate))
}
}
And ViewModel code
class AddReportViewModel : ViewModel() {
private val _bookName = MutableLiveData<List<GetBookName>>()
val bookName: LiveData<List<GetBookName>>
get() = _bookName
private val _mandatoryName = MutableLiveData<List<GetMandatoryName>>()
val mandatoryName: LiveData<List<GetMandatoryName>>
get() = _mandatoryName
private val _nonMandatoryName = MutableLiveData<List<GetNonMandatoryName>>()
val nonMandatoryName: LiveData<List<GetNonMandatoryName>>
get() = _nonMandatoryName
private val _bookType = MutableLiveData<List<GetBookType>>()
val bookType: LiveData<List<GetBookType>>
get() = _bookType
private val _getAll = MutableLiveData<List<GetAll>>()
val getAll: LiveData<List<GetAll>>
get() = _getAll
init {
getBookName()
}
private fun getBookName() {
viewModelScope.launch {
try {
val bookName = Ebook.retrofitService.getBookName()
_bookName.value = bookName
} catch (exception: Exception) {
_bookName.value = ArrayList()
}
}
}
fun getBookType(bookName: String){
viewModelScope.launch {
try {
val bookType = Ebook.retrofitService.getBookType(bookName)
_bookType.value = bookType
}catch (exception: Exception){
_bookType.value = ArrayList()
}
}
}
fun getMandatoryName(bookName: String){
viewModelScope.launch {
try{
val mandatoryName = Ebook.retrofitService.getMandatory(bookName)
_mandatoryName.value = mandatoryName
}catch (exception: Exception){
Log.e("Exception Message", exception.message.toString())
Log.e("Exception Message", exception.cause.toString())
}
}
}
fun getNonMandatoryName(bookName: String){
viewModelScope.launch {
try{
val nonMandatoryName = Ebook.retrofitService.getNonMandatory(bookName)
_nonMandatoryName.value = nonMandatoryName
}catch (exception: Exception){
Log.e("Exception Message", exception.message.toString())
Log.e("Exception Message", exception.cause.toString())
}
}
}
fun resetBookType(){
_bookType.value = null
}
fun getAll(bookName: String){
viewModelScope.launch {
try{
val getAll = Ekomplek.retrofitService.getAllBayar(bookName)
_getAll.value = getAll
}catch (exception: Exception){
Log.e("Exception Message", exception.message.toString())
}
}
}
fun resetGetAll(){
_getAll.value = null
}
fun resetDonaturDebetKredit(){
_mandatoryName.value = null
_nonMandatoryName.value = null
}
}
As you can see, here on override fun onDateSet(view: DatePicker?, year: Int, month: Int, dayOfMonth: Int), I'm doing a check whether month now is the same as the month that is selected from date picker dialog. If it's the same, the spinner is filled with a filtered data from database that is matched with the month. And if month now is not the same as the month that is selected, the spinner is filled with all data from database.
Rarely, after I set the date, it shows "Job was cancelled" and the spinner is blank. Most of the time it works, though.
Is there anyway I fix this?
P.S. the same coroutine is also used in other fragments. Even in class AddReportFragment, that coroutine is also used. Everything was fine. No "Job was cancelled". I don't know why sometimes it doesn't work on DatePickerDialog class

Related

Change RecyclerView Layout (from Linear to Grid and reverse) when rotate device automatically

I have 4 different "Card List", "Card Magazine" , "Title" and "Grid" and view holders for each one to relate check my other question here.
now I am trying to change the layout automatically when the device rotates so when orientation is a portrait the layout be LinearLayout "Card layout" and when orientation changes to landscape the layout will be GridLayout, also I have a changeAndSaveLayout method to make the user choose between each layout from option menu
and I save the layout in ViewModel using DataStore and Flow,
The problem
When I rotate the device the RecyclerView and the list is gone and I see the empty screen,
and when I back to portrait the list is back it's back to default layout "cardLayout"
I tried multiple methods like notifyDataSetChanged after changing layout and handle changes in onConfigurationChanged methods but all these methods fails
DataStore class code saveRecyclerViewLayout and readRecyclerViewLayout
private val Context.dataStore by preferencesDataStore("user_preferences")
private const val TAG = "DataStoreRepository"
#ActivityRetainedScoped
class DataStoreRepository #Inject constructor(#ApplicationContext private val context: Context) {
suspend fun saveRecyclerViewLayout(
recyclerViewLayout: String,
) {
datastore.edit { preferences ->
preferences[PreferencesKeys.RECYCLER_VIEW_LAYOUT_KEY] = recyclerViewLayout
}
}
val readRecyclerViewLayout:
Flow<String> = datastore.data.catch { ex ->
if (ex is IOException) {
ex.message?.let { Log.e(TAG, it) }
emit(emptyPreferences())
} else {
throw ex
}
}.map { preferences ->
val recyclerViewLayout: String =
preferences[PreferencesKeys.RECYCLER_VIEW_LAYOUT_KEY] ?: "cardLayout"
recyclerViewLayout
}
}
I used it in ViewModel like the following
#HiltViewModel
class PostViewModel #Inject constructor(
private val mainRepository: MainRepository,
private val dataStoreRepository: DataStoreRepository,
application: Application
) :
AndroidViewModel(application) {
val recyclerViewLayout = dataStoreRepository.readRecyclerViewLayout.asLiveData()
fun saveRecyclerViewLayout(layout: String) {
viewModelScope.launch {
dataStoreRepository.saveRecyclerViewLayout(layout)
}
}
}
PostAdapter Class
class PostAdapter(
private val titleAndGridLayout: TitleAndGridLayout,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var viewType = 0
private val differCallback = object : DiffUtil.ItemCallback<Item>() {
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
return (oldItem.id == newItem.id)
}
override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
return (oldItem == newItem)
}
}
val differ = AsyncListDiffer(this, differCallback)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (this.viewType) {
CARD -> {
fromCardViewHolder(parent)
}
CARD_MAGAZINE -> {
fromCardMagazineViewHolder(parent)
}
TITLE -> {
fromTitleViewHolder(parent)
}
else -> {
fromGridViewHolder(parent)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item: Item = differ.currentList[position]
when (this.viewType) {
CARD -> if (holder is CardViewHolder) {
holder.bind(item)
}
CARD_MAGAZINE -> if (holder is CardMagazineViewHolder) {
holder.bind(item)
}
TITLE -> if (holder is TitleViewHolder) {
holder.bind(item)
if (position == itemCount - 1)
titleAndGridLayout.tellFragmentToGetItems()
}
GRID -> if (holder is GridViewHolder) {
holder.bind(item)
if (position == itemCount - 1)
titleAndGridLayout.tellFragmentToGetItems()
}
}
}
override fun getItemCount(): Int {
return differ.currentList.size
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
class CardViewHolder(private val cardLayoutBinding: CardLayoutBinding) :
RecyclerView.ViewHolder(cardLayoutBinding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
var date: Date? = Date()
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault())
cardLayoutBinding.postTitle.text = item.title
try {
Glide.with(cardLayoutBinding.root).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(cardLayoutBinding.postImage)
} catch (e: IndexOutOfBoundsException) {
cardLayoutBinding.postImage.setImageResource(R.drawable.no_image)
}
cardLayoutBinding.postDescription.text = document.text()
try {
date = format.parse(item.published)
} catch (e: ParseException) {
e.printStackTrace()
}
val prettyTime = PrettyTime()
cardLayoutBinding.postDate.text = prettyTime.format(date)
}
}
class CardMagazineViewHolder(private val cardMagazineBinding: CardMagazineBinding) :
RecyclerView.ViewHolder(cardMagazineBinding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
var date: Date? = Date()
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault())
cardMagazineBinding.postTitle.text = item.title
try {
Glide.with(itemView.context).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(cardMagazineBinding.postImage)
} catch (e: IndexOutOfBoundsException) {
cardMagazineBinding.postImage.setImageResource(R.drawable.no_image)
}
try {
date = format.parse(item.published)
} catch (e: ParseException) {
e.printStackTrace()
}
val prettyTime = PrettyTime()
cardMagazineBinding.postDate.text = prettyTime.format(date)
}
}
class TitleViewHolder(private val binding: TitleLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
binding.postTitle.text = item.title
try {
Glide.with(itemView.context).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(binding.postImage)
} catch (e: IndexOutOfBoundsException) {
binding.postImage.setImageResource(R.drawable.no_image)
}
}
}
class GridViewHolder constructor(private val binding: GridLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
binding.postTitle.text = item.title
try {
Glide.with(itemView.context).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(binding.postImage)
} catch (e: IndexOutOfBoundsException) {
binding.postImage.setImageResource(R.drawable.no_image)
}
}
}
companion object {
private const val CARD = 0
private const val CARD_MAGAZINE = 1
private const val TITLE = 2
private const val GRID = 3
private const val TAG = "POST_ADAPTER"
fun fromCardViewHolder(parent: ViewGroup): CardViewHolder {
val cardLayoutBinding: CardLayoutBinding =
CardLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return CardViewHolder(cardLayoutBinding)
}
fun fromCardMagazineViewHolder(parent: ViewGroup): CardMagazineViewHolder {
val cardMagazineBinding: CardMagazineBinding =
CardMagazineBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return CardMagazineViewHolder(cardMagazineBinding)
}
fun fromTitleViewHolder(parent: ViewGroup): TitleViewHolder {
val titleLayoutBinding: TitleLayoutBinding =
TitleLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return TitleViewHolder(titleLayoutBinding)
}
fun fromGridViewHolder(
parent: ViewGroup
): GridViewHolder {
val gridLayoutBinding: GridLayoutBinding =
GridLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return GridViewHolder(gridLayoutBinding)
}
}
init {
setHasStableIds(true)
}
}
and finally the HomeFragment
#AndroidEntryPoint
class HomeFragment : Fragment(), TitleAndGridLayout, MenuProvider {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
private var itemArrayList = arrayListOf<Item>()
private var searchItemList = arrayListOf<Item>()
private val postViewModel: PostViewModel by viewModels()
private var linearLayoutManager: LinearLayoutManager? = null
private val titleLayoutManager: GridLayoutManager by lazy {
GridLayoutManager(requireContext(), 2)
}
private val gridLayoutManager: GridLayoutManager by lazy {
GridLayoutManager(requireContext(), 3)
}
private var menuHost: MenuHost? = null
private lateinit var networkListener: NetworkListener
private lateinit var adapter:PostAdapter
private var isScrolling = false
var currentItems = 0
var totalItems: Int = 0
var scrollOutItems: Int = 0
private var postsAPiFlag = false
private val recyclerStateKey = "recycler_state"
private val mBundleRecyclerViewState by lazy { Bundle() }
private var keyword: String? = null
private var orientation: Int? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
postViewModel.finalURL.value = "$BASE_URL?key=$API_KEY"
networkListener = NetworkListener()
}
// This property is only valid between onCreateView and
// onDestroyView.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
adapter = PostAdapter(this)
orientation = resources.configuration.orientation
_binding = FragmentHomeBinding.inflate(inflater, container, false)
menuHost = requireActivity()
menuHost?.addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.CREATED)
postViewModel.recyclerViewLayout.observe(viewLifecycleOwner) { layout ->
linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
Log.w(TAG, "getSavedLayout called")
Log.w(TAG, "getSavedLayout: orientation ${orientation.toString()}", )
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
when (layout) {
"cardLayout" -> {
//
adapter.viewType = 0
binding.apply {
homeRecyclerView.layoutManager = linearLayoutManager
homeRecyclerView.adapter = adapter
}
}
"cardMagazineLayout" -> {
// binding.loadMoreBtn.visibility = View.VISIBLE
binding.homeRecyclerView.layoutManager = linearLayoutManager
adapter.viewType = 1
binding.homeRecyclerView.adapter = adapter
}
}
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
when (layout) {
"titleLayout" -> {
// binding.loadMoreBtn.visibility = View.GONE
binding.homeRecyclerView.layoutManager = titleLayoutManager
adapter.viewType = 2
binding.homeRecyclerView.adapter = adapter
}
"gridLayout" -> {
binding.homeRecyclerView.layoutManager = gridLayoutManager
adapter.viewType = 3
binding.homeRecyclerView.adapter = adapter
}
}
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return if (menuItem.itemId == R.id.change_layout) {
changeAndSaveLayout()
true
} else false
}
private fun changeAndSaveLayout() {
// Log.w(TAG, "changeAndSaveLayout: called")
val builder = AlertDialog.Builder(requireContext())
builder.setTitle(getString(R.string.choose_layout))
val recyclerViewPortraitLayout =
resources.getStringArray(R.array.RecyclerViewPortraitLayout)
val recyclerViewLandscapeLayout =
resources.getStringArray(R.array.RecyclerViewLandscapeLayout)
// SharedPreferences.Editor editor = sharedPreferences.edit();
Log.d(TAG, "changeAndSaveLayout: ${orientation.toString()}")
if (orientation == 1) {
builder.setItems(
recyclerViewPortraitLayout
) { _: DialogInterface?, index: Int ->
try {
when (index) {
0 -> {
adapter.viewType = 0
binding.homeRecyclerView.layoutManager = linearLayoutManager
binding.homeRecyclerView.adapter = adapter
postViewModel.saveRecyclerViewLayout("cardLayout")
}
1 -> {
adapter.viewType = 1
binding.homeRecyclerView.layoutManager = linearLayoutManager
binding.homeRecyclerView.adapter = adapter
postViewModel.saveRecyclerViewLayout("cardMagazineLayout")
}
}
} catch (e: Exception) {
Log.e(TAG, "changeAndSaveLayout: " + e.message)
Log.e(TAG, "changeAndSaveLayout: " + e.cause)
}
}
} else if (orientation == 2) {
builder.setItems(
recyclerViewLandscapeLayout
) { _: DialogInterface?, index: Int ->
try {
when (index) {
2 -> {
adapter.viewType = 2
binding.homeRecyclerView.layoutManager = titleLayoutManager
binding.homeRecyclerView.adapter = adapter
postViewModel.saveRecyclerViewLayout("titleLayout")
}
3 -> {
adapter.viewType = 3
binding.homeRecyclerView.layoutManager = gridLayoutManager
binding.homeRecyclerView.adapter = adapter
postViewModel.saveRecyclerViewLayout("gridLayout")
}
}
} catch (e: Exception) {
Log.e(TAG, "changeAndSaveLayout: " + e.message)
Log.e(TAG, "changeAndSaveLayout: " + e.cause)
}
}
}
val alertDialog = builder.create()
alertDialog.show()
}
}
GIF showing the problem
since long time I was looking for a soultion and i found it and added it to my old project, i was use shared prefernces but in your case i mean data store it will work normally
you need to create two arrays for each orintation
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="recyclerViewPortraitList">
<item>Card List</item>
<item>Card Magazine</item>
<item>Title</item>
</array>
<array name="recyclerViewLandscapeList">
<item>Grid with 3 Span</item>
<item>Grid with 4 Span</item>
</array>
</resources>
in your data store will be like the following
private object PreferencesKeys {
var RECYCLER_VIEW_PORTRAIT_LAYOUT_KEY = stringPreferencesKey("recyclerViewPortraitLayout")
var RECYCLER_VIEW_LANDSCAPE_LAYOUT_KEY = stringPreferencesKey("recyclerViewLandscapeLayout")
}
suspend fun saveRecyclerViewPortraitLayout(
recyclerViewLayout: String,
) {
datastore.edit { preferences ->
preferences[PreferencesKeys.RECYCLER_VIEW_PORTRAIT_LAYOUT_KEY] = recyclerViewLayout
}
}
suspend fun saveRecyclerViewLandscapeLayout(recyclerViewLayout: String) {
datastore.edit { preferences ->
preferences[PreferencesKeys.RECYCLER_VIEW_LANDSCAPE_LAYOUT_KEY] = recyclerViewLayout
}
}
val readRecyclerViewPortraitLayout:
Flow<String> = datastore.data.catch { ex ->
if (ex is IOException) {
ex.message?.let { Log.e(TAG, it) }
emit(emptyPreferences())
} else {
throw ex
}
}.map { preferences ->
val recyclerViewLayout: String =
preferences[PreferencesKeys.RECYCLER_VIEW_PORTRAIT_LAYOUT_KEY] ?: "cardLayout"
recyclerViewLayout
}
val readRecyclerViewLandscpaeLayout:
Flow<String> = datastore.data.catch { ex ->
if (ex is IOException) {
ex.message?.let { Log.e(TAG, it) }
emit(emptyPreferences())
} else {
throw ex
}
}.map { preferences ->
val recyclerViewLayout: String =
preferences[PreferencesKeys.RECYCLER_VIEW_LANDSCAPE_LAYOUT_KEY] ?: "gridWith3Span"
recyclerViewLayout
}
in the viewModel
val readRecyclerViewPortraitLayout =
dataStoreRepository.readRecyclerViewPortraitLayout.asLiveData()
val readRecyclerViewLandscapeLayout =
dataStoreRepository.readRecyclerViewLandscpaeLayout.asLiveData()
fun saveRecyclerViewPortraitLayout(layout: String) {
viewModelScope.launch {
dataStoreRepository.saveRecyclerViewPortraitLayout(layout)
}
}
fun saveRecyclerViewLandscapeLayout(layout: String) {
viewModelScope.launch {
dataStoreRepository.saveRecyclerViewLandscapeLayout(layout)
}
}
in adapter change constants
companion object {
private const val CARD = 0
private const val CARD_MAGAZINE = 1
private const val TITLE = 2
private const val GRID_WITH_3_SPAN = 3
private const val GRID_WITH_4_SPAN = 4
}
5.and finally in the fragment or activity you can use it like the following
private fun setUpRecyclerViewLayout() {
if (requireActivity().resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
postViewModel.readRecyclerViewPortraitLayout.observe(viewLifecycleOwner) { layout ->
recyclerViewLayout = layout
when (layout) {
"cardLayout" -> {
adapter.viewType = 0
binding.apply {
homeRecyclerView.layoutManager = linearLayoutManager
homeRecyclerView.adapter = adapter
homeRecyclerView.setHasFixedSize(true)
}
}
"cardMagazineLayout" -> {
binding.homeRecyclerView.layoutManager = linearLayoutManager
adapter.viewType = 1
binding.homeRecyclerView.adapter = adapter
}
"titleLayout" -> {
binding.homeRecyclerView.layoutManager = titleLayoutManager
adapter.viewType = 2
binding.homeRecyclerView.adapter = adapter
}
}
}
} else {
postViewModel.readRecyclerViewLandscapeLayout.observe(viewLifecycleOwner) { layout ->
recyclerViewLayout = layout
when (layout) {
"gridWith3Span" -> {
binding.homeRecyclerView.layoutManager = gridWith3SpanLayoutManager
adapter.viewType = 3
binding.homeRecyclerView.adapter = adapter
}
"gridWith4Span" -> {
binding.homeRecyclerView.layoutManager = gridWith4SpanLayoutManager
adapter.viewType = 4
binding.homeRecyclerView.adapter = adapter
}
}
}
}
}
private fun changeAndSaveLayout() {
val builder = AlertDialog.Builder(requireContext())
builder.setTitle(getString(R.string.choose_layout))
// SharedPreferences.Editor editor = sharedPreferences.edit();
if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
builder.setItems(
resources.getStringArray(R.array.recyclerViewPortraitList)
) { _: DialogInterface?, index: Int ->
try {
when (index) {
0 -> {
adapter.viewType = 0
binding.homeRecyclerView.layoutManager = linearLayoutManager
binding.homeRecyclerView.adapter = adapter
postViewModel.saveRecyclerViewPortraitLayout("cardLayout")
}
1 -> {
adapter.viewType = 1
binding.homeRecyclerView.layoutManager = linearLayoutManager
binding.homeRecyclerView.adapter = adapter
postViewModel.saveRecyclerViewPortraitLayout("cardMagazineLayout")
}
2 -> {
adapter.viewType = 2
binding.homeRecyclerView.layoutManager = titleLayoutManager
binding.homeRecyclerView.adapter = adapter
postViewModel.saveRecyclerViewPortraitLayout("titleLayout")
}
else -> {
throw Exception("Unknown layout")
}
}
} catch (e: Exception) {
Log.e(TAG, "changeAndSaveLayout: " + e.message)
Log.e(TAG, "changeAndSaveLayout: " + e.cause)
}
}
} else {
builder.setItems(
resources.getStringArray(R.array.recyclerViewLandscapeList)
) { _: DialogInterface?, index: Int ->
try {
when (index) {
0 -> {
adapter.viewType = 3
binding.homeRecyclerView.layoutManager = gridWith3SpanLayoutManager
binding.homeRecyclerView.adapter = adapter
postViewModel.saveRecyclerViewLandscapeLayout("gridWith3Span")
}
1 -> {
adapter.viewType = 4
binding.homeRecyclerView.layoutManager = gridWith4SpanLayoutManager
binding.homeRecyclerView.adapter = adapter
postViewModel.saveRecyclerViewLandscapeLayout("gridWith4Span")
}
else -> {
throw Exception("Unknown layout")
}
}
} catch (e: Exception) {
Log.e(TAG, "changeAndSaveLayout: " + e.message)
Log.e(TAG, "changeAndSaveLayout: " + e.cause)
}
}
}
val alertDialog = builder.create()
alertDialog.show()
}
if you setting configuration changes in manifest like that android:configChanges="orientation|screenSize"you will need to call the setUpRecyclerViewLayout() from onConfigurationChanged
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
setUpRecyclerViewLayout()
}

How to get child Count in Header in expandable list view Kotlin

enter image description here
How get the child count same as shown in the picture in the header.
API RESPONSE: -
data class ToDoListResponse(
val message: String = "",
val success: Int = 0,
val todays_task_list: MutableList<TodaysTask>,
val todays_task_total: Int = 0,
val tomorows_task_list: MutableList<TodaysTask>,
val tomorrow_task_total: Int = 0,
val upcomming_task_list: MutableList<TodaysTask>,
val upcomming_task_total: Int = 0)
{
data class TodaysTask(
val created_at: String = "",
val description: String = "",
val due_date: String= "",
val is_completed: String= "",
val is_pinned_task: String= "",
val remind_me: String= "",
val task_id: String= "",
var title: String= "",
val updated_at: String= "",
val user_id: String= ""
)
ADPATER: -
package com.coronation.jackpotplus.adapter
import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.content.Context
import android.graphics.Typeface
import android.os.Build
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.annotation.RequiresApi
import com.coronation.jackpotplus.R
import com.coronation.jackpotplus.model.CommonResponse
import com.coronation.jackpotplus.model.ToDoListResponse
import com.coronation.jackpotplus.network.ApiClient
import com.coronation.jackpotplus.network.ApiService
import com.coronation.jackpotplus.view.ToDoListActivity
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.textfield.TextInputEditText
import org.jetbrains.anko.layoutInflater
import retrofit2.Call
import retrofit2.Response
import java.text.SimpleDateFormat
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.*
#Suppress("NAME_SHADOWING", "DEPRECATED_IDENTITY_EQUALS")
class CustomExpandableListAdapter(
context: Context,
expandableListTitle: ArrayList<String>,
expandableListDetail: HashMap<String,
MutableList<ToDoListResponse.TodaysTask>>)
: BaseExpandableListAdapter()
{
private val context: Context
private val expandableListTitle: ArrayList<String>
private val expandableListDetail: HashMap<String, MutableList<ToDoListResponse.TodaysTask>>
private lateinit var edtTitle: TextInputEditText
private lateinit var edtDes: TextInputEditText
private lateinit var dueDate: TextView
private lateinit var txtreminddate: TextView
private lateinit var createAt: String
private lateinit var taskId: String
private var ChildCount: String? = null
private lateinit var dialog: BottomSheetDialog
override fun getChild(listPosition: Int, expandedListPosition: Int): Any
{
return expandableListDetail[expandableListTitle[listPosition]]?.get(expandedListPosition)!!
}
override fun getChildId(listPosition: Int, expandedListPosition: Int): Long
{
return expandedListPosition.toLong()
}
#RequiresApi(Build.VERSION_CODES.O)
#SuppressLint("InflateParams", "SimpleDateFormat")
override fun getChildView(listPosition: Int, expandedListPosition: Int, isLastChild: Boolean, convertView: View?, parent: ViewGroup?): View
{
var convertView = convertView
if (convertView == null)
{
val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
convertView = layoutInflater.inflate(R.layout.list_item, null)
}
val expandedListTextView = convertView?.findViewById(R.id.expandedListItem) as TextView
val expandedItem = convertView.findViewById(R.id.expandedItem) as LinearLayout
val txtDelete = convertView.findViewById(R.id.delete) as FrameLayout
val unpin = convertView.findViewById(R.id.unpin) as FrameLayout
val pin = convertView.findViewById(R.id.pin) as FrameLayout
val complete = convertView.findViewById(R.id.complete) as FrameLayout
val mainPin = convertView.findViewById(R.id.mainPin) as ImageView
val getset = getChild(listPosition, expandedListPosition) as ToDoListResponse.TodaysTask
expandedListTextView.text = getset.title
createAt = getset.created_at
unpin.visibility = View.GONE
if (getset.is_pinned_task == "1")
{
mainPin.visibility = View.VISIBLE
unpin.visibility = View.VISIBLE
pin.visibility = View.GONE
}
else
{
mainPin.visibility = View.GONE
pin.visibility = View.VISIBLE
}
unpin.setOnClickListener {
pinnedtask(getset.task_id)
}
pin.setOnClickListener {
pinnedtask(getset.task_id)
}
complete.setOnClickListener {
markascomplete(getset.task_id)
}
txtDelete.setOnClickListener {
deleteTask(getset.task_id)
}
expandedItem.setOnClickListener {
openTaskDetails()
taskId = getset.task_id
edtTitle.setText(getset.title)
edtDes.setText(getset.description)
dueDate.visibility = View.VISIBLE
txtreminddate.visibility = View.VISIBLE
val dueDate1 = getset.due_date
val sdf = SimpleDateFormat("yyyy-MM-dd")
val date = Date(dueDate1.toLong() * 1000)
dueDate.text = sdf.format(date)
val remindDate = getset.remind_me
val sdf1 = SimpleDateFormat("yyyy-MM-dd")
val date1 = Date(remindDate.toLong() * 1000)
txtreminddate.text = sdf1.format(date1)
}
return convertView
}
private fun pinnedtask(taskId: String)
{
val retIn = ApiClient.client!!.create(ApiService::class.java)
retIn.pintask(taskId).enqueue(object : retrofit2.Callback<CommonResponse> {
override fun onResponse(call: Call<CommonResponse>, response: Response<CommonResponse>) {
showToast(response.body()?.message)
notifyDataSetChanged()
if (context is ToDoListActivity) {
context.tasklistAPI("1")
}
}
override fun onFailure(call: Call<CommonResponse>, t: Throwable) {
showToast("Shomwthing wents wrong")
}
})
}
private fun markascomplete(taskId: String)
{
val retIn = ApiClient.client!!.create(ApiService::class.java)
retIn.markcomplete(taskId).enqueue(object : retrofit2.Callback<CommonResponse> {
override fun onResponse(call: Call<CommonResponse>, response: Response<CommonResponse>) {
showToast(response.body()?.message)
notifyDataSetChanged()
if (context is ToDoListActivity) {
context.tasklistAPI("1")
}
}
override fun onFailure(call: Call<CommonResponse>, t: Throwable) {
showToast("Shomwthing wents wrong")
}
})
}
private fun deleteTask(taskId: String)
{
val retIn = ApiClient.client!!.create(ApiService::class.java)
retIn.deteletask(taskId).enqueue(object : retrofit2.Callback<CommonResponse> {
override fun onResponse(call: Call<CommonResponse>, response: Response<CommonResponse>) {
showToast(response.body()?.message)
notifyDataSetChanged()
if (context is ToDoListActivity) {
context.tasklistAPI("1")
}
}
override fun onFailure(call: Call<CommonResponse>, t: Throwable) {
showToast("Shomwthing wents wrong")
}
})
}
#RequiresApi(Build.VERSION_CODES.O)
#SuppressLint("InflateParams")
private fun openTaskDetails()
{
try
{
val modalbottomsheet = context.layoutInflater.inflate(R.layout.custom_view_todo, null)
dialog = BottomSheetDialog(context, R.style.DialogStyle)
dialog.setContentView(modalbottomsheet)
edtTitle = dialog.findViewById(R.id.edtTitle)!!
edtDes = dialog.findViewById(R.id.edtDes)!!
dueDate = dialog.findViewById(R.id.dueDate)!!
txtreminddate = dialog.findViewById(R.id.txtreminddate)!!
val imgDatePicker = dialog.findViewById<ImageView>(R.id.imgDatePicker)
val imgDatePickerReminder = dialog.findViewById<ImageView>(R.id.imgDatePickerReminder)
val button = dialog.findViewById<Button>(R.id.Button)
imgDatePicker?.setOnClickListener {
showDatePicker(context)
}
imgDatePickerReminder?.setOnClickListener {
showDatePickerForRemind(context)
}
button?.setOnClickListener {
updateTaskAPI()
}
dialog.show()
}
catch (e: Exception)
{
e.printStackTrace()
}
}
#RequiresApi(Build.VERSION_CODES.O)
private fun updateTaskAPI()
{
val l = LocalDate.parse(dueDate.text, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
val dueDateTimestamp = l.atStartOfDay(ZoneId.systemDefault()).toInstant().epochSecond
Toast.makeText(context, dueDateTimestamp.toString(), Toast.LENGTH_SHORT).show()
val l1 = LocalDate.parse(txtreminddate.text, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
val remindMeTimestamp = l1.atStartOfDay(ZoneId.systemDefault()).toInstant().epochSecond
Toast.makeText(context, remindMeTimestamp.toString(), Toast.LENGTH_SHORT).show()
val retIn = ApiClient.client!!.create(ApiService::class.java)
retIn.taskupdate(
createAt,
edtDes.text?.trim().toString(),
dueDateTimestamp.toString(),
remindMeTimestamp.toString(),
taskId, edtTitle.text?.trim().toString(),
"1").enqueue(object : retrofit2.Callback<CommonResponse>
{
override fun onResponse(call: Call<CommonResponse>, response: Response<CommonResponse>)
{
if (response.body()!!.success == 1)
{
dialog.dismiss()
showToast(response.body()?.message)
}
}
override fun onFailure(call: Call<CommonResponse>, t: Throwable)
{
showToast("Something not right, Please try again.")
}
}
)
}
#SuppressLint("InflateParams")
fun showToast(msg: String?)
{
try
{
val inflater = context.layoutInflater
val layout = inflater.inflate(R.layout.custom_toast, null)
val text = layout.findViewById<View>(R.id.text) as TextView
text.text = msg
val toast = Toast(context)
toast.duration = Toast.LENGTH_SHORT
toast.view = layout
if (toast.view.isShown)
{
toast.cancel()
}
else
{
toast.show()
}
}
catch (e: Exception)
{
e.printStackTrace()
}
}
private fun showDatePickerForRemind(context: Context)
{
val cal = Calendar.getInstance()
val dateSetListener =
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
cal.set(Calendar.YEAR, year)
cal.set(Calendar.MONTH, monthOfYear)
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth)
val myFormat = "yyyy-MM-dd" // mention the format you need
val sdf = SimpleDateFormat(myFormat, Locale.US)
txtreminddate.visibility = View.VISIBLE
txtreminddate.text = sdf.format(cal.time)
}
var datepicker: DatePickerDialog? = null
if (txtreminddate.text.isNotBlank())
{
try
{
val date: String = txtreminddate.text.trim().toString()
val datearr = date.split("-").toTypedArray()
val yearint = datearr[2].toInt()
val monthint = datearr[1].toInt()
val dateint = datearr[0].toInt()
datepicker = DatePickerDialog(
context,
dateSetListener,
dateint,
monthint - 1,
yearint
)
}
catch (e: Exception)
{
e.printStackTrace()
}
}
else
{
try
{
val calendar = Calendar.getInstance()
val dd = calendar[Calendar.DAY_OF_MONTH]
val mm = calendar[Calendar.MONTH]
val yy = calendar[Calendar.YEAR]
datepicker = DatePickerDialog(context, dateSetListener, dd, mm, yy)
}
catch (e: Exception)
{
e.printStackTrace()
}
}
datepicker!!.show()
}
private fun showDatePicker(context: Context)
{
val cal = Calendar.getInstance()
val dateSetListener =
DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
cal.set(Calendar.YEAR, year)
cal.set(Calendar.MONTH, monthOfYear)
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth)
val myFormat = "yyyy-MM-dd" // mention the format you need
val sdf = SimpleDateFormat(myFormat, Locale.US)
dueDate.visibility = View.VISIBLE
dueDate.text = sdf.format(cal.time)
}
var datepicker: DatePickerDialog? = null
if (dueDate.text.isNotBlank())
{
try
{
val date: String = dueDate.text.toString()
val datearr = date.split("-").toTypedArray()
val dateint = datearr[0].toInt()
val monthint = datearr[1].toInt()
val yearint = datearr[2].toInt()
datepicker = DatePickerDialog(
context,
dateSetListener,
yearint,
monthint - 1,
dateint
)
}
catch (e: java.lang.Exception)
{
e.printStackTrace()
}
}
else
{
try
{
val calendar = Calendar.getInstance()
val yy = calendar[Calendar.YEAR]
val mm = calendar[Calendar.MONTH]
val dd = calendar[Calendar.DAY_OF_MONTH]
datepicker = DatePickerDialog(context, dateSetListener, yy, mm, dd)
}
catch (e: java.lang.Exception)
{
e.printStackTrace()
}
}
datepicker!!.show()
}
override fun getChildrenCount(listPosition: Int): Int
{
ChildCount = expandableListDetail[expandableListTitle[listPosition]]?.size.toString()
Log.e("TAG",ChildCount)
return expandableListDetail[expandableListTitle[listPosition]]?.size!!
}
override fun getGroup(listPosition: Int): Any
{
return expandableListTitle[listPosition]
}
override fun getGroupCount(): Int
{
return expandableListTitle.size
}
override fun getGroupId(listPosition: Int): Long
{
return listPosition.toLong()
}
#SuppressLint("InflateParams")
override fun getGroupView(listPosition: Int, isExpanded: Boolean, convertView: View?, parent: ViewGroup?): View
{
var convertView: View? = convertView
val listTitle = getGroup(listPosition) as String
if (convertView == null)
{
val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
convertView = layoutInflater.inflate(R.layout.list_group, null)
}
val listTitleTextView = convertView?.findViewById(R.id.listTitle) as TextView
val listCount = convertView.findViewById(R.id.listCount) as TextView
val arrowicon = convertView.findViewById(R.id.arrowicon) as ImageView
listTitleTextView.setTypeface(null, Typeface.BOLD)
listTitleTextView.text = listTitle
listCount.text = "($ChildCount)"
if (getChildrenCount(listPosition) === 0)
{
arrowicon.visibility = View.INVISIBLE
}
else
{
arrowicon.visibility = View.VISIBLE
arrowicon.setImageResource(if (isExpanded) R.drawable.ic_arrow_up else R.drawable.ic_arrow_down)
}
return convertView
}
override fun hasStableIds(): Boolean
{
return false
}
override fun isChildSelectable(listPosition: Int, expandedListPosition: Int): Boolean
{
return true
}
init
{
this.context = context
this.expandableListTitle = expandableListTitle
this.expandableListDetail = expandableListDetail
}
}
ACTIVITY: -
fun tasklistAPI(userId: String)
{
if (isOnline())
{
llNoInternet.gone()
llLoading.visible()
apiService.todotasklist(userId).enqueue(object :
Callback<ToDoListResponse>
{
override fun onFailure(call: Call<ToDoListResponse>, t: Throwable)
{
llLoading.gone()
}
override fun onResponse(call: Call<ToDoListResponse>, response: Response<ToDoListResponse>)
{
if (response.isSuccessful)
{
if (response.body()!!.success == 1)
{
try
{
llLoading.gone()
expandableListDetail = HashMap()
val todaytask = response.body()!!.todays_task_list
expandableListTitle = ArrayList()
todaystitle = ArrayList()
todayslist = ArrayList()
Log.e("<>list", todaystitle.toString())
for (i in todaytask.indices)
{
todaystitle.add(todaytask[i])
}
val tommrowtask = response.body()!!.tomorows_task_list
val tommorowtitle: MutableList<ToDoListResponse.TodaysTask> = ArrayList()
for (i in tommrowtask.indices)
{
tommorowtitle.add(tommrowtask[i])
}
val upcomingtask = response.body()!!.upcomming_task_list
val upcomingtitle: MutableList<ToDoListResponse.TodaysTask> = ArrayList()
for (i in upcomingtask.indices)
{
upcomingtitle.add(upcomingtask[i])
}
expandableListTitle.add("Today's Task")
expandableListTitle.add("Tommorow's Task")
expandableListTitle.add("Upcoming Task")
expandableListDetail.put(expandableListTitle[0], todaystitle)
expandableListDetail.put(expandableListTitle[1], tommrowtask)
expandableListDetail.put(expandableListTitle[2], upcomingtask)
Log.e("task", expandableListTitle.get(0) + " = " + expandableListDetail.put(expandableListTitle.get(0), todaystitle))
Log.e("task", expandableListTitle.get(1) + " = " + expandableListDetail.put(expandableListTitle.get(1), tommrowtask))
Log.e("task", expandableListTitle.get(2) + " = " + expandableListDetail.put(expandableListTitle.get(2), upcomingtask))
expandableListAdapter = CustomExpandableListAdapter(activity, expandableListTitle, expandableListDetail)
expandableListView!!.setAdapter(expandableListAdapter)
}
catch (e: Exception)
{
e.printStackTrace()
}
}
}
else
{
llLoading.gone()
}
}
})
}
else
{
llNoInternet.visible()
}
}
Here is the code that show that how can i add add data into hashmap and the expandable list view.
as u see there is an hashmap for expandable list view and and in the adaoter i put the title from api respose but the header for listview parent is static and the data in the child re dynamic so you can see the diffrance
You've added a lot of code but I think this is the only one which is important for your question :-
expandableListTitle.add("Today's Task")
expandableListTitle.add("Tommorow's Task")
expandableListTitle.add("Upcoming Task")
expandableListDetail.put(expandableListTitle[0], todaystitle)
expandableListDetail.put(expandableListTitle[1], tommrowtask)
expandableListDetail.put(expandableListTitle[2], upcomingtask)
Log.e("task", expandableListTitle.get(0) + " = " + expandableListDetail.put(expandableListTitle.get(0), todaystitle))
Log.e("task", expandableListTitle.get(1) + " = " + expandableListDetail.put(expandableListTitle.get(1), tommrowtask))
Log.e("task", expandableListTitle.get(2) + " = " + expandableListDetail.put(expandableListTitle.get(2), upcomingtask))
expandableListAdapter = CustomExpandableListAdapter(activity, expandableListTitle, expandableListDetail)
expandableListView!!.setAdapter(expandableListAdapter)
So As much as I have understood from the image you have added in the link what you need is you need to show the number of elements present in the child in your header right.
What you can do is simply add the total number of elements present in your list like this :-
expandableListTitle.add("Today's Task (${todaystitle.count()})") //.count() gives the total number of elements present in list
It will add title as Today's Task (10) \\ here 10 would be the number of elements present in your list. Now you can simply format in any way you want

LongClickListerner Not Responding (Kotlin/RecyclerView

I have implemented a onClicklistener in my recycleview which works but the onLongClickListener is not responding? I have added the code in the Adapter, which is being called from a fragment. The onClickListener works well. Whn I click onLongClick -> it needs to display an AlertDialog, but in this case on running the app, there are no errors and no longclick too. This is in Kotlin, what am I doing wrong - any thoughts please?
class UserMsgAdapter(
mContext: Context,
userMsgList: List<MessageList>,
mUsers: List<Users>,
isChatCheck: Boolean
): RecyclerView.Adapter<UserMsgAdapter.ViewHolder?>()
{
private val mContext: Context
private val userMsgList: List<MessageList>
private val mUsers:List<Users>
private var isChatCheck: Boolean
init{
this.mUsers = mUsers
this.userMsgList = userMsgList
this.mContext = mContext
this.isChatCheck = isChatCheck
}
var lastMsg: String = ""
var timeMsg: Long = 0
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val view: View = LayoutInflater.from(mContext).inflate(
R.layout.user_search_item_layout,
viewGroup,
false
)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val message: MessageList? = userMsgList[position]
//Accessing the receiverId from the ChatList
val receiverId = message!!.getId()
val ref = FirebaseDatabase.getInstance().reference.child("Users").child(receiverId!!)
ref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
val user = p0.getValue(Users::class.java)
holder.userNameText.text = user!!.getUserName()
Picasso.get().load(user.getProfile())
.placeholder(R.drawable.ic_baseline_whatshot_24).fit().centerCrop().into(
holder.profileImageView
)
//Showcasing Last Message
if (isChatCheck) {
retrieveLastMessage(
user.getUID(),
holder.lastMessagetxt,
holder.timestamptxt,
holder.new_message_count
)
} else {
holder.lastMessagetxt.visibility = View.GONE
}
//Calling the Message Activity
holder.itemView.setOnClickListener {
val intent = Intent(mContext, MessageActivity::class.java)
intent.putExtra("visit_id", user.getUID())
mContext.startActivity(intent)
}
//Menu on Search Fragment
holder.itemView.setOnLongClickListener {
val options = arrayOf<CharSequence>(
"Delete Chat",
"Visit Profile"
)
val builder: AlertDialog.Builder = AlertDialog.Builder(mContext)
builder.setTitle("What would you like to do?")
builder.setItems(options, DialogInterface.OnClickListener { _, which ->
if (which == 0) {
val intent = Intent(mContext, MessageActivity::class.java)
intent.putExtra("visit_id", user.getUID())
mContext.startActivity(intent)
}
if (which == 1) {
val intent = Intent(mContext, VisitUserProfile::class.java)
intent.putExtra("visit_id", user.getUID())
mContext.startActivity(intent)
}
})
builder.show()
true
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
override fun getItemCount(): Int {
return userMsgList.size
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
{
var userNameText: TextView = itemView.findViewById(R.id.username)
var profileImageView: ShapeableImageView = itemView.findViewById(R.id.profile_image)
var lastMessagetxt: TextView = itemView.findViewById(R.id.last_message)
var timestamptxt: TextView = itemView.findViewById(R.id.time_stamp)
var new_message_count: TextView = itemView.findViewById(R.id.new_message_count)
}
//print last message and timestamp
private fun retrieveLastMessage(
messageUserId: String?, lastMessagetxt: TextView,
timestamptxt: TextView, new_message_count: TextView,
)
{
lastMsg = "defaultMsg"
timeMsg = 0
var countUnreadMessages = 0
val firebaseUser = FirebaseAuth.getInstance().currentUser
val reference = FirebaseDatabase.getInstance().reference.child("Chats")
reference.addValueEventListener(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
for (dataSnapshot in p0.children) {
val message: Message? = dataSnapshot.getValue(Message::class.java)
if (firebaseUser != null && message != null) {
if (message.getReceiver() == firebaseUser.uid && message.getSender() == messageUserId ||
message.getReceiver() == messageUserId && message.getSender() == firebaseUser.uid
) {
lastMsg = message.getMessage()!!
timeMsg = message.getTimestamp()!!
}
if (message.getReceiver() == firebaseUser.uid && message.getSender() == messageUserId && !message.isIsSeen()) {
countUnreadMessages += 1
}
}
}
when (lastMsg) {
"defaultMsg" -> lastMessagetxt.text = ""
"Shared an image with you." -> lastMessagetxt.text = "Shared an image"
else -> lastMessagetxt.text = lastMsg
}
//Display count of unread messages
if (countUnreadMessages != 0) {
new_message_count.text = countUnreadMessages.toString()
new_message_count.setBackgroundResource(R.drawable.new_chat_bg)
} else {
new_message_count.setBackgroundResource(0)
new_message_count.text = ""
}
countUnreadMessages = 0
//Display formatted date and time
val time = dateFormatter(timeMsg)
when (time) {
"01-01-1970" -> timestamptxt.text = ""
else -> timestamptxt.text = time
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
}

RecyclerView not binding data notifyDataSetChanged not working when filter data

I have two screens first one has recycler view list of data and searchView above it that's filter data in this recycler, the view Model code of the first fragment
class MscInspectionViewModel(val activity: LaunchActivity, val mRootView: MscInspectFragment) :
BaseViewModel(),
SwipeRefreshLayout.OnRefreshListener {
val toolBarTitle: MutableLiveData<String> = MutableLiveData()
private val getDataError = MutableLiveData<Boolean>()
var listType = MutableLiveData<Int>()
val hint = MutableLiveData<String>()
private var isRefreshing: Boolean = false
private var mSharedPreferences: SharedPreferences? = null
val dataListAdapter = ContainersUnderCheckAdapter(activity)
val backClickListener = View.OnClickListener { activity.supportFragmentManager.popBackStack() }
val filterDataByTab = object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabSelected(tab: TabLayout.Tab?) {
when (tab!!.text) {
activity.resources.getString(R.string.cidPending) -> {
listType.value = 0
getPendingData()
}
activity.resources.getString(R.string.cidDone) -> {
listType.value = 1
getDoneData()
}
}
}
}
val filterData = object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
if (query.length > 2) {
val mQuery = Utility(activity).switchArabicNumerals(query)
dataListAdapter.getFilter(3, listType.value!!).filter(mQuery)
} else {
errorMessage.value = activity.resources.getString(R.string.addCorrectNumber)
}
return true
}
override fun onQueryTextChange(newText: String): Boolean {
if (newText.length > 2) {
val mQuery = Utility(activity).switchArabicNumerals(newText)
dataListAdapter.getFilter(3, listType.value!!).filter(mQuery)
}
return false;
}
}
val closeImgListener = View.OnClickListener {
mRootView.svSearchMSC.setQuery("", true)
if (listType.value == 1) {
dataListAdapter.getFilter(1, listType.value!!).filter("ANY")
} else if (listType.value == 0) {
dataListAdapter.getFilter(2, listType.value!!).filter("PENDING")
}
}
init {
listType.value = 0
mSharedPreferences = getDefaultSharedPreferences(activity.applicationContext)
toolBarTitle.value = activity.resources.getString(R.string.mscInspectTitle)
hint.value = activity.resources.getString(R.string.msc_search)
getData()
}
fun getData() {
onRetrievePostListStart()
subscription = apiAccount.getContainersUnderCheck(
"getContainersUnderCheck",
mSharedPreferences!!.getString(Constants.CFID, "")!!,
mSharedPreferences!!.getString(Constants.CFTOKEN, "")!!
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {}
.doOnTerminate {}
.subscribe({ result ->
result?.let {
if (result.ResponseCode != null && result.ResponseCode.trim() != "000") {
onRetrievePostListError(result.ResponseMessage)
} else {
result.ContainersData?.let { it1 -> onRetrievePostListSuccess(it1) }
}
}
}, { throwable ->
android.util.Log.e("getDataInquiry", throwable.message!!)
onRetrievePostListError(activity.resources.getString(R.string.general_error))
})
}
private fun getPendingData() {
val query = mRootView.svSearchMSC.query.toString()
if (query == "") {
dataListAdapter.getFilter(2, listType.value!!).filter("PENDING")
} else {
if (query.length > 2) {
dataListAdapter.getFilter(3, listType.value!!).filter(query)
} else {
errorMessage.value = activity.resources.getString(R.string.addCorrectNumber)
}
}
}
private fun getDoneData() {
val query = mRootView.svSearchMSC.query.toString()
if (query == "") {
dataListAdapter.getFilter(1, listType.value!!).filter("ANY")
} else {
if (query.length > 2) {
dataListAdapter.getFilter(3, listType.value!!).filter(query)
} else {
errorMessage.value = activity.resources.getString(R.string.addCorrectNumber)
}
}
}
private fun onRetrievePostListStart() {
loading.value = true
}
private fun onRetrievePostListFinish() {
loading.value = false
isRefreshing = false
}
private fun onRetrievePostListSuccess(containersData: List<ContainersData>) {
onRetrievePostListFinish()
dataListAdapter.updateInquiryAdapter(containersData as ArrayList<ContainersData>)
if (listType.value == 1) {
dataListAdapter.getFilter(1, listType.value!!).filter("ANY")
} else if (listType.value == 0) {
dataListAdapter.getFilter(2, listType.value!!).filter("PENDING")
}
}
private fun onRetrievePostListError(message: String?) {
onRetrievePostListFinish()
getDataError.value = true
errorMessage.value = message
}
override fun onCleared() {
super.onCleared()
subscription.dispose()
}
override fun onRefresh() {
isRefreshing = true
getData()
}
}
adapter is :
class ContainersUnderCheckAdapter(val activity: LaunchActivity) :
RecyclerView.Adapter<ContainersUnderCheckAdapter.ViewHolder>() {
private lateinit var mDataSet: ArrayList<ContainersData>
private lateinit var mDataSetFiltered: ArrayList<ContainersData>
fun updateInquiryAdapter(dataSet: ArrayList<ContainersData>) {
mDataSet = ArrayList()
mDataSet.clear()
mDataSet.addAll(dataSet)
mDataSetFiltered = mDataSet
getFilter(2, 1).filter("PENDING")
// notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ContianerItemFieldLayoutBinding = DataBindingUtil
.inflate(
LayoutInflater.from(parent.context),
R.layout.contianer_item_field_layout,
parent,
false
)
return ViewHolder(binding, activity)
}
override fun getItemCount(): Int {
return if (::mDataSetFiltered.isInitialized) mDataSetFiltered.size else 0
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(mDataSetFiltered[position])
}
operator fun get(position: Int): ContainersData {
return mDataSetFiltered.get(position)
}
/**
* #filterType :
* IF 1 : filter on Data Type RJCTD + APPROVED
* 2 : filter on Data Type PENDING
* 3 :
*/
fun getFilter(filterType: Int, listType: Int): Filter {
return object : Filter() {
override fun performFiltering(charSequence: CharSequence): FilterResults {
val charString = charSequence.toString()
mDataSetFiltered = if (charString.isEmpty()) {
mDataSet
} else {
val filteredList = ArrayList<ContainersData>()
for (row in mDataSet) {
when (filterType) {
1 -> {
if (row.status == "RJCTD" || row.status == "APPROVED") {
filteredList.add(row)
}
}
2 -> {
if (row.status == charString) {
filteredList.add(row)
}
}
3 -> {
when (listType) {
0 -> {
if ((row.CID!!.contains(charString.toUpperCase(Locale.ROOT)) || row.TN!!.contains(
charSequence
) || row.PN!!.contains(charSequence)) && row.status == "PENDING"
) {
filteredList.add(row)
}
}
1 -> {
if ((row.CID!!.contains(charString.toUpperCase(Locale.ROOT)) || row.TN!!.contains(
charSequence
) || row.PN!!.contains(charSequence)) && row.status != "PENDING"
) {
filteredList.add(row)
}
}
}
}
}
}
filteredList
}
val filterResults = FilterResults()
filterResults.values = mDataSetFiltered
return filterResults
}
override fun publishResults(
charSequence: CharSequence,
filterResults: FilterResults
) {
if (::mDataSetFiltered.isInitialized) {
mDataSetFiltered = try {
filterResults.values as ArrayList<ContainersData>
} catch (e: Exception) {
Log.e("mDataSetFiltered",e.message!!)
ArrayList()
}
when (filterType) {
1->{
mDataSetFiltered.sortWith(Comparator { p0, p1 -> p1!!.UpdateDate.compareTo(p0!!.UpdateDate) })
}
2->{
mDataSetFiltered.sortWith(Comparator { p0, p1 -> p0!!.ID!!.compareTo(p1.ID!!) })
}
}
}
// refresh the list with filtered data
notifyDataSetChanged()
}
}
}
class ViewHolder(
private val binding: ContianerItemFieldLayoutBinding,
val activity: LaunchActivity
) : RecyclerView.ViewHolder(binding.root) {
private val viewModel = MscInspectionListViewModel(activity)
fun bind(data: ContainersData) {
viewModel.bind(data)
binding.viewModel = viewModel
}
}
}
any data in this recycler on click go to fragment has tow recycler first one to show data, the second one to pick Images
the second-page code
class MSCDataFragment : Fragment() {
lateinit var rootView: View
lateinit var activity: LaunchActivity
lateinit var utility: Utility
lateinit var loadingView: LoadingView
private lateinit var viewModel: MSCDataViewModel
private lateinit var binding: FragmentMscdataBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (getActivity() != null) {
activity = getActivity() as LaunchActivity
utility = Utility(activity)
loadingView = LoadingView(activity)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_mscdata, container, false)
rootView = binding.root
initial()
return rootView
}
private fun initial() {
viewModel = ViewModelProvider(
this, ViewModelFactory(
activity,
arguments!!.getSerializable("Data") as ContainersData
)
).get(MSCDataViewModel::class.java)
binding.viewModel = viewModel
// binding.imgList.layoutManager = GridLayoutManager(activity, 3)
binding.containerInfo.layoutManager = LinearLayoutManager(activity)
binding.openCIDNotValid.typeface =
Typeface.createFromAsset(activity.assets, "Bahij_Janna-Regular.ttf")
binding.openCIDNotValid.setOnCheckedChangeListener(viewModel.onOpenCidNotValidListener)
viewModel.loading.observe(this, Observer { loading ->
loading?.let {
if (it) {
loadingView.show()
} else {
loadingView.dismiss()
}
}
})
viewModel.errorMessage.observe(this, Observer { msg ->
msg?.let {
utility.ShowToast(msg)
}
})
viewModel.imagesAdapters2.observe(this, Observer { msg ->
msg?.let {
binding.imgList.apply {
layoutManager = GridLayoutManager(activity, 3)
adapter = it
}
}
})
rootView.toolbar_Back.setOnClickListener(viewModel.backClickListener)
binding.btnAddImages.setOnClickListener(viewModel.pickImages)
binding.successContianer.setOnClickListener(viewModel.correctContainer)
binding.damagedContianer.setOnClickListener(viewModel.wrongContainer)
}
}
the view model is :
class MSCDataViewModel(val activity: LaunchActivity, val containersData: ContainersData) :
BaseViewModel(), GetImagesListener {
#Inject
lateinit var restApiAccount: RestApiAccount
val toolBarTitle: MutableLiveData<String> = MutableLiveData()
val ButtonText: MutableLiveData<String> = MutableLiveData()
var openCIDNotValidVisibility = MutableLiveData<Int>()
private val getDataError = MutableLiveData<Boolean>()
val btnImagesVisibility = MutableLiveData<Int>()
var imgeNoteVisibility = MutableLiveData<Int>()
var successVisibility = MutableLiveData<Int>()
var damagedVisibility = MutableLiveData<Int>()
var notesVisibility = MutableLiveData<Int>()
val btnVisibility = MutableLiveData<Int>()
var canNotOpen = MutableLiveData<Int>()
private val images = ArrayList<Image>()
var utility = Utility(activity)
private var CURRENTINDEX = 0
private var mSharedPreferences: SharedPreferences? = null
val DataListAdapter = ContainerDataAdapter(activity)
var imagesAdapter = ContainerImagesAdapter(activity, containersData.status!!, ArrayList())
val imagesAdapters2 = MutableLiveData<ContainerImagesAdapter2>()
val userInfo: UserInfo
val backClickListener = View.OnClickListener { activity.supportFragmentManager.popBackStack() }
val pickImages = View.OnClickListener {
pickImages()
}
val correctContainer = View.OnClickListener {}
val onOpenCidNotValidListener =
CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
successVisibility.value = View.GONE
canNotOpen.value = 1
} else {
canNotOpen.value = 0
successVisibility.value = View.VISIBLE
}
}
val wrongContainer = View.OnClickListener {}
var mscNotes: ObservableField<String> = ObservableField("")
init {
canNotOpen.value = 0
mSharedPreferences =
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
toolBarTitle.value = containersData.CID
ButtonText.value = activity.resources.getString(R.string.cleanContianer)
userInfo = utility.readObjectFromSharedPreferences(
mSharedPreferences,
Constants.USER_INFO_KEY,
UserInfo::class.java
) as UserInfo
openCIDNotValidVisibility.value = View.GONE
fillData()
}
private fun fillData() {
val data: LinkedHashMap<String, String> = containersData.data!!
val captionsMap = utility.readObjectFromSharedPreferences(
mSharedPreferences, Constants.CAPTIONS_MAP_KEY,
HashMap::class.java
) as HashMap<String, String>
if (containersData.data.size > 0) {
val list = ArrayList<KeyValueModel>()
for (inside in data.keys) {
val ky = captionsMap[inside]
val value = data[inside].toString()
ky?.let { KeyValueModel(it, value) }?.let { list.add(it) }
}
DataListAdapter.updateInquiryAdapter(list)
} else {
errorMessage.value = activity.resources.getString(R.string.no_data)
}
if (containersData.ImageList != null && containersData.ImageList.isNotEmpty()) {
imagesAdapter.updateContainerImagesAdapter(containersData.ImageList)
}
}
private fun pickImages() {
activity.setCallBack(this)
val pictureDialog: AlertDialog
val builder = activity.let { AlertDialog.Builder(it) }
val dialogView = View.inflate(activity, R.layout.choose_camera_method, null)
builder.setView(dialogView)
val nafithPopupContainer = dialogView.findViewById<RelativeLayout>(R.id.RLTitle)
nafithPopupContainer.setBackgroundColor(
ContextCompat.getColor(
activity,
R.color.mainColor
)
)
val popUpGallery = dialogView.findViewById<LinearLayout>(R.id.PopupGellary)
val popUpCamera = dialogView.findViewById<LinearLayout>(R.id.PopupCamera)
pictureDialog = builder.create()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Objects.requireNonNull<Window>(pictureDialog.window)
.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
} else {
if (pictureDialog.window != null) {
pictureDialog.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
}
popUpGallery.setOnClickListener {
fromGallery()
pictureDialog.dismiss()
}
popUpCamera.setOnClickListener {
fromCamera()
pictureDialog.dismiss()
}
val popupClose = dialogView.findViewById<ImageView>(R.id.popupClose)
popupClose.setOnClickListener { pictureDialog.dismiss() }
pictureDialog.show()
}
private fun fromGallery() {
ImagePicker.create(activity)
.toolbarImageTitle(activity.resources.getString(R.string.get_image))
.toolbarArrowColor(ContextCompat.getColor(activity, R.color.colorWhite))
.showCamera(false)
.limit(6)
.start()
}
private fun fromCamera() {
ImagePicker.cameraOnly().start(activity)
}
override fun onGetImage(image: Image) {
imgeNoteVisibility.value = View.GONE
imagesAdapter.updateContainerImagesAdapter(image)
images.add(image)
}
override fun addingImagesDone(mImages: MutableList<Image>) {
images.clear()
images.addAll(mImages)
imgeNoteVisibility.value = View.GONE
val listString :ArrayList<String> = ArrayList()
for (i in mImages.indices){
listString.add(mImages[i].path)
}
imagesAdapters2.value = ContainerImagesAdapter2(activity,containersData.status!!,listString)
imagesAdapters2.value!!.notifyItemRangeChanged(0,listString.size)
}
override fun onImgDelete(image: String) {
var x = 0
try {
for (i in 0 until images.size) {
x = i
if (images[i].path == image) {
images.remove(images[i])
}
}
} catch (e: Exception) {
Log.e("errorImages", e.message!!)
Log.e("xx", x.toString())
}
}
private fun onRetrievePostListStart() {
loading.value = true
}
private fun onRetrievePostListFinish() {
loading.value = false
}
private fun onRetrievePostListSuccess(msg: String?) {
onRetrievePostListFinish()
}
private fun onRetrievePostListError(message: String?) {
onRetrievePostListFinish()
getDataError.value = true
errorMessage.value = message
}
}
Adapter code is :
class ContainerImagesAdapter2() : RecyclerView.Adapter<ContainerImagesAdapter2.ViewHolder>() {
var status: String = ""
lateinit var activity: LaunchActivity
lateinit var utility: Utility
constructor(
mActivity: LaunchActivity,
mStatus: String,
pathsList: ArrayList<String>
) : this() {
activity = mActivity
pathsDataSet = pathsList
status = mStatus
utility = Utility(activity)
}
private var pathsDataSet: ArrayList<String> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ContianerImageFieldBinding = DataBindingUtil
.inflate(
LayoutInflater.from(parent.context),
R.layout.contianer_image_field,
parent,
false
)
return ViewHolder(binding, activity)
}
override fun getItemCount(): Int {
return pathsDataSet.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindPath(pathsDataSet[position], position)
}
inner class ViewHolder(
private val binding: ContianerImageFieldBinding,
val activity: LaunchActivity
) : RecyclerView.ViewHolder(binding.root) {
private val viewModel = MscImagesListViewModel(activity)
fun bindPath(data: String, position: Int) {
viewModel.bindPath(data)
binding.viewModel = viewModel
if (status != "PENDING") {
binding.closeImg.visibility = View.GONE
}
binding.closeImg.setOnClickListener {}
binding.mainImg.setOnClickListener {
val fragment = FullImageFragment()
val bundle = Bundle()
val list = ArrayList<String>()
for (item in 0 until pathsDataSet.size) {
list.add(pathsDataSet[item])
}
bundle.putSerializable("ImageList", list)
bundle.putInt("Position", position)
fragment.arguments = bundle
activity.supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment).addToBackStack(fragment.tag)
.commit()
}
}
}
}
if you filter data using search view in the first-page and pick images in the second page , list of picked images doesn't appear, if you going to the second page without filtering data everything ok
solve Problem found
Just Update constraint-layout library in gradle dependencies to version '2.0.0-beta4'

RecyclerView list item does not show when entries are made to the Room database

I am trying to rewrite an existing app in Kotlin for the purpose of learning and getting used to the language. The app allows the user to enter and modify entries and each entry is hosted in a RecyclerView list with a custom list item. Although entries are successfully added to the database (confirming that with Toast messages), there isn't a list item present for the said entry.
This is my adapter:
class EntryAdapter : androidx.recyclerview.widget.ListAdapter<Entry, EntryAdapter.ViewHolder>(DIFF_CALLBACK){
private var listener: OnItemLongClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val currentEntry = getItem(position)
holder.hint.text = currentEntry.hint
holder.username.text = currentEntry.username
holder.password.text = currentEntry.password
}
fun getEntryAt(position: Int): Entry{return getItem(position)}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val username: TextView = itemView.findViewById(R.id.username_display)
val password: TextView = itemView.findViewById(R.id.password_display)
val hint: TextView = itemView.findViewById(R.id.hint_display)
init {
itemView.setOnLongClickListener{
val position = adapterPosition
if (listener != null && position != RecyclerView.NO_POSITION){listener!!.onItemLongClick(getItem(position))}
true
}
}
}
interface OnItemLongClickListener{fun onItemLongClick(entry: Entry)}
fun setOnItemLongClickListener(listener: OnItemLongClickListener){this.listener = listener}
companion object{
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Entry>(){
override fun areItemsTheSame(oldItem: Entry, newItem: Entry): Boolean{return oldItem.id == newItem.id}
override fun areContentsTheSame(oldItem: Entry, newItem: Entry): Boolean{return oldItem.username == newItem.username && oldItem.hint == newItem.hint && oldItem.password == newItem.password}
}
}
}
This is my AddEditEntry.kt activity. The way it works is that when the user wishes to make an entry, he/she clicks on the FAB button which invokes this activity. This activity is where the user enters the entry and adds it to the database (and by extension, the RecyclerView) by clicking the saveEntry button:
class AddEditEntryActivity : AppCompatActivity() {
private var usernameEditText: EditText? = null
private var passwordEditText: EditText? = null
private var hintEditText: EditText? = null
private var passwordABCD: CheckBox? = null
private var passwordabcd: CheckBox? = null
private var password0123: CheckBox? = null
private var passwordSymbols: CheckBox? = null
private var radio4: RadioButton? = null
private var radio8: RadioButton? = null
private var radio12: RadioButton? = null
private var radio16: RadioButton? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_addedit_entry)
usernameEditText = findViewById(R.id.username_field)
passwordEditText = findViewById(R.id.password_field)
hintEditText = findViewById(R.id.hint_field)
passwordABCD = findViewById(R.id.upp_checkbox)
passwordabcd = findViewById(R.id.low_checkbox)
password0123 = findViewById(R.id.num_checkbox)
passwordSymbols = findViewById(R.id.sym_checkbox)
radio4 = findViewById(R.id.four)
radio8 = findViewById(R.id.eight)
radio12 = findViewById(R.id.twelve)
radio16 = findViewById(R.id.sixteen)
val generatePassword = findViewById<Button>(R.id.btn_password_generate)
val saveEntry = findViewById<Button>(R.id.btn_save)
val intent = intent
if (intent.hasExtra(EXTRA_ID)) {
title = getString(R.string.edit_entry)
saveEntry.setText(R.string.update_entry)
usernameEditText!!.setText(getIntent().getStringExtra(EXTRA_USERNAME))
passwordEditText!!.setText(getIntent().getStringExtra(EXTRA_PASSWORD))
hintEditText!!.setText(getIntent().getStringExtra(EXTRA_HINT))
}
else {title = "Add Entry"}
Objects.requireNonNull<ActionBar>(supportActionBar).setHomeAsUpIndicator(R.drawable.ic_close_white_24dp)
generatePassword.setOnClickListener { passwordEditText!!.setText(generatedPassword()) }
saveEntry.setOnClickListener {
val data = Intent()
data.putExtra(EXTRA_USERNAME, usernameEditText!!.text.toString())
data.putExtra(EXTRA_HINT, hintEditText!!.text.toString())
data.putExtra(EXTRA_PASSWORD, passwordEditText!!.text.toString())
val id = getIntent().getIntExtra(EXTRA_ID, -1)
if (id != -1) {data.putExtra(EXTRA_ID, id)}
setResult(Activity.RESULT_OK, data)
finish()
Toast.makeText(this, "data.putExtra() from AddEditEntryActivity", Toast.LENGTH_SHORT).show()
Toast.makeText(this, usernameEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
Toast.makeText(this, hintEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
Toast.makeText(this, passwordEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
}
}
private fun generatedPassword(): String? {
var length = 0
val generatedString = StringBuilder()
val rand = Random()
val capitalLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
val lowercaseLetters = "abcdefghijklmnopqrstuvwxyz"
val numbers = "0123456789"
val characters = "!##$%^&*()"
if (radio4!!.isChecked) {length = 4}
else if (radio8!!.isChecked) {length = 8}
else if (radio12!!.isChecked) {length = 12}
else if (radio16!!.isChecked) {length = 16}
var totalCharacters = ""
if (passwordABCD!!.isChecked) {totalCharacters += capitalLetters}
if (passwordabcd!!.isChecked) {totalCharacters += lowercaseLetters}
if (password0123!!.isChecked) {totalCharacters += numbers}
if (passwordSymbols!!.isChecked) {totalCharacters += characters}
if (!totalCharacters.trim { it <= ' ' }.isEmpty() && length > 0) {
for (i in 0 until length) {generatedString.append(totalCharacters[rand.nextInt(totalCharacters.length)])}
return generatedString.toString()
}
else {Toast.makeText(this, "Not a valid password!", Toast.LENGTH_SHORT).show()}
return null
}
companion object {
val EXTRA_USERNAME = "com.ozbek.cryptpass.EXTRA_USERNAME"
val EXTRA_HINT = "com.ozbek.cryptpass.EXTRA_HINT"
val EXTRA_PASSWORD = "com.ozbek.cryptpass.EXTRA_PASSWORD"
val EXTRA_ID = "com.ozbek.cryptpass.EXTRA_ID"
}
}
And this is the MainActivity.kt file where the user enters new entries. The first if block in onActivityResult() is the code that retrieves the entries from AddEditEntry.kt file and adds it to the entity class:
class MainActivity : AppCompatActivity(), LifecycleOwner {
private lateinit var recyclerView: RecyclerView
internal lateinit var adapter: EntryAdapter
private lateinit var floatingActionButton: FloatingActionButton
private lateinit var layoutManager: RecyclerView.LayoutManager
private lateinit var addEditEntryActivity: AddEditEntryActivity
internal lateinit var entry: Entry
private lateinit var viewModel: EntryViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val ctx = this.applicationContext
val sentryDsn = "https://93f06d5090d646ac9443a3fc531fc5de#sentry.io/1454826:port/1?options"
Sentry.init(sentryDsn, AndroidSentryClientFactory(ctx))
Sentry.init(AndroidSentryClientFactory(ctx))
viewModel = ViewModelProviders.of(this).get(EntryViewModel::class.java)
viewModel.allEntries.observe(this, Observer { entries -> adapter.submitList(entries) })
adapter = EntryAdapter()
layoutManager = LinearLayoutManager(this)
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
layoutManager = layoutManager
adapter = adapter
}
addEditEntryActivity = AddEditEntryActivity()
entry = Entry()
floatingActionButton = findViewById<FloatingActionButton>(R.id.generate_fab)
floatingActionButton.setOnClickListener {
val intent = Intent(this#MainActivity, AddEditEntryActivity::class.java)
Toast.makeText(this, "AddEditActivity started", Toast.LENGTH_SHORT).show()
startActivityForResult(intent, ADD_ENTRY_REQUEST)
}
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {return false}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {viewModel.delete(adapter.getEntryAt(viewHolder.adapterPosition))}
}).attachToRecyclerView(recyclerView)
adapter.setOnItemLongClickListener (object: EntryAdapter.OnItemLongClickListener {
override fun onItemLongClick(entry: Entry){
val intent = Intent(this#MainActivity, AddEditEntryActivity::class.java)
intent.putExtra(AddEditEntryActivity.EXTRA_ID, entry.id)
intent.putExtra(AddEditEntryActivity.EXTRA_USERNAME, entry.username)
intent.putExtra(AddEditEntryActivity.EXTRA_HINT, entry.hint)
intent.putExtra(AddEditEntryActivity.EXTRA_PASSWORD, entry.password)
startActivityForResult(intent, EDIT_ENTRY_REQUEST)
}
})
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val itemId = item.itemId
if (itemId == R.id.delete_all) {viewModel.deleteAll()}
return super.onOptionsItemSelected(item)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == ADD_ENTRY_REQUEST && resultCode == Activity.RESULT_OK) {
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_USERNAME), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_HINT), Toast.LENGTH_SHORT).show()
val username = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_USERNAME)
val password = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD)
val hint = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_HINT)
val entry = Entry(username, hint, password)
viewModel.insert(entry)
Toast.makeText(this, "Entry added!", Toast.LENGTH_SHORT).show()
} else if (requestCode == EDIT_ENTRY_REQUEST && resultCode == Activity.RESULT_OK) {
Toast.makeText(this, data!!.getIntExtra(AddEditEntryActivity.EXTRA_ID, -1), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_USERNAME), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_HINT), Toast.LENGTH_SHORT).show()
val id = Objects.requireNonNull<Intent>(data).getIntExtra(AddEditEntryActivity.EXTRA_ID, -1)
if (id == -1) {
Toast.makeText(this, "Something went wrong", Toast.LENGTH_SHORT).show()
return
}
val username = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_USERNAME)
val password = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD)
val hint = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_HINT)
val entry = Entry(username, hint, password, id)
entry.id = id
viewModel.update(entry)
Toast.makeText(this, "Entry updated", Toast.LENGTH_SHORT).show()
} else {Toast.makeText(this, "Entry not added!", Toast.LENGTH_SHORT).show()}
}
companion object {
const val ADD_ENTRY_REQUEST = 1
const val EDIT_ENTRY_REQUEST = 2
}
}
I can add more code per request. This is the full Github repo.
Here is the problem:
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
layoutManager = layoutManager
adapter = adapter
}
Idk what's going on but it seems like you're initializing them to themselves. Don't do that. Its good practice to keep your variable names distinct. Refactor the objects like this:
internal lateinit var entryAdapter: EntryAdapter
private lateinit var linearLayoutManager: RecyclerView.LayoutManager
And make the following changes in your onCreate():
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
adapter = entryAdapter
layoutManager = linearLayoutManager
}

Categories

Resources