I have the following:
Main Activity with RecyclerView which is filled from Array List
Adapter (of course)
Another Activity where a new item should be created, added to RecyclerView on Main Activity and then be closed
The main question is, how can I add an item to RecyclerView from another Activity?
MainActivity Code
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val items = arrayListOf<CustomData>()
items.add(CustomData("Name", R.drawable.icon, 50, "Weekly", "Monday", 1, 50))
button.setOnClickListener {
val intent = Intent(this, NewItem::class.java)
startActivity(intent)
}
itemsListView.apply {
layoutManager = LinearLayoutManager(this#MainActivity)
adapter = ItemsAdapter(items)
}
}
Adapter Code
class ItemsAdapter(private val items: ArrayList<CustomData>) :
RecyclerView.Adapter<ItemsAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.wish_row, parent, false)
val holder = ViewHolder(view)
view.setOnClickListener {
val intent = Intent(parent.context, ItemView::class.java)
intent.putExtra("Name", items[holder.adapterPosition].name)
intent.putExtra("Price", items[holder.adapterPosition].price)
intent.putExtra("Icon", items[holder.adapterPosition].image)
parent.context.startActivity(intent)
}
return holder
}
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ItemsAdapter.ViewHolder, position: Int) {
holder.name.text = items[position].name
holder.price.text = items[position].price.toString()
holder.image.setImageDrawable(holder.image.context.getDrawable(items[position].image))
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val image: ImageView = itemView.findViewById(R.id.imageView)
val name: TextView = itemView.findViewById(R.id.textView)
val price: TextView = itemView.findViewById(R.id.textView2)
}
}
Another Activity from which I want to add item to Recyclerview
class NewItem : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.new_item_activity)
val items = arrayListOf<CustomData>()
items.add(CustomData("Name2", R.drawable.icon, 50, "Weekly", "Monday", 2, 50))
newItemButton.setOnClickListener {
itemsListView.layoutManager = LinearLayoutManager(this)
itemsListView.adapter = itemsAdapter(items)
finish()
}
}
}
When adding new item from your NewItem activity, pass a bundle adding that item to your MainActivity.
Then in your MainActivity, get that bundle and set that new item to your RecyclerView.
Related
I have got the following for setting the background color for a single item when clicking on it:
// Get the listView on the Home Fragment
val contactList = findViewById<ListView>(R.id.listViewContacts)
// Create IDs for each element
val id : Int = R.id.txtListElement
// Fill the ListView with the custom contact_list_template
arrayAdapter = ArrayAdapter(this,
R.layout.contact_list_template, id, contactSimple)
contactList.adapter = arrayAdapter
contactList.onItemClickListener = AdapterView.OnItemClickListener { adapterView, view,
position, _ ->
val selectedItem = adapterView.getItemAtPosition(position) as String
val itemIdAtPos = adapterView.getItemIdAtPosition(position)
view.setBackgroundColor(ContextCompat.getColor(this, R.color.purple_200))
...
}
But how can I set the color for every object inside the ListView?
I tried
contactList.setBackgroundColor(ContextCompat.getColor(this, R.color.white)) but that does not seem to work.
Try to define your custom BaseAdapter for the ListView. (I assume that the contact_list_template XML contains a TextView with id title).
class ListViewContactAdapter(
private val contactList: List<String>
) : BaseAdapter() {
private val lastClicked = ""
override fun getCount(): Int = contactList.size
override fun getItem(position: Int): String = contactList[position]
override fun getItemId(position: Int): Long = position.toLong()
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val view = convertView ?: LayoutInflater.from(parent.context).inflate(R.layout.contact_list_template, parent, false)
val holder = ViewHolderContact(view)
val contact = getItem(position)
holder.title.text = contact
if (contact == lastClicked) {
// Selected background
view.setBackgroundColor(ContextCompat.getColor(this, ...))
} else {
// Unselected background
view.setBackgroundColor(ContextCompat.getColor(this, ...))
}
view.setOnClickListener {
lastClicked = contact
notifyDataSetChanged()
}
return view
}
class ViewHolderContact(itemView: View) {
val title = itemView.findViewById<TextView>(R.id.title)
}
}
You can then set the adapter of the list with:
val contactList = findViewById<ListView>(R.id.listViewContacts)
contactList.adapter = ListViewContactAdapter(contactSimple)
I have two scenario to highlight the item in recyclerview first one is when user click on any item so that clicked item should be highlighted and and second scenario is when last or recent item added into the list that time that last added item should be highlighted.
So my first scenario(onClick) is working fine but problem is in my second scenario so whenever I'm adding item into the list so two items are highlighted. Please suggest me where I am wrong. and one more thing that recent or last item should be perform auto click also. so How can I achieve it.
class CarItemAdapter(
private val activity: MainActivity,
private val carList: ArrayList<CarEntity>,
private val carViewModel: CarViewModel,
private val settings: Settings
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var listener: OnItemsClickListener? = null
var rowIndex = 100
var listItemCount = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
return when (viewType) {
VIEW_TYPE_ADDITEM -> ViewHolderAddCarItem(SettingsCarAddItemTileBinding.inflate(layoutInflater, parent, false))
VIEW_TYPE_ITEM -> ViewHolderCarItem(SettingsCarItemTileBinding.inflate(layoutInflater, parent, false))
else -> throw IllegalArgumentException("Invalid view type")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ViewHolderCarItem -> holder.bind(carList[position -1], position-1)
is ViewHolderAddCarItem -> holder.bind()
}
}
inner class ViewHolderAddCarItem internal constructor(private var binding: SettingsCarAddItemTileBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind() {
binding.lifecycleOwner = activity
binding.handler = EventHandler()
binding.userSpecificCarSubtitle.setText(R.string.user_specific_car_subtitle)
}
inner class EventHandler {
fun onItemClicked() {
activity.showSubPage(Car360AddVehiclePage(activity), true)
}
}
}
inner class ViewHolderCarItem internal constructor(private var binding: SettingsCarItemTileBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(carEntity: CarEntity, position: Int) {
Timber.d("Data store position %s %s", position, carList.size)
listItemCount = itemCount
binding.lifecycleOwner = activity
val bmp = BitmapFactory.decodeByteArray(carEntity.image, 0, Objects.requireNonNull<ByteArray>(carEntity.image).size)
binding.carImage.setImageBitmap(bmp)
binding.carName.text = carEntity.label
binding.layoutCar.setOnClickListener {
listItemCount = 100
listener?.onItemClick(carEntity)
settings.finNumber = carEntity.finNumber
settings.selectedCarType = activity.getString(R.string.settings_category_view_sub_title_for_user_specific_car)
settings.isCarSelected = true
rowIndex = position
notifyDataSetChanged()
}
binding.deleteButton.setOnClickListener {
GlobalScope.launch(Dispatchers.IO) { carViewModel.deleteByFinNumber(carEntity.finNumber) }
carList.removeAt(position)
notifyDataSetChanged()
}
if(position == listItemCount-2){
binding.layoutCar.setBackgroundColor(activity.getColor(R.color.petrol))
binding.carName.setTextColor(activity.getColor(R.color.white))
}else{
binding.layoutCar.setBackgroundColor(activity.getColor(R.color.coolGray))
binding.carName.setTextColor(activity.getColor(R.color.coolGray_80k))
}
if (rowIndex == position) {
binding.layoutCar.setBackgroundColor(activity.getColor(R.color.petrol))
binding.carName.setTextColor(activity.getColor(R.color.white))
}
}
}
override fun getItemViewType(position: Int): Int {
return if (position == 0 && position <= carList.size) {
VIEW_TYPE_ADDITEM
} else VIEW_TYPE_ITEM
}
override fun getItemCount(): Int {
return carList.size + 1
}
interface OnItemsClickListener {
fun onItemClick(carEntity: CarEntity)
}
fun setWhenClickListener(listener: OnItemsClickListener) {
this.listener = listener
}
companion object {
const val VIEW_TYPE_ITEM = 0
const val VIEW_TYPE_ADDITEM = 1
}
}
MyFragmentSubPage : where I'm adding data into the list
if (isDialogVisible) {
Dialog imageDownloadCheckDialog = Dialog.createProgressBarDialog(getContext(), R.string.image_progressbar_title);
imageDownloadCheckDialog.setCancelable(false);
imageDownloadCheckDialog.show();
carViewModel.getGetDismissDialog().observe(getActivity(), isDismissDialog -> {
if (isDismissDialog) {
imageDownloadCheckDialog.dismiss();
carViewModel.dismissDialog(false);
new Thread(() -> {
CarEntity carEntity = carViewModel.getImageFor340Degree(vinNumber, "340");
// Here I am adding data into the list
recyclerImageList.add(carEntity);
}).start();
}
});
}
RecyclerView recyclerView = binding.getRoot().findViewById(R.id.list);
if (recyclerView != null) {
layoutManager = new GridLayoutManager(getContext(), 2);
recyclerView.setLayoutManager(layoutManager);
}
carItemAdapter = new CarItemAdapter(getActivity(), recyclerImageList, eventHandler, carViewModel, settings);
assert recyclerView != null;
recyclerView.setAdapter(carItemAdapter);
recyclerView.getRecycledViewPool().setMaxRecycledViews(carItemAdapter.VIEW_TYPE_ITEM, 50);
First make a CarEntity var inside your adapter called selectedCar.
Make a function inside your adapter called submitItem
fun submitItem(car: CarEntity) {
selectedCar = car //set the selected car to the new car
notifyItemChanged(rowIndex)//rebind old selected car
carList.add(car) //add new car to list
rowIndex = carList.size() - 1 //set current selected index
notifyItemInserted(rowIndex)//notify the change at selected index
}
And your dialog
new Thread(() -> {
CarEntity carEntity = carViewModel.getImageFor340Degree(vinNumber, "340");
// Here I am adding data into the list
requireActivity().runOnUiThread{
carItemAdapter.submitItem(carEntity);
}
}).start();
and your button
binding.layoutCar.setOnClickListener {
listItemCount = 100
listener?.onItemClick(carEntity)
settings.finNumber = carEntity.finNumber
settings.selectedCarType = activity.getString(R.string.settings_category_view_sub_title_for_user_specific_car)
selectedCar = carEntity //set the select car on click
notifyItemChanged(rowIndex) //notify old row has changed
notifyItemChanged(position) //notify new row has changed
rowIndex = position // set current selected row
}
and on bind to set selected view stuff
if(carEntity == selectedCar){
//highlight row
}
I have created a custom spinner adapter which returns two views (item_new_area & item_area), item_new_area is static with an option Add New and item_area populated dynamic.
When Add new is selected,I want to launch the dialog for add new area but i keep on getting an error that lateinit property listener has not been initialized
Custom Spinner Adapter
class AreaSpinnerAdapter(private val inflater: LayoutInflater) : BaseAdapter() {
var areas: List<AreaEntity>? = null
lateinit var listener: OnItemSelectedListener
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
return when (getItemViewType(position)) {
1 -> {
val binding: ItemNewAreaBinding = DataBindingUtil.inflate<ItemNewAreaBinding>(
inflater,
R.layout.item_new_area,
parent, false
).also {
it.root.tag = it
}
binding.addNew.setOnClickListener {
listener.onClick()
}
binding.root
}
else -> {
val binding: ItemAreaBinding = DataBindingUtil.inflate<ItemAreaBinding>(
inflater,
R.layout.item_area,
parent, false
).also {
it.root.tag = it
}
binding.area = getItem(position)
binding.root
}
}
}
override fun getItemViewType(position: Int): Int {
return if (position == 1) {
1
} else {
2
}
}
fun setOnItemClickListener(listener: OnItemSelectedListener) {
this.listener = listener
}
interface OnItemSelectedListener {
fun onClick()
}
}
I try to set the on Item Clicked listener in the onCreateView method of my fragment:
val spinnerAdapter = AreaSpinnerAdapter(layoutInflater)
spinnerAdapter.setOnItemClickListener(object :
AreaSpinnerAdapter.OnItemSelectedListener {
override fun onClick() {
addNewAreaDialog()
}
})
binding?.spinnerArea?.adapter = spinnerAdapter
Error Log
kotlin.UninitializedPropertyAccessException: lateinit property listener has not been initialized
at com.test.adapter.AreaSpinnerAdapter.getListener(AreaSpinnerAdapter.kt:15)
at com.test.adapter.AreaSpinnerAdapter$getView$1.onClick(AreaSpinnerAdapter.kt:50)
line 15 = lateinit var listener: OnItemSelectedListener
line 25 = listener.onClick()
I know that am supposed to initialize the listener before i call getView method but for some reasons am failing to initialize the listener, Any help on how to do it shall be be greatly appreciated. Thanks
You need to move the listener AreaSpinnerAdapter to the constructor.
class AreaSpinnerAdapter(
private val inflater: LayoutInflater,
private val listener: OnItemSelectedListener
) : BaseAdapter() {
var areas: List<AreaEntity>? = null
and then call it like
val spinnerAdapter = AreaSpinnerAdapter(layoutInflater,
object: AreaSpinnerAdapter.OnItemSelectedListener {
override fun onClick() {
addNewAreaDialog()
}
})
I have recyclerview in fragment. When I click the recyclerview item, background color changes. But when I go to another activity and go back, pressed item color stayed.
I have called adapter.notifyDataSetChanged(), but nothing happened.
Here is my RecyclerView.ViewHolder class
class GreetingsView(itemView: View) : RecyclerView.ViewHolder(itemView) {
var imageView: ImageView? = null
var category: TextView? = null
var frameLayout: FrameLayout? = null
var context: Context? = itemView.context
private var listOfGreetings: List<GreetingModel>? = null
init {
imageView = itemView.findViewById(R.id.greetingImage)
category = itemView.findViewById(R.id.greetingCategory)
frameLayout = itemView.findViewById(R.id.card_view)
this.listOfGreetings = listOfGreetings
itemView.setOnClickListener {
val position = adapterPosition
if (position != RecyclerView.NO_POSITION) {
frameLayout?.setBackgroundColor(Color.BLACK)
val intent = Intent(context, GridGreetingListActivity::class.java)
intent.putExtra("position", position)
context?.startActivity(intent)
}else {
frameLayout?.setBackgroundColor(Color.WHITE)
}
}
}
}
Here is my Fragment
class CategoryFragment : Fragment(), SendItemPositionListener {
var greetingAdapter: GreetingAdapter? = null
var listOfGreetings: ArrayList<GreetingModel>? = arrayListOf()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
listOfGreetings?.apply {
add(0, GreetingModel("Greeting 1", R.drawable.img_1))
add(1,
GreetingModel("Greeting 2", R.drawable.greeting_1)
)
add(2, GreetingModel("Greeting 3", R.drawable.img_3))
add(3, GreetingModel("Greeting 4", R.drawable.img_4))
add(4, GreetingModel("Greeting 5", R.drawable.img_5))
add(5, GreetingModel("Greeting 6", R.drawable.img_6))
add(6, GreetingModel("Greeting 7", R.drawable.img_7))
add(7, GreetingModel("Greeting 8", R.drawable.img_8))
}
val view = inflater.inflate(R.layout.fragment_category, container, false)
//Get the recyclerView from the inflated view.
val recyclerView = view.findViewById<RecyclerView>(R.id.recyclerView)
val manager = LinearLayoutManager(activity)
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = manager
greetingAdapter = GreetingAdapter(activity!!, listOfGreetings!!.toList())
recyclerView.adapter = greetingAdapter
greetingAdapter!!.notifyDataSetChanged()
return view
}
I am not sure that I am calling notifyDataSetChanged() in right place. Hope you will help!
I find the solution of my problem. It is needed to call setOnClickListener on itemView in onBindViewHolder but not in ViewHolder and call greetingAdapter?.notifyDataSetChanged() on onResume() method.
override fun onBindViewHolder(holder: GreetingsView, position: Int) {
holder.category?.text = listOfGreetings?.get(position)?.greetingCategory
holder.cardView?.setBackgroundColor(Color.TRANSPARENT)
Glide
.with(context?.applicationContext!!)
.load(listOfGreetings?.get(position)?.imageId)
.apply(
RequestOptions()
.placeholder(R.drawable.loading)
.fitCenter()
)
.into(holder.imageView!!)
holder.itemView.setOnClickListener {
holder.cardView?.setBackgroundColor(Color.RED)
if (position != RecyclerView.NO_POSITION) {
val intent = Intent(context, GridGreetingListActivity::class.java)
intent.putExtra("position", 1)
context?.startActivity(intent)
}
}
}
class GreetingsView(itemView: View) : RecyclerView.ViewHolder(itemView) {
var imageView: ImageView? = null
var category: TextView? = null
var cardView: FrameLayout? = itemView.findViewById(R.id.card_view)
var context: Context? = itemView.context
private var listOfGreetings: List<GreetingModel>? = null
init {
imageView = itemView.findViewById(R.id.greetingImage)
category = itemView.findViewById(R.id.greetingCategory)
this.listOfGreetings = listOfGreetings
}
}
I am implementing RecyclerView and want to get details of selected items.
I am following a tutorial given here. But I want to get some string key instead of item position.
For example, if i do like this:
for (selection in selectionTracker?.selection!!)
Log.e("tag", selection.toString())
I get indexes as output, but I want to get string keys here.
But I don't know what further changes should be made if I do like this:
fun getItemDetails() = object: ItemDetailsLookup.ItemDetails<Long>() {
override fun getSelectionKey() = itemId
override fun getPosition() = adapterPosition
}
and change it into:
fun getItemDetails() = object: ItemDetailsLookup.ItemDetails<Long>() {
override fun getSelectionKey() = myData[adapterPosition].myKey // a string
override fun getPosition() = adapterPosition
}
----EDIT----
These are my codes:
The adapter class:
class AdapterSubjectsIndexRV (
private val indexList: ArrayList<ModelSubjectsIndex>,
val itemClick: (String) -> Unit
): RecyclerView.Adapter<AdapterSubjectsIndexRV.MyViewHolder>() {
// every recycler view item has unique id
init {
setHasStableIds(true)
}
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder.
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val courseValue = itemView.findViewById<TextView>(R.id.course_value)!!
val courseCode = itemView.findViewById<TextView>(R.id.course_code)!!
val courseName = itemView.findViewById<TextView>(R.id.course_name)!!
val progressValue = itemView.findViewById<TextView>(R.id.progress_value)!!
val progressBar = itemView.findViewById<ProgressBar>(R.id.progress_bar)!!
val selectedOverlay = itemView.findViewById<View>(R.id.subjects_card_view_selected)!!
// set click events
init {
itemView.setOnClickListener {
itemClick(courseCode.text.toString())
}
}
// get item details
fun getItemDetails() = object: ItemDetailsLookup.ItemDetails<String>() {
// override fun getSelectionKey() = itemId
override fun getSelectionKey() = indexList[adapterPosition].courseCode
override fun getPosition() = adapterPosition
}
}
// lookup class
class MyItemDetailsLookup(private val rv: RecyclerView): ItemDetailsLookup<String>() {
override fun getItemDetails(e: MotionEvent): ItemDetails<String>? {
val v = rv.findChildViewUnder(e.x, e.y)
return if (v != null) {
(rv.getChildViewHolder(v) as MyViewHolder).getItemDetails()
} else null
}
}
// set tracker
var tracker: SelectionTracker<Long>? = null
// Create new views (invoked by the layout manager)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AdapterSubjectsIndexRV.MyViewHolder {
// create a new view
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.subjects_card, parent, false)
return MyViewHolder(itemView)
}
// Replace the contents of a view (invoked by the layout manager)
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.courseValue.text = indexList[position].courseValue
holder.courseCode.text = indexList[position].courseCode
holder.courseName.text = indexList[position].courseName
holder.progressValue.text = "${indexList[position].progress}%"
holder.progressBar.progress = indexList[position].progress
// Highlight the item if it's selected
holder.selectedOverlay.visibility = if (tracker!!.isSelected(position.toLong())) View.VISIBLE else View.INVISIBLE
}
// Return the size of your data set (invoked by the layout manager)
override fun getItemCount() = indexList.size
override fun getItemId(position: Int): Long {
return position.toLong()
}
}
And this is how I am setting my fragment:
val rvAdapter = AdapterSubjectsIndexRV(getSubjectsIndexList(subjectsList)) {
// if not in selection mode
if (selectionTracker?.selection?.size() == 0) {
listener.showSubjectDetails(it)
}
}
// set recycler view
val recyclerView = theView.findViewById<RecyclerView>(R.id.subjects_recycler_view)
recyclerView.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(activity)
adapter = rvAdapter
}
// set selection tracker
selectionTracker = SelectionTracker
.Builder<Long>(
"subjects_index_selection",
recyclerView,
StableIdKeyProvider(recyclerView),
AdapterSubjectsIndexRV.MyItemDetailsLookup(recyclerView),
StorageStrategy.createLongStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectAnything()
).build()
rvAdapter.tracker = selectionTracker
if(savedInstanceState != null)
selectionTracker?.onRestoreInstanceState(savedInstanceState)
return theView
Change the generic type from <Long> to <String>, then you will be able to return a string key.
fun getItemDetails() = object: ItemDetailsLookup.ItemDetails<String>() {
override fun getSelectionKey() = myData[adapterPosition].myKey
// ...
}
I am a little bit confused by your code, but the best way to get the details is to do it on the `onBindViewHolder method. Call your on click method and then within that do your method to get the string.