I have backend firebase which i am using to populate the listview , i was implementing the search functionality suddenly i found a weird behavior in listview ,it is creating duplicates in numbers like 30-40 and above.
Please help me i was batteling for it from 3 days. i am very new to development so any suggestions to code optimizing will also be taken as help!
class Menu : AppCompatActivity(){
private var prevKey : String? = null
var listAdapter : CustomAdapter ? = null
private val listItemArrayList = ArrayList<ListItem>()
var rootRef : FirebaseDatabase? = null
var adminRef : DatabaseReference? = null
var mAuth : FirebaseAuth? = null
var helper : CustomAdapter?=null
private var user : String?=null
var mAuthListener: FirebaseAuth.AuthStateListener? = null
var Items = ArrayList<String>()
var isSectionBoolean = ArrayList<Boolean>()
var ItemCategory= ArrayList<String>()
var ItemCost = ArrayList<Int>()
var listView : ListView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_menu)
mAuth = FirebaseAuth.getInstance()
mAuthListener = FirebaseAuth.AuthStateListener { }
rootRef = FirebaseDatabase.getInstance()
user =mAuth!!.currentUser!!.phoneNumber as String
adminRef = rootRef!!.getReference("AdminCafe").child(user!!)
listView = findViewById(R.id.admindynamiclist)
listAdapter = CustomAdapter(listItemArrayList,this)
listView!!.adapter = listAdapter
listView!!.isTextFilterEnabled = true
SearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
listAdapter!!.filter(newText.toString())
return false
}
})
getData()
}
fun addAct(v:View)
{
val intent = Intent(applicationContext,AddActivity::class.java)
startActivity(intent)
}
private fun getData() {
println("getData")
var menucatRef = adminRef!!.child("MenuItem")
menucatRef.addValueEventListener(object : ValueEventListener{
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
for(Snap in p0.children)
{
println("wtfff")
println("Child"+Snap.childrenCount)
getDataFromKey(Snap.key.toString(),Snap.childrenCount.toInt())
}
}
})
}
private fun getDataFromKey(key: String, child :Int) {
listItemArrayList.clear()
println("called")
var count = 0
var counter = 0
var Category = CategoryHeader()
var menuRef = adminRef!!.child("MenuItem").child(key)
menuRef.addListenerForSingleValueEvent(object : ValueEventListener{
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
var catKey = p0.key.toString()
for(snap in p0!!.children)
{
if(count == 0)
{
Category!!.setheader(catKey)
listItemArrayList!!.add(Category)
println("Seperator will be added")
}
else
{
var hashMap = snap.value as HashMap<String,String>
var itemName = snap.key
var isVeg = hashMap["isVeg"]
var itemCost = hashMap["itemCost"]!!.toInt()
var isSig = hashMap["isSig"]
var ItemHelper = Item()
if(itemName!=null)
{
ItemHelper.setItemName(itemName)
}
if(itemCost!=null)
{
ItemHelper.setItemCost(itemCost)
}
listItemArrayList!!.add(ItemHelper)
}
count++
}
listAdapter!!.notifyDataSetChanged()
}
})
}
interface ListItem {
fun isHeader(): Boolean
fun getName(): String
fun getItemCost() : Int
}
fun editAct(v:View)
{
println("Inside edit")
}
}
And Adapter class
class CustomAdapter(private var listItem : ArrayList<Menu.ListItem>, private val context: Activity) : BaseAdapter() {
var orig = ArrayList<Menu.ListItem> ()
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
orig.addAll(listItem)
println("List Item is"+listItem)
println("List Item is"+orig)
var holder = ViewHolder()
var customView = convertView
var flag = true
if (convertView == null) {
val layoutInflater = context.layoutInflater
if(listItem.get(position).isHeader()) {
println("position"+position)
customView = layoutInflater.inflate(R.layout.seperator, null)
holder.tvLabel = customView.categoryText
} else {
println("position"+position)
customView = layoutInflater.inflate(R.layout.list_item, null)
holder.tvLabel = customView.itemad
holder.itemCost = customView.cost
flag = false
}
customView.setTag(holder)
}
else
{
customView = convertView
holder = convertView!!.tag as ViewHolder
}
if(flag) {
println("Custoom Flag is"+flag)
holder!!.tvLabel!!.text = listItem.get(position).getName()
}
else {
println("Custoom Flag is"+flag)
holder!!.tvLabel!!.text = listItem.get(position).getName()
holder.itemCost!!.text = listItem.get(position).getItemCost().toString()
}
return customView!!
}
override fun getItem(position: Int): Any {
return listItem.get(position)
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
return listItem.size
}
private inner class ViewHolder {
var tvLabel: TextView? = null
var itemCost: TextView? = null
}
fun filter(query : String)
{
println("called")
listItem.clear()
var search = query
search = search.toLowerCase(Locale.getDefault())
if(search.isEmpty())
{
listItem.addAll(orig!!)
println("Inside size 0")
}
else
{
var counter = 0
for(result in orig!! )
{
if(result.getName().toLowerCase().contains(search))
{
println("Result is "+result.getName())
println("listItem"+listItem)
}
}
}
}
}
Related
I need to develop an app from university.
When I start the app, the RecyclerView is displayed correctly. However, when I refresh the RecyclerView or switch to another TAP, the card layouts are no longer displayed.
I get all the events from Firebase using an Event data class and pass them to the RecyclerAdapter.
Can you help me so that the refresh works?
Thanks a lot
Augustin
RecyclerAdapter:
class RecyclerAdapter(val events: List<Event>) :
RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {
private lateinit var navController: NavController
private lateinit var events_id : String
var countEventId = mutableListOf<String>()
var count = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.view_cardlayout, parent, false)
return ViewHolder(view)
}
override fun getItemCount() = events.size
override fun onBindViewHolder(holder: RecyclerAdapter.ViewHolder, position: Int) {
holder.bind(events[position])
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(event: Event) {
itemView.headlineTV.text = event.name
itemView.placeHolderTV.text = event.location
itemView.dateHolderTV.text = event.date
itemView.timeHolderTV.text = "${event.starttime} Uhr"
itemView.dataHolderVotes.text = event.votes.toString()
itemView.dataHolderVotes.text = event.votesUser?.size.toString()
countEventId.add(event.eventId)
count++
}
init {
itemView.setOnClickListener {
val pos: Int =
bindingAdapterPosition
navController = findNavController(itemView)
val action_HomeToData1 =
HomeFragmentDirections.actionHomeFragmentToData1Fragment(countEventId[pos])
navController.navigate(action_HomeToData1)
}
}
}
}
HomeFragment:
class HomeFragment : Fragment(R.layout.fragment_home) {
private lateinit var firestoneDb: FirebaseFirestore
private lateinit var events: MutableList<Event>
//private lateinit var adapter: RecyclerAdapter
private var layoutManager: RecyclerView.LayoutManager? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var auth = FirebaseAuth.getInstance()
var user = auth.currentUser
var uid = user?.uid
firestoneDb = FirebaseFirestore.getInstance()
events = mutableListOf()
recyclerView.adapter = RecyclerAdapter(events)
recyclerView.layoutManager = LinearLayoutManager(activity)
val eventsReference = firestoneDb.collection("events")
eventsReference
.whereEqualTo("creator", uid)
.addSnapshotListener { snapshot, exception ->
if (exception != null || snapshot == null) {
Log.e(TAG, "Exception when querying events", exception)
return#addSnapshotListener
}
var eventList = snapshot.toObjects(Event::class.java)
events.clear()
events.addAll(eventList)
for (event in eventList) {
Log.i(TAG, "Event ${event}")
}
}
refreshHome()
}
override fun onResume() {
super.onResume()
}
private fun refreshHome() {
refresherHome.setOnRefreshListener {
Toast.makeText(activity, "Page has been refreshed", Toast.LENGTH_SHORT).show()
refresherHome.isRefreshing = false
val action_HomeToSelf = HomeFragmentDirections.actionHomeFragmentSelf()
findNavController().navigate(action_HomeToSelf)
}
}
}
Data class Event:
data class Event (
var description: String = "",
#get: PropertyName("start_time") #set:PropertyName("start_time") var starttime: String = "",
var creator: String = "",
#get: PropertyName("event_id") #set:PropertyName("event_id")var eventId: String = "",
var location: String = "",
var name: String = "",
var questions: List<String>? = null,
var date: String = "",
#get: PropertyName("votes") #set:PropertyName("votes") var votes: Int = 0,
#get: PropertyName("creationTimeMs") #set:PropertyName("creationTimeMs")var creationTimeMs: Long = 0,
#get: PropertyName("votes_user") #set:PropertyName("votes_user")var votesUser: List<String>? = null
)
After you change the events in the list, you need to tell the adapter that its data has change so that it knows to refresh the view. To do this call adapter.notifyDataSetChanged(), inside the snapshot listener after you're done modifying events.
Somehow I see in the app messages I sent, but can't see those which should be received from others. Also when I'm sending a message it's sent to all users. Here I'm sending two classes, adapter and main chat activity. I tried using inner classes, different types of getItemViewType functions but nothing works.
MessageAdapter
class MessageAdapter(val context: Context, val messageList: ArrayList<Message>):
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val ITEM_RECIEVE = 1
val ITEM_SENT = 2
class SentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
{
val sentMessage = itemView.findViewById<TextView>(R.id.txt_sent_message)
}
class RecieveViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
{
val recieveMessage = itemView.findViewById<TextView>(R.id.txt_recieve_message)
}
override fun getItemViewType(position: Int): Int {
val currentMessage = messageList[position]
if (FirebaseAuth.getInstance().currentUser?.uid.equals(currentMessage.senderId)) {
return ITEM_SENT
}
else {
return ITEM_RECIEVE
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == 1)
{
val view = LayoutInflater.from(context).inflate(R.layout.recieve, parent, false)
Log.d("tag2", "testrecieveviewholderview11")
return RecieveViewHolder(view)
}
else
{
val view = LayoutInflater.from(context).inflate(R.layout.sent, parent, false)
Log.d("tag", "testsentviewholderview22")
return SentViewHolder(view)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val currentMessage = messageList[position]
if(holder.javaClass == SentViewHolder::class.java)
{
val viewHolder = holder as SentViewHolder
holder.sentMessage.text = currentMessage.message
}
else
{
val viewHolder = holder as RecieveViewHolder
holder.recieveMessage.text = currentMessage.message
}
}
override fun getItemCount(): Int {
return messageList.size
}
}
And ChatActivity
var firebaseUser: FirebaseUser? = null
private lateinit var messageRecyclerView: RecyclerView
private lateinit var messageBox: EditText
private lateinit var sendButton: ImageButton
private lateinit var messageAdapter: MessageAdapter
private lateinit var messageList: ArrayList<Message>
private lateinit var mRef: DatabaseReference
var recieverRoom: String? = null
var senderRoom: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat)
val intent = Intent()
val name = intent.getStringExtra("name")
val recieverUid = intent.getStringExtra("uid")
val senderUid = FirebaseAuth.getInstance().currentUser?.uid
mRef = FirebaseDatabase.getInstance().reference
senderRoom = recieverUid + senderUid
recieverRoom = senderUid + recieverUid
messageRecyclerView = findViewById(R.id.RecyclerViewChat)
messageBox = findViewById(R.id.messageBox)
sendButton = findViewById(R.id.sendButton)
messageList = ArrayList()
messageAdapter = MessageAdapter(this, messageList)
messageRecyclerView.layoutManager = LinearLayoutManager(this)
messageRecyclerView.scrollToPosition(messageAdapter.itemCount-1)
messageRecyclerView.adapter = messageAdapter
mRef.child("chats").child(senderRoom!!).child("messages")
.addValueEventListener(object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
messageList.clear()
for (postSnapshot in snapshot.children)
{
val message = postSnapshot.getValue(Message::class.java)
messageList.add(message!!)
}
messageAdapter.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
}
})
sendButton.setOnClickListener {
if (messageBox.text.toString().isEmpty() || messageBox.text.toString().isBlank())
{
messageBox.setText("")
}
else {
val message = messageBox.text.toString()
val messageObject = Message(message, senderUid)
mRef.child("chats").child(senderRoom!!).child("messages").push()
.setValue(messageObject).addOnSuccessListener {
mRef.child("chats").child(recieverRoom!!).child("messages").push()
.setValue(messageObject)
}
messageBox.setText("")
}
}
}
}
The error coming in this code is "Unresolved reference: message" and "Unresolved reference: senderId" in MessageAdapter. MessageAdapter is not able to take variables that are declared in Message class(message and senderId).
I have added firebase database and the data also gets saved into the firebase realtime database, but it doesnot gets shown into the spinner, my spinner is showing no value.If theres some other way?
This is the code of my fragment:
class IncomeFragment: Fragment(){
//FAB
lateinit var addBankFab: FloatingActionButton
lateinit var addBankText: TextView
//Spinner
lateinit var spinner: Spinner
lateinit var dropDown: TextView
//Firebase Database
lateinit var mAuth: FirebaseAuth
lateinit var mIncomeDatabase: DatabaseReference
lateinit var mBankDatabase: DatabaseReference
lateinit var helper: FirebaseHelper
//Recycler View
lateinit var recyclerView: RecyclerView
//TextView
lateinit var incomeTotal : TextView
//Update Edit Text
lateinit var editAmount: EditText
lateinit var editType: EditText
lateinit var editNote: EditText
lateinit var editBankName:EditText
//Button for update and delete
lateinit var btnUpdate: Button
lateinit var btnDelete: Button
lateinit var btnSave : Button
lateinit var btnCancel:Button
//Data item Value
lateinit var type: String
lateinit var note: String
var amount: Int = 0
lateinit var post_key:String
lateinit var name:String
lateinit var ac:String
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val myview= inflater.inflate(R.layout.fragment_income, container, false)
mAuth= FirebaseAuth.getInstance()
val mUser = mAuth.currentUser
val uid = mUser?.uid
addBankFab=myview.findViewById(R.id.add_bank_fab)
addBankText=myview.findViewById(R.id.add_bank_text)
spinner=myview.findViewById(R.id.incomeSpinner)
mIncomeDatabase =
FirebaseDatabase.getInstance().reference.child("IncomeData").child(uid.toString())
incomeTotal=myview.findViewById(R.id.income_txt_result)
mBankDatabase= FirebaseDatabase.getInstance().reference.child("BankData").child(uid.toString())
helper = FirebaseHelper(mBankDatabase)
spinner.setAdapter(
activity?.let {
ArrayAdapter<String>(
it,
android.R.layout.simple_list_item_1,
helper.retrieve()
)
}
)
recyclerView=myview.findViewById(R.id.incomeRecyclerView)
val layoutManager = LinearLayoutManager(activity)
layoutManager.reverseLayout = true
layoutManager.stackFromEnd = true
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = layoutManager
mIncomeDatabase.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
var totalvalue :Int = 0
for (ds in dataSnapshot.children) {
val data: Data = ds.getValue<Data>(Data::class.java)!!
totalvalue+=data.amount
val stTotalValue:String = valueOf(totalvalue)
incomeTotal.text = stTotalValue+".00"
}
}
override fun onCancelled(databaseError: DatabaseError) {}
})
return myview
}
private fun insertBank() {
val bankdialog = AlertDialog.Builder(activity)
val inflater = LayoutInflater.from(activity)
val view: View = inflater.inflate(R.layout.add_bank_layout, null)
bankdialog.setView(view)
val dialog = bankdialog.create()
dialog.setCancelable(false)
val BankName = view.findViewById<EditText>(R.id.account_bank)
val BankAc = view.findViewById<EditText>(R.id.account_number)
val btnSaveBank = view.findViewById<Button>(R.id.btn_save_bank)
val btnCancelBank = view.findViewById<Button>(R.id.btn_cancel_bank)
btnSaveBank.setOnClickListener {
val name = BankName.text.toString().trim()
val ac = BankAc.text.toString().trim()
if (TextUtils.isEmpty(name)) {
BankName.error = "Required Field..."
return#setOnClickListener
}
if (TextUtils.isEmpty(ac)) {
BankAc.error = "Required Field..."
return#setOnClickListener
}
val bankacno = ac.toInt()
val id: String? = mBankDatabase.push().key
val mDate: String = DateFormat.getDateInstance().format(Date())
val bankdata = EmiData(bankacno, name, id, mDate)
mBankDatabase.child(id.toString()).setValue(bankdata)
Toast.makeText(activity, "DATA ADDED", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
btnCancelBank.setOnClickListener {
dialog.dismiss()
}
dialog.show()
}
override
fun onStart() {
super.onStart()
val options = FirebaseRecyclerOptions.Builder<Data>()
.setQuery(mIncomeDatabase, Data::class.java)
.setLifecycleOwner(this)
.build()
val firebaseRecyclerAdapter: FirebaseRecyclerAdapter<Data, MyViewHolder> =
object : FirebaseRecyclerAdapter<Data, MyViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.income_recycler_data, parent, false)
)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int, model: Data) {
holder.setType(model.type)
holder.setAmount(model.amount)
holder.setDate(model.date)
holder.setNote(model.note)
holder.mView.setOnClickListener {
post_key= getRef(position).key.toString()
type=model.type
note=model.note
amount=model.amount
updateDataItem()
}
}
}
recyclerView.adapter = firebaseRecyclerAdapter
}
class MyViewHolder(itemView: View) : ViewHolder(itemView) {
val mView = itemView
fun setType(type: String) {
val mType = mView.findViewById<TextView>(R.id.type_txt_income)
mType.text = type
}
fun setNote(note: String) {
val mNote = mView.findViewById<TextView>(R.id.note_txt_income)
mNote.text = note
}
fun setDate(date: String) {
val mDate = mView.findViewById<TextView>(R.id.date_txt_income)
mDate.text = date
}
fun setAmount(amount: Int) {
val mAmount = mView.findViewById<TextView>(R.id.amount_txt_income)
val stAmount = valueOf(amount)
mAmount.text = stAmount
}
}
private fun updateDataItem() {
val mydialog = AlertDialog.Builder(activity)
val inflater = LayoutInflater.from(activity)
val myView: View = inflater.inflate(R.layout.update_data_item, null)
mydialog.setView(myView)
val dialog = mydialog.create()
val editAmount = myView.findViewById<EditText>(R.id.amount_edit)
val editType = myView.findViewById<EditText>(R.id.type_edit)
val editNote = myView.findViewById<EditText>(R.id.note_edit)
//Set data to edit text..
editType.setText(type)
editType.setSelection(type.length)
editNote.setText(note)
editNote.setSelection(note.length)
editAmount.setText(valueOf(amount))
editAmount.setSelection(valueOf(amount).length)
val btnUpdate = myView.findViewById<Button>(R.id.btn_update)
val btnDelete = myView.findViewById<Button>(R.id.btn_delete)
btnUpdate.setOnClickListener {
type = editType.text.toString().trim()
note = editNote.text.toString().trim()
var mdAmount = amount.toString()
mdAmount = editAmount.text.toString().trim { it <= ' ' }
val myAmount = mdAmount.toInt()
val mDate: String = DateFormat.getDateInstance().format(Date())
val data = Data(myAmount, type, note, post_key, mDate)
mIncomeDatabase.child(post_key).setValue(data)
dialog.dismiss()
}
btnDelete.setOnClickListener {
mIncomeDatabase.child(post_key).removeValue()
dialog.dismiss()
}
dialog.show()
}
}
This is my firebase helper:
class FirebaseHelper(val db: DatabaseReference) {
private var saved: Boolean? = null
//SAVE
fun save(bankData: BankData?): Boolean? {
saved = if (bankData == null) {
false
} else {
try {
db.child("name").push().setValue(bankData)
true
} catch (e: DatabaseException) {
e.printStackTrace()
false
}
}
return saved
}
//READ
fun retrieve(): ArrayList<String> {
val bankNames = ArrayList<String>()
db.addChildEventListener(object : ChildEventListener {
override fun onChildAdded(
dataSnapshot: DataSnapshot,
s: String?
) {
fetchData(dataSnapshot, bankNames)
}
override fun onChildChanged(
dataSnapshot: DataSnapshot,
s: String?
) {
fetchData(dataSnapshot, bankNames)
}
override fun onChildRemoved(dataSnapshot: DataSnapshot) {}
override fun onChildMoved(
dataSnapshot: DataSnapshot,
s: String?
) {
}
override fun onCancelled(databaseError: DatabaseError) {}
})
return bankNames
}
private fun fetchData(
snapshot: DataSnapshot,
bankData: ArrayList<String>
) {
bankData.clear()
val name: BankData? = snapshot.getValue(BankData::class.java)
bankData.add(bankData.toString())
}
}
Your spinner shows no value because the data is not retrieved yet from the DB.
So what you should do is create a callback from FirebaseHelper to IncomeFragment
First, create a callback
interface FirebaseHelperCallback {
fun dataRetrievedFromDB(data: List<String>)
}
Second, create a variable, setter function and call dataRetrievedFromDB function in FirebaseHelper class
class FirebaseHelper(val db: DatabaseReference) {
private lateinit var firebaseHelperCallback: FirebaseHelperCallback
fun setFirebaseHelperCallback(firebaseHelperCallback: FirebaseHelperCallback) {
this.firebaseHelperCallback = firebaseHelperCallback
}
...
private fun fetchData(snapshot: DataSnapshot, bankData: ArrayList<String>) {
bankData.clear()
val name: BankData? = snapshot.getValue(BankData::class.java)
bankData.add(bankData.toString())
firebaseHelperCallback.dataRetrievedFromDB(bankData)
}
}
Lastly, implement the callback in IncomeFragment
class IncomeFragment: Fragment(), FirebaseHelperCallback {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
...
helper.setFirebaseHelperCallback(this)
}
...
override fun dataRetrievedFromDB(data: List<String>) {
spinner.setAdapter(
activity?.let {
ArrayAdapter<String>(
it,
android.R.layout.simple_list_item_1,
data
)
}
)
}
}
From the Android app, I wrote for I want to add, add a button in the toolbar that acts as a toggle. When the toggle is disabled (the default state) all posts should be shown, when it is enabled (after a tap) the list should only show the posts having user_id set to 1 and sorted by descending published_at. Tapping on the button again will return it to its default state.
Note that publishedAt returning date and publishedAt and user_id coming from postList from the server I want to know how can I implement above requirement what kind of steps should I have to follow
below my logic implementation in MainActivity.kt
class MainActivity : AppCompatActivity() {
#Inject
lateinit var restInterface: RestInterface
private fun initializeDagger() = App.appComponent.inject(this)
var context: Context? = null
private var filteredList: List<Post>? = null
private var recyclerView: RecyclerView? = null
private var switch1: Switch? = null
private var restAdapter: RestAdapter? = null
private var postList: List<Post>? = null
private var restList: RestList? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initializeDagger()
recyclerView = findViewById(R.id.recycler_view)
switch1 = findViewById(R.id.switch1)
fetchPosts()
switch1.setOnclickListener {
postList.forEach { postItem: Post ->
if (postItem.userId == 1)
filteredList.add(postItem)
}
recyclerView.post = filteredList
recyclerView.notifyDatasetChanged()
}
// Collections.sort( filteredList.get(4).publishedAt, Collections.reverseOrder());
}
private fun fetchPosts() {
val progress = ProgressDialog(this)
progress.setMessage("Loading... ")
progress.isIndeterminate = true
progress.show()
restInterface?.getPosts?.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : DisposableSingleObserver<Response<RestList>>() {
override fun onSuccess(response: Response<RestList>) {
restList = response.body()
val layoutManager = LinearLayoutManager(applicationContext)
recyclerView?.layoutManager = layoutManager
// initialize postList with posts
postList = restList?.posts
restAdapter = postList?.let { RestAdapter(it, restList) }
recyclerView?.adapter = restAdapter
}
override fun onError(e: Throwable) {
progress.dismiss()
Toast.makeText(context, "" + e.message, Toast.LENGTH_SHORT).show()
}
})
}
}
below my RestAdapter.kt
class RestAdapter(val post: List<Post>,val restList: RestList?) : RecyclerView.Adapter<RestAdapter.PostHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.post_list, null)
return PostHolder(itemView)
}
override fun getItemCount(): Int {
return post.size
}
override fun onBindViewHolder(holder: PostHolder, position: Int) {
val posts = post[position]
Picasso
.get() // give it the context
.load(posts.image) // load the image
.into(holder.postImage)
holder.userId.text = posts.userId.toString()
holder.postTitle.text = posts.title
holder.postTime.text = posts.publishedAt
holder.postDescription.text = posts.description
}
class PostHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val postImage: ImageView = itemView.findViewById(R.id.postImage)
val userId: TextView = itemView.findViewById(R.id.userId)
val postTitle: TextView = itemView.findViewById(R.id.postTitle)
val postTime: TextView = itemView.findViewById(R.id.postTime)
val postDescription: TextView = itemView.findViewById(R.id.postDescription)
}
}
You can use method .sortedByDescending or .sortedBy on list :)
[Edit]
I give you a simple solution for that, maybe not the best but should work
1. Change RestAdapter constructor to (var post: List,val restList: RestList?)
Add method to updateData in apadapter:
fun filterData(isChecked: Boolean) {
if (isChecked) {
val filteredList = arrayListOf<Post>()
filteredList.addAll(restList?posts.filter { it.user_id == 1 }.sortedByDescending { it.published_at })
post = filteredList
} else {
post = restList?.posts
}
notifyDatasetChanged()
}
And in your class use this:
switch1.setOnclickListener {
restAdapter?.filterData(switch1.isChecked()//state of your switch i.e isChecked() which return true or false)
}
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'