I am trying to search through a recycler view with cards by allowing a user to search. When the user searches, the cards should "reorganize" to show according to the characters entered by the user. I have tried to do this but am having issues doing this. Any assistance is appreciated.
MainActivity.kt
class MainActivity : AppCompatActivity(), BottomSheetRecyclerViewAdapter.ListTappedListener {
private var customAdapter: CustomAdapter? = null
private var arrayListModel = ArrayList<Model>()
private lateinit var bottomSheetBehavior: CustomBottomSheetBehavior<ConstraintLayout>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val modelList = readFromAsset()
val adapterList = CustomAdapter(modelList, this)
customAdapter = CustomAdapter(arrayListModel, this#MainActivity)
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout) as CustomBottomSheetBehavior
recyclerView.adapter = adapterList
recyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
et_search.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (!s.isNullOrEmpty()) {
val searchList = ArrayList<Model>()
for (i in arrayListModel.indices) {
if (arrayListModel[i].name.toLowerCase().contains(s)) {
searchList.add(arrayListModel[i])
}
}
try {
customAdapter?.notifyDataSetChanged()
recyclerView.adapter = CustomAdapter(searchList, this#MainActivity)
} catch (e: Exception) {
e.printStackTrace()
}
} else {
customAdapter?.notifyDataSetChanged()
recyclerView.adapter = customAdapter
}
}
})
}
override fun onClickList(text: String) {
}
private fun readFromAsset(): List<Model> {
val modeList = mutableListOf<Model>()
val bufferReader = application.assets.open("android_version.json").bufferedReader()
val json_string = bufferReader.use {
it.readText()
}
val jsonArray = JSONArray(json_string);
for (i in 0..jsonArray.length() - 1) {
val jsonObject: JSONObject = jsonArray.getJSONObject(i)
val model = Model(jsonObject.getString("name"), jsonObject.getString("version"))
modeList.add(model)
}
return modeList
}
}
I might found your problem. Here you getting data val modelList = readFromAsset() but you are never assigning data to arrayListModel that your problem.
Assign the data to arrayListModel
val modelList = readFromAsset()
arrayListModel=modelList
Here's a clean approach you might want to consider/try:
Make your adapter implement the filterable interface.
Provide your own Filter object in which you implement your filtering logic (asynch).
You might as well use a SearchView instead of using onTextChange on an EditText.
So: onTextChange(newText) => call adapter.getFilter().filter(newText) => filtering happens in background (filter method performFiltering is called) => when filtered list ready (filter method publishResults is called), you push it to your adapter and notifyDataSetChanged.
Hope this helps.
Here's a clean example on how to implement this:
Example
Related
I can't seem to get my driver to update through my app. I have tried but with no luck. I have a possible solution but don't know what the adapter is that needs to be used. This data all happens in my MainActivity. When the new spinner item is selected. I pass the driver through to do new calculations with the correct DRIVER data.
My spinner class:
private fun Spinner( tripsheetlist: ArrayList<DataModel>): String {
var driverlist = tripsheetlist.distinctBy { it.DRIVER }
var driver : String = driverlist[1].DRIVER //this will be the default first picked driver
spnDriver.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
driver = driverlist[p2].DRIVER.toString()
Toast.makeText(this#MainActivity, "Driver $driver selected", Toast.LENGTH_SHORT).show()
myAdapter.notifyItemChanged(p2);
// myAdapter.notifyDataSetChanged()
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
return driver
}
Calling class:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerViewTripsheetlist.layoutManager = LinearLayoutManager(this)
val spnDriver: Spinner = findViewById(R.id.spnDriver)
var adapter = ArrayAdapter<String>(this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item)
conn(adapter, spnDriver)
}
private fun conn(adapter: ArrayAdapter<String>, spnDriver: Spinner) {
var tripsheetlist = ArrayList<DataModel>()
//populated by sql - removed for clarity
populatespinner(tripsheetlist, adapter)
var driver : String = Spinner(tripsheetlist)
var driverList : ArrayList<DataModel> = datafilter(tripsheetlist, driver)
tripsheetlist = driverList
weightsum(tvTotalweight, tripsheetlist)
totaldelNotes(tvTotaldelv,tripsheetlist)
runOnUiThread {
recyclerViewTripsheetlist.adapter = TableViewAdapter(tripsheetlist, driver, tvHeader, adapter)
}
}
Thank you for all and any help.
When I try to fill a ListView using a custom adapter, I get an empty list, I can't figure out what the error is? Why is everything loaded and displayed when using the default adapter?
HeroesAdapter.kt
class HeroesAdapter(context: Context, heroes: List<TestHero>): BaseAdapter() {
private val context = context
private val heroes = heroes
override fun getCount(): Int {
return heroes.count()
}
override fun getItem(position: Int): Any {
return heroes[position]
}
override fun getItemId(position: Int): Long {
return 0
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
// categoryView = LayoutInflater.from(context).inflate(R.layout.activity_heroes, null)
val categoryView = LayoutInflater.from(context).inflate(R.layout.activity_heroes, parent, false)
// val categoryImage: ImageView = categoryView.findViewById(R.id.heroesImageView)
val categoryText: TextView = categoryView.findViewById(R.id.textHeroView)
val category = heroes[position]
categoryText.text = category.global.name
return categoryView
}
}
HeroesActivity
class HeroesActivity : AppCompatActivity() {
lateinit var adapter : ArrayAdapter<TestHero>
lateinit var adapt: ArrayAdapter<String>
lateinit var heroesAdapt : HeroesAdapter
var listHero = ArrayList<TestHero>()
private val TAG = "HeroesActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_heroes)
// val adapterr = ArrayAdapter(this, android.R.layout.simple_list_item_1,)
adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1,
LinkedList<TestHero>())
// heroesListView.adapter = adapter
adapt = ArrayAdapter(this, android.R.layout.simple_list_item_1,
LinkedList<String>())
//heroesListView.adapter = adapt
getCurrentData()
heroesAdapt = HeroesAdapter(this, listHero)
heroesListView.adapter = heroesAdapt
}
private fun getCurrentData() {
val api = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiRequest::class.java)
GlobalScope.launch(Dispatchers.IO) {
val response = api.herList().awaitResponse()
if (response.isSuccessful) {
val data = response.body()!!
Log.d(TAG, data.toString())
withContext(Dispatchers.Main){
// adapter.add(data.global.name)
adapt.add(data.global.platform)
listHero.add(data)
}
}
}
}
}
Looks like you add data to the backing list of the adapter (listHero.add(data)), but you never inform the adapter that its backing data has changed (heroesAdapt.notifyDataSetChanged()).
As an aside, there are some issues with your coroutine. You should not use GlobalScope. Use lifecycleScope instead to avoid leaking network calls and copies of your Activity. Really, you should fetch data in a ViewModel using the ViewModel's scope and expose it via LiveData or SharedFlow so the network call doesn't have to restart if the phone rotates.
I just want to say sorry to my English skill
I've studied the Android Studio and Kotlin these days.
but I'd got a problem on RecyclerViewer and Adapter, for Intent method
work flow chart
this image, this is what i want to do
so i coded the three classes
ShoppingAppActivity.kt, MyAdapter.kt, CartActivity.kt
At ShoppingAppActivity, If I click the itemId ( in the Red box texts)
I make it move to other context(CartActivity)
ShoppingAppActivity working
if i clicked the red box then
cartStatus
go to cart Activity
it worked but already I said, I just want to send only send itemID
covert to String (i will use toString())
SO i tried to use Intent method in ShoppingAppActivity.kt
///PROBLEM PART
adapter?.setOnItemClickListener{
val nextIntent = Intent(this, CartActivity::class.java)
//nextIntent.putExtra("itemID", )
startActivity(nextIntent)
}
///PROBLEM PART
like this but the problem is I don't know what am i have to put the parameter in
nextIntent.putExtra("itemID", )
what should i do?
I think, I should fix MyAdaptor.kt or ShopingAppActivity.kt for this problem.
But in my knowledge, this is my limit. :-(
below
Full Codes of ShoppingAppActivity.kt, MyAdapter.kt, CartActivity.kt
ShoppingAppActivity.kt
class ShoppingAppActivity : AppCompatActivity() {
lateinit var binding: ActivityShoppingAppBinding
private var adapter: MyAdapter? = null
private val db : FirebaseFirestore = Firebase.firestore
private val itemsCollectionRef = db.collection("items")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityShoppingAppBinding.inflate(layoutInflater)
setContentView(binding.root)
updateList()
binding.recyclerViewItems.layoutManager = LinearLayoutManager(this)
adapter = MyAdapter(this, emptyList())
binding.recyclerViewItems.adapter = adapter
///PROBLEM PART
adapter?.setOnItemClickListener{
val nextIntent = Intent(this, CartActivity::class.java)
//nextIntent.putExtra("itemID", )
startActivity(nextIntent)
}
///PROBLEM PART
}
private fun updateList() {
itemsCollectionRef.get().addOnSuccessListener {
val items = mutableListOf<Item>()
for (doc in it) {
items.add(Item(doc))
}
adapter?.updateList(items)
}
}
}
MyAdapter.kt
data class Item(val id: String, val name: String, val price: Int, val cart: Boolean) {
constructor(doc: QueryDocumentSnapshot) :
this(doc.id, doc["name"].toString(), doc["price"].toString().toIntOrNull() ?: 0, doc["cart"].toString().toBoolean() ?: false)
constructor(key: String, map: Map<*, *>) :
this(key, map["name"].toString(), map["price"].toString().toIntOrNull() ?: 0, map["cart"].toString().toBoolean() ?: false)
}
class MyViewHolder(val binding: ItemBinding) : RecyclerView.ViewHolder(binding.root)
class MyAdapter(private val context: Context, private var items: List<Item>)
: RecyclerView.Adapter<MyViewHolder>() {
fun interface OnItemClickListener {
fun onItemClick(student_id: String)
}
private var itemClickListener: OnItemClickListener? = null
fun setOnItemClickListener(listener: OnItemClickListener) {
itemClickListener = listener
}
fun updateList(newList: List<Item>) {
items = newList
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding: ItemBinding = ItemBinding.inflate(inflater, parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = items[position]
val itemID : String
holder.binding.textID.text = item.id
holder.binding.textName.text = item.name
if(item.cart)
{
holder.binding.textCart.text = "in Cart"
}
else
{
holder.binding.textCart.text = ""
}
holder.binding.textID.setOnClickListener {
AlertDialog.Builder(context).setMessage("You clicked ${item.id}.").show()
itemClickListener?.onItemClick(item.id)
}
holder.binding.textName.setOnClickListener {
//AlertDialog.Builder(context).setMessage("You clicked ${student.name}.").show()
itemClickListener?.onItemClick(item.id)
}
//return item.id.toString()
}
override fun getItemCount() = items.size
}
CartActivity.kt
class CartActivity : AppCompatActivity() {
lateinit var binding: ActivityCartBinding
private val db: FirebaseFirestore = Firebase.firestore
private val itemsCollectionRef = db.collection("items")
private var adapter: MyAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityCartBinding.inflate(layoutInflater)
setContentView(binding.root)
updateList()
//binding.recyclerViewItems.layoutManager = LinearLayoutManager(this)
//adapter = MyAdapter(this, emptyList())
//binding.recyclerViewItems.adapter = adapter
binding.changeCartStatus.setOnClickListener{
//change the button's text if the itemID is corrected
//if(){
// binding.changeCartStatus.text = ""
//}
}
}
private fun updateList() {
itemsCollectionRef.get().addOnSuccessListener {
val items = mutableListOf<Item>()
for (doc in it) {
items.add(Item(doc))
}
adapter?.updateList(items)
}
}
}
You just need to implement listener to your activity
class ShoppingAppActivity : AppCompatActivity() ,MyAdapter.OnItemClickListener {
In oncreate add below line after adapter
adapter?.setOnItemClickListener(this)
Then override its method
override fun onItemClick(id: String){
val nextIntent = Intent(this, CartActivity::class.java)
nextIntent.putExtra("itemID",id )
startActivity(nextIntent)
}
I have an array list in kotlin and I want to remove all item from it, leave it as an empty array to start adding new dynamic data. i tried ArrayList.remove(index) arrayList.drop(index) but none works,
The Declaration:
var fromAutoCompleteArray: List<String> = ArrayList()
here is how I try it :
for (item in fromAutoCompleteArray){
fromAutoCompleteArray.remove(0)
}
I'm using the addTextChangedListener to remove old data and add new data based on user's input:
private fun settingToAutoComplete() {
val toAutoCompleteTextView: AutoCompleteTextView =
findViewById<AutoCompleteTextView>(R.id.toAutoCompleteText)
toAutoCompleteTextView.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun afterTextChanged(s: Editable?) {
doLocationSearch(toAutoCompleteTextView.text.toString(), 2)
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
toAutoCompleteTextView.postDelayed({
toAutoCompleteTextView.showDropDown()
}, 10)
}
})
val adapter = ArrayAdapter(this, android.R.layout.select_dialog_item, toAutoCompleteArray)
toAutoCompleteTextView.setAdapter(adapter)
toAutoCompleteTextView.postDelayed({
toAutoCompleteTextView.setText("")
toAutoCompleteTextView.showDropDown()
}, 10)
}
And here is the function that adds the data :
private fun doLocationSearch(keyword: String, fromTo: Number) {
val baseURL = "api.tomtom.com"
val versionNumber = 2
val apiKey = "******************"
val url =
"https://$baseURL/search/$versionNumber/search/$keyword.json?key=$apiKey"
val client = OkHttpClient()
val request = Request.Builder().url(url).build()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: okhttp3.Response) {
val body = response.body?.string()
println("new response is : $body")
val gson = GsonBuilder().create()
val theFeed = gson.fromJson(body, TheFeed::class.java)
if (theFeed.results != null) {
for (item in theFeed.results) {
println("result address ${item.address.freeformAddress} ")
if (fromTo == 1) {
fromAutoCompleteArray = fromAutoCompleteArray + item.address.freeformAddress
println(fromAutoCompleteArray.size)
} else {
toAutoCompleteArray = toAutoCompleteArray + item.address.freeformAddress
}
}
} else {
println("No Locations found")
}
}
override fun onFailure(call: Call, e: IOException) {
println("Failed to get the data!!")
}
})
}
and as you see the line println(fromAutoCompleteArray.size) shows me if it's deleted or not, and it is always encreasing.
Also, tried to use clear() without a loop and none works:
fromAutoCompleteArray.clear()
The List type in Kotlin is not mutable. If you want to cause your list to change, you need to declare it as a MutableList.
I would suggest changing this line:
var fromAutoCompleteArray: List<String> = ArrayList()
To this:
val fromAutoCompleteArray: MutableList<String> = mutableListOf()
And then you should be able to call any of these:
fromAutoCompleteArray.clear() // <--- Removes all elements
fromAutoCompleteArray.removeAt(0) // <--- Removes the first element
I also recommend mutableListOf() over instantiating an ArrayList yourself. Kotlin has sensible defaults and it is a bit easier to read. It will end up doing the same thing either way for the most part.
It is also preferable to use val over var, whenever possible.
Update: Vals not vars, thanks for spotting that Alexey
i dont know how how you declare arraylist but this can be done as following
var arrayone: ArrayList<String> = arrayListOf("one","two","three")
val arraytwo = arrayone.drop(2)
for (item in arraytwo) {
println(item) // now prints all except the first one...
}
in your case try this
val arraytwo = fromAutoCompleteArray.toMutableList().apply {
removeAt(0)
}
I have recyclerview with checkbox and I want to checklist all the data using button. I have trying this tutorial, but when i click the button, the log is call the isSelectedAll function but can't make the checkbox checked. what wrong with my code?
this is my adapter code
var isSelectedAll = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListApproveDeatilViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_list_approve_row, parent, false)
return ListApproveDeatilViewHolder(itemView)
}
private lateinit var mSelectedItemsIds: SparseBooleanArray
fun selectAll() {
Log.e("onClickSelectAll", "yes")
isSelectedAll = true
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: ListApproveDeatilViewHolder, position: Int) {
val approve = dataSet!![position]
holder.soal.text = approve.title
holder.kategori.text = approve.kategori
if (!isSelectedAll){
holder.checkBox.setChecked(false)
} else {
holder.checkBox.setChecked(true)
}
}
and this is my activity code
override fun onCreate(savedInstanceState: Bundle?) {
private var adapter: ListApproveDetailAdapter? = null
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list_approve)
ButterKnife.bind(this)
getData()
// this is my button onclick code
select.setOnClickListener(){
if (select.getText().toString().equals("Select all")){
Toast.makeText(this, "" + select.getText().toString(), Toast.LENGTH_SHORT).show()
adapter?.selectAll()
select.setText("Deselect all")
} else {
Toast.makeText(this, "" + select.getText().toString(), Toast.LENGTH_SHORT).show()
select.setText("Select all")
}
}
}
//this is for get my data for the recyclerview
fun getData() {
val created_by = intent.getStringExtra(ID_SA)
val tgl_supervisi = intent.getStringExtra(TGL_SURVEY)
val no_dlr = intent.getStringExtra(NO_DLR)
API.getListApproveDetail(created_by, tgl_supervisi, no_dlr).enqueue(object : Callback<ArrayList<ListApprove>> {
override fun onResponse(call: Call<ArrayList<ListApprove>>, response: Response<ArrayList<ListApprove>>) {
if (response.code() == 200) {
tempDatas = response.body()
Log.i("Data Index History", "" + tempDatas)
recyclerviewApprove?.setHasFixedSize(true)
recyclerviewApprove?.layoutManager = LinearLayoutManager(this#ListApproveActivity)
recyclerviewApprove?.adapter = ListApproveDetailAdapter(tempDatas)
adapter?.notifyDataSetChanged()
} else {
Toast.makeText(this#ListApproveActivity, "Error", Toast.LENGTH_LONG).show()
}
swipeRefreshLayout.isRefreshing = false
}
override fun onFailure(call: Call<ArrayList<ListApprove>>, t: Throwable) {
Toast.makeText(this#ListApproveActivity, "Error", Toast.LENGTH_SHORT).show()
swipeRefreshLayout.isRefreshing = false
}
})
}
thankyou for any help :)
I am posting the answer with implementation of demo project. I haven't modified your code but as per your requirement i have done this.
MainActivity class:
class MainActivity : AppCompatActivity() {
var selectAll: Boolean = false;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView) as RecyclerView
val btnSelectAll = findViewById<Button>(R.id.btnSelectAll) as Button
//adding a layoutmanager
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
//crating an arraylist to store users using the data class user
val users = ArrayList<User>()
//adding some dummy data to the list
users.add(User("Piyush", "Ranchi"))
users.add(User("Mehul", "Chennai"))
users.add(User("Karan", "TamilNadu"))
users.add(User("Bela", "Kolkata"))
//creating our adapter
val adapter = CustomAdapter(users, selectAll)
//now adding the adapter to recyclerview
recyclerView.adapter = adapter
btnSelectAll.setOnClickListener {
if (!selectAll) {
selectAll = true
} else {
selectAll = false
}
adapter?.selectAllCheckBoxes(selectAll)
}
}
}
User class:
data class User(val name: String, val address: String)
Adapter class:
class CustomAdapter(val userList: ArrayList<User>, val selectAll: Boolean) :
RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
var selectAllA = selectAll;
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_layout, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: CustomAdapter.ViewHolder, position: Int) {
holder.textViewName.text = userList[position].name;
if (!selectAllA){
holder.checkBox.setChecked(false)
} else {
holder.checkBox.setChecked(true)
}
}
//this method is giving the size of the list
override fun getItemCount(): Int {
return userList.size
}
//the class is hodling the list view
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textViewName = itemView.findViewById(R.id.textViewUsername) as TextView
val checkBox = itemView.findViewById(R.id.checkbox) as CheckBox
}
fun selectAllCheckBoxes(selectAll: Boolean) {
selectAllA = selectAll
notifyDataSetChanged()
}
}
As i already mentioned in comments you are using two different adapter instance .
Now i see you have declared adapter globally .
Just modify your code as follows and make sure response.body() have data int it :
if (response.code() == 200) {
tempDatas = response.body()
Log.i("Data Index History", "" + tempDatas)
recyclerviewApprove?.setHasFixedSize(true)
recyclerviewApprove?.layoutManager = LinearLayoutManager(this#ListApproveActivity)
adapter = ListApproveDetailAdapter(tempDatas)
recyclerviewApprove?.adapter=adapter
} else {
Toast.makeText(this#ListApproveActivity, "Error", Toast.LENGTH_LONG).show()
}
Add one variable in model class.
like var isSelect : Boolean
In your selectAll() method update adpter list and notify adapter.
Edit:
in the adapter class.
if (approve.isSelect){
holder.checkBox.setChecked(true)
} else {
holder.checkBox.setChecked(false)
}
Hope this may help you.
OR
If you are using AndroidX then use should use one recyclerview features.
androidx.recyclerview.selection
A RecyclerView addon library providing support for item selection. The
library provides support for both touch and mouse driven selection.
Developers retain control over the visual representation, and the
policies controlling selection behavior (like which items are eligible
for selection, and how many items can be selected.)
Reference from here