Im currently working on an app which has a RecyclerView for the Settings Menu. My objective is to use strings saved in strings.xml for the RecyclerView, because my app has localization;
In my case, i generate the RecyclerView with a list saved in the fragment, which needs strings from string.xml, however, trying to call strings from the xml dosnt work.
How do you pass strings from string.xml as parameters? How can i pass im this scenario?
frgSettingsMain.kt
class frgSettingsMain : Fragment() {
val listsettings = listOf(
//using #string/string here doesnt work!
dataListIcons(#string/..., "Description of option", R.drawable.ic_outline_color_lens_24),
dataListIcons(I WANT THE STRING HERE, "Description of option", R.drawable.ic_outline_dashboard_24),
dataListIcons("Option", "Description of option", R.drawable.ic_outline_image_24),
dataListIcons("Option", "Description of option", R.drawable.ic_outline_volume_up_24),
dataListIcons("Option", "Description of option", R.drawable.ic_outline_library_music_24)
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_settingsmain, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
rvSettingsMain.apply {
layoutManager = LinearLayoutManager(activity)
adapter = adapterSettingsMain(listsettings)
}
}
companion object {
fun newInstance(): frgSettingsMain = frgSettingsMain()
}
}
dataListIcons.kt
data class dataListIcons (
val stringTitle: String,
val stringDescription: String,
val imageIcon: Int
)
adapterSettingsMain.kt
package com.meltixdev.revomusicplayer
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_settingsicon.view.*
class adapterSettingsMain(
var listsettings: List<dataListIcons>
) : RecyclerView.Adapter<adapterSettingsMain.SettingsViewHolder>() {
inner class SettingsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingsViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_settingsicon, parent, false)
return SettingsViewHolder(view)
}
override fun getItemCount(): Int {
return listsettings.size
}
override fun onBindViewHolder(holder: SettingsViewHolder, position: Int) {
holder.itemView.apply {
rvSettingsTitle.text = listsettings[position].stringTitle
rvSettingsDescription.text = listsettings[position].stringDescription
rvSettingsIcon.setImageResource(listsettings[position].imageIcon)
}
}
}
use getString(R.string.stringname)
Edit
will make some changes to your code
data class dataListIcons (
val stringTitle: Int,
val stringDescription: Int,
val imageIcon: Int
)
val listsettings = listOf(
dataListIcons(R.string.title1, R.string.desription1,
R.drawable.ic_outline_color_lens_24),
dataListIcons(R.string.title2, R.string.desription2,
R.drawable.ic_outline_dashboard_24),
.........
)
in your adapter
holder.itemView.apply {
rvSettingsTitle.text = this.context.getString(listsettings[position].stringTitle)
rvSettingsDescription.text = this.context.getString(listsettings[position].stringDescription)
rvSettingsIcon.setImageResource(listsettings[position].imageIcon)
}
you can always pass the string resource ids.
something like below:
fun getList(): List<Int> {
return listOf(R.string.app_name, R.string.app_name, R.string.app_name)
}
and wherever you need to get the string from ids just call
context?.getString(getList()[position])
Related
I implemented a simple recyclerview using databinding using kotlin language.
In xml, recyclerview seems to be the default,
but when I run it, it doesn't show up as default.
Other buttons and views included in the fragment appear normally when executed, but only the recyclerview is not visible. Which part is wrong?
class WorkFragment : Fragment() {
private var _binding: FragmentWorkBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentWorkBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val data = mutableListOf<WorkList>()
val adapter = WorkAdapter()
adapter.data = data
binding.recycler1.adapter = adapter
binding.recycler1.layoutManager = LinearLayoutManager(requireContext())
}
}
class WorkAdapter : RecyclerView.Adapter<WorkAdapter.ViewHolder>() {
var data = mutableListOf<WorkList>()
class ViewHolder(val binding: RecyclerviewWorkItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(workList: WorkList) {
binding.tvStarttime.text = workList.Starttime
binding.tvAdmin.text = workList.Admin
binding.tvPart.text = workList.Part
binding.tvStoptime.text = workList.Stoptime
binding.tvWorkers.text = workList.Workers.toString()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WorkAdapter.ViewHolder {
val binding = RecyclerviewWorkItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: WorkAdapter.ViewHolder, position: Int) {
holder.bind(data[position])
}
override fun getItemCount(): Int {
return data.size
}
// fun replaceList(newList: MutableList<WorkList>) {
// data = newList.toMutableList()
// notifyDataSetChanged()
// }
}
Is there something wrong with my code?
For reference, I just used a fragment, not an activity.
its typically for the RecyclerView to behave like its hidden but actually its not. the reason is you are passing an empty list so the recycler will not be extended or show because there is no items in the list.
I am using recyclerView to show list of apps installed in device
image - Link
and for more details I use bottomSheet on LongPress i.e. in ViewHolder Class, but How to send data of selected tab to bottomSheet with more details(such as package name, API level etc.)...for reference see images
I want - Link
I get from below coding - Link
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView.adapter = Adapter // I set adapter here with function getApps()
recyclerView.layoutManager = LinearLayoutManager(this)
private fun getApps(): List<DataClass> {
// here I get apps icon,name,size and return list<DataClass>
return list
}
Adapter.kt
class Adapter(private val listOfApps: List<AppData>) :
RecyclerView.Adapter<Adapter.ViewHolder>() {
class ViewHolder(appView: View) : RecyclerView.ViewHolder(appView), View.OnClickListener,
View.OnLongClickListener {
init {
appView.setOnClickListener(this)
appView.setOnLongClickListener(this)
}
val icon: ImageView = appView.App_icon
val name: TextView = appView.App_name
val size: TextView = appView.App_size
override fun onClick(v: View?) {
Toast.makeText(v?.context, "OnClick", Toast.LENGTH_SHORT).show()
}
override fun onLongClick(v: View?): Boolean {
// I want here on Long press BottomSheet appears with details
val bottomSheetDialog = BottomSheetDialog()
// Show bottomSheet on LongPress
bottomSheetDialog.show(
(v?.context as FragmentActivity).supportFragmentManager, bottomSheetDialog.tag
)
return true
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.list_apps, parent, false
)
return ViewHolder(view)
}
override fun getItemCount() = listOfApps.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentItem = listOfApps[position]
holder.icon.setImageDrawable(currentItem.icon)
holder.name.text = currentItem.name
holder.size.text = currentItem.size
}
}
BottomSheetDialog.kt
class BottomSheetDialog: BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
override fun getTheme(): Int = R.style.RoundBottomSheetDialog
}
DataClass
data class AppData(
val icon: Drawable,
val name: String,
val size: String,
)
With your current code the easiest solution would be:
Modify your BottomSheetDialog to include AppData in the constructor
class BottomSheetDialog(val appData: AppData): BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
override fun getTheme(): Int = R.style.RoundBottomSheetDialog
}
Add onBind method inside your ViewHolder class:
fun onBind(appData: AppData) {
icon.setImageDrawable(currentItem.icon)
name.text = currentItem.name
size.text = currentItem.size
}
Modify your onBindViewHolder method inside the Adapter to call that onBind method:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.onBind(listOfApps[position])
}
Inside your ViewHolder add lateinit var currentItem: AppData that will be set inside onBind and we can use it in onLongClick:
class ViewHolder(appView: View) : RecyclerView.ViewHolder(appView), View.OnClickListener,
View.OnLongClickListener {
.
.
.
override fun onLongClick(v: View?): Boolean {
// I want here on Long press BottomSheet appears with details
**val bottomSheetDialog = BottomSheetDialog(currentItem)**
// Show bottomSheet on LongPress
bottomSheetDialog.show(
(v?.context as FragmentActivity).supportFragmentManager, bottomSheetDialog.tag
)
return true
}
**private lateinit var currentItem: AppData**
fun onBind(appData: AppData) {
**currentItem = appData**
icon.setImageDrawable(currentItem.icon)
name.text = currentItem.name
size.text = currentItem.size
}
attach your currentItem list item to ViewHolder inside onBindViewHolder and inside onLongClick pass this data to freshly created BottomSheetDialog, which is extending Fragment (so you may use Bundle - setArgument method). then inside onCreateView set your data to Views
Create an interface
interface OnListItemClicked {
fun onClicked(data : AppData)
}
Implement that interface in BottomSheetDialog
class BottomSheetDialog: BottomSheetDialogFragment(), OnListItemClicked {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
override fun onClicked(data: AppData) {
//show Details here
}
override fun getTheme(): Int = R.style.RoundBottomSheetDialog
}
Pass extra callback to the adapter from main activity.
Adapter(private val listOfApps: List<AppData>, val onClick: (AppData) -> Unit)
Invoke the click listener from adapter with listOfAppData[position]
On MainActivity pass a function into the Adapter initialisation.
fun passDataToBottomSheet(data: AppData) {
val listener = OnListItemClicked()
listener.onClicked(data)
}
Adapter(list, ::passDataToBottomSheet)
That's it. It should work now.
So, I'm creating an app, which has a RecyclerView in the settings. The RecyclerView items, contain a cardview, containing an ImageView, used to represent the item of the RecyclerView. What i need to do is replacing the placeholder ImageView with the newer icon, give trough the fragment that loads the RecyclerView. I created my RecyclerView in Kotlin following this tutorial.
I tried creating a value with type ImageView, Image and Bitmap in the data class, however the functions suggested dont work.
How do you create an ImageView value in a class? How do you change a RecyclerView ImageView content?
dataListIcon.kt
import android.widget.ImageView
data class dataListIcons (
val stringTitle: String,
val stringDescription: String,
val imageIcon: ImageView //Here i tried creating the ImageView value
)
fragmentSettings.kt
class frgSettingsMain : Fragment() {
val listsettings = listOf(
dataListIcon("Option", "Description of option", R.drawable.ic_outline_color_lens_24),
dataListIcon("Option", "Description of option", R.drawable.ic_outline_dashboard_24),
dataListIcon("Option", "Description of option", R.drawable.ic_outline_image_24),
dataListIcon("Option", "Description of option", R.drawable.ic_outline_volume_up_24),
dataListIcon("Option", "Description of option", R.drawable.ic_outline_library_music_24)
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_settingsmain, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
rvSettingsMain.apply {
layoutManager = LinearLayoutManager(activity)
adapter = adapterSettingsMain(listsettings)
}
}
companion object {
fun newInstance(): frgSettingsMain = frgSettingsMain()
}
}
adapterSettings.kt
class adapterSettingsMain(
var listsettings: List<dataItems>
) : RecyclerView.Adapter<adapterSettingsMain.SettingsViewHolder>() {
inner class SettingsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingsViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_settingsicon, parent, false)
return SettingsViewHolder(view)
}
override fun getItemCount(): Int {
return listsettings.size
}
override fun onBindViewHolder(holder: SettingsViewHolder, position: Int) {
holder.itemView.apply {
rvSettingsTitle.text = listsettings[position].stringTitle
rvSettingsDescription.text = listsettings[position].stringDescription
rvSettingsIcon //TODO
}
}
}
val imageIcon: ImageView //Here i tried creating the ImageView
value
Change ImageView type to int as R.drawable.ic_outline_color_lens_24 is Integer type
data class dataListIcons (
val stringTitle: String,
val stringDescription: String,
val imageIcon Int //Here i tried creating the ImageView value
)
Add this Inside your onBindViewHolder(...) method
override fun onBindViewHolder(holder: SettingsViewHolder, position: Int) {
holder.itemView.apply {
rvSettingsTitle.text = listsettings[position].stringTitle
rvSettingsDescription.text = listsettings[position].stringDescription
rvIcon.setImageResource(listsettings[position].imageIcon);//rvIcon is id of ImageView from R.layout.item_settingsicon
}
}
Add <ImageView..../> inside your R.layout.item_settingsicon
Solution 1:
Simply use Glide for image rendering in Imageviews.
It will be more flexible with more features use can use with it.
val imageView : ImageView = ..initialise it..
Glide.with(imageView).placeholder(R.drawable.your_placeholder).into(imageView)
Dependency:
implementation 'com.github.bumptech.glide:glide:4.11.0'
Solution 2:
val imageView : ImageView = ..initialise it..
imageView.setImageResource(R.drawable.your_resource)
I appreciate that this question has been asked MANY times in SO but all the solutions refer to a missing reference in my arraylist which currently I (believe) am preserving.
I have an adapter as follows:
/**
* [RecyclerView.Adapter] that can display a [Note].
*/
class MyNoteRecyclerViewAdapter(
private var notes: ArrayList<Note>,
private val onNoteSelectedListener: MainActivityContract.OnNoteSelectedListener
) : RecyclerView.Adapter<MyNoteRecyclerViewAdapter.ViewHolder>() {
private val mNotes: ArrayList<Note> = notes
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_notes_list, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = mNotes[position]
holder.bind(item, onNoteSelectedListener)
}
override fun getItemCount(): Int = mNotes.size
fun setItems(notes: ArrayList<Note>) {
mNotes.clear()
mNotes.addAll(notes)
notifyDataSetChanged()
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val idView: TextView = view.findViewById(R.id.item_number)
private val contentView: TextView = view.findViewById(R.id.content)
fun bind(item: Note, onNoteSelectedListener: MainActivityContract.OnNoteSelectedListener) {
idView.text = item.id
contentView.text = item.title
itemView.setOnClickListener(View.OnClickListener { onNoteSelectedListener.onNoteSelected(item) })
}
override fun toString(): String {
return super.toString() + " '" + contentView.text + "'"
}
}
}
as you can see above, the function setItems() is setting clearing all notes in the list and readding them followed by notifyDataSetChanged()
The view that calls this is here:
class NotesListFragment() : BaseFragment(), MainActivityContract.OnNoteSelectedListener {
var mAdapter: MyNoteRecyclerViewAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_notes_list_list, container, false)
val lvNotes = view.findViewById<RecyclerView>(R.id.lv_notes);
mAdapter = MyNoteRecyclerViewAdapter(ArrayList<Note>(), this)
lvNotes.adapter = mAdapter
return view
}
override fun onNoteSelected(note: Note) {
(activity as MainActivity).onNoteSelected(Note());
}
fun onNotesLoaded(notes: ArrayList<Note>) {
mAdapter?.setItems(notes)
view?.findViewById<RecyclerView>(R.id.lv_notes)?.adapter?.notifyDataSetChanged()
}
}
The function onNotesLoaded is called by an external class that fetches notes. As you can see, I am setting items using mAdapter.setItems() which should notify the list that data has changed but without luck. I tried to also add the second line to see if it's something I'm missing but again, no luck.
I'm not sure if this is a kotlin issue on my part when assigning the list variable but any assistance would be greatly appreciated.
It seem you forgot add LayoutManager for RecyclerView
mAdapter = MyNoteRecyclerViewAdapter(ArrayList<Note>(), this)
lvNotes.adapter = mAdapter
lvNotes.layoutManager = LinearLayoutManager(context)
It seems like the issue lied in other areas of my codebase so I will close this question. Thanks for anyone who tried to help
I want to create a small application that once the user clicks on floating button action to add element. It will be added automatically
to the top of the recycle view.
I have created for this A main activity which contains the fragment of the recycler and the add flotten.
Once the user clicks on this button a fragmented dialog is shown to insert the element. Once the user confirms i want to add the new element to the top of the recycle view.
TO did I used a global array which contains the item.
I have not implemented the logic yet for that. But what surprised me once I click on confirm button of the shown dialog the element added correctly to the global array, however, the recycle view on his own without any action on it duplicate the latest element.
I used the model view presenter for that and here are my diff classes
1.MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
addFragment(InterFragment(), R.id.container)
fab.setOnClickListener { view ->
val dialog = IntervAddFragment()
val ft = supportFragmentManager.beginTransaction()
dialog.show(ft, ContentValues.TAG)
}
}
}
2.ADDFragmet
class IntervAddFragment : DialogFragment(), AddContract.View
{
val presenter: AddPresenter by lazy { AddPresenter(this) }
var dat=""
var plom=""
var typ=""
override fun onCreate(savedInstanceState: Bundle?) { ....}
override fun onStart() {.... }
override fun onCreateView(inflater: LayoutInflater?, parent: ViewGroup?, state: Bundle?): View? {...}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {....}
fun on_confirmClick()
{
presenter.on_confirmClick(dat,plom,typ)
Log.e("errrr",presenter.getList().toString())
dismiss()
}
fun fillSpinerPlom(view : View?) {.... }
fun fillSpinerType(view : View?) {.... }
private fun updateDateInView(datep:String) {....}
}
3.IntervFragement
InterFragment: Fragment(), InterContract.View {
var views: View? = null
var List_inter_adapter: InterListAdapter? = null
private var recyclerView: RecyclerView? = null
val presenter: InterventionPresenter by lazy { InterventionPresenter(this) }
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {.... }
interface ClickListener {
fun onClick(view: View, position: Int)
fun onLongClick(view: View?, position: Int)
}
internal class RecyclerTouchListener(context: Context, recyclerView: RecyclerView, private val clickListener: ClickListener?) : RecyclerView.OnItemTouchListener {..... }
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {.....}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView = views!!.findViewById<View>(R.id.interv_item_listview) as RecyclerView
List_inter_adapter = InterListAdapter(activity, presenter.getList(true))
recyclerView!!.adapter = List_inter_adapter
recyclerView!!.layoutManager = LinearLayoutManager(activity)
}
}
4.InterPresenter
class InterventionPresenter(val view: InterContract.View)
{
fun getList(firstTime:Boolean): LinkedList<InterItem> {
if(firstTime)
return populateList().list
else
return items
}
fun populateList(): ListInter
{
val item1 = InterItem(1, DateT(2018, Calendar.FEBRUARY, 6).date, plomb.get(0), types.get(0))
val item2 = InterItem(2, DateT(2018, Calendar.MARCH, 8).date, plomb.get(1), types.get(1))
val item3= InterItem(3, DateT(2018, Calendar.MAY, 10).date, plomb.get(2), types.get(2))
val lstint = ListInter()
lstint.list= LinkedList(listOf(item1, item2, item3))
items=lstint.list
return lstint
}
companion object {
val plomb= LinkedList<String>(listOf("Dina", "Lili", "Wiseem"))
val types= LinkedList<String>(listOf("type1","type2","type3"))
lateinit var items: LinkedList<InterItem>
}
}
5.ADD Presenter
class AddPresenter(val view: AddContract.View)
{
fun on_confirmClick(date:String,plom:String,type:String)
{
val item= InterItem(items.size+1,date,plom,type)
items.push(item)
}
fun getList(): LinkedList<InterItem> {
return items
}
fun getplom(): LinkedList<String>
{ return plomb
}
fun getType(): LinkedList<String>
{ return types
}
}
6.InterListAdapter
class InterListAdapter(private val context: Context, linkedList: LinkedList<InterItem>) : RecyclerView.Adapter<InterListAdapter.ViewHolder>()
{
internal var linkedList = LinkedList<InterItem>()
private val inflater: LayoutInflater
//private lateinit var listener: OnTaskSelected
init {... }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {... }
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.numero?.text=(linkedList[position].numero).toString()
holder.date?.text=linkedList[position].date
holder.plom?.text=linkedList[position].plom
holder.type?.text=linkedList[position].type
}
override fun getItemCount(): Int {
return linkedList.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var numero: TextView?=null
var date: TextView?=null
var plom: TextView?=null
var type: TextView?=null
var btn_delete: Button?=null
var btn_update: Button?=null
init {
numero = itemView.findViewById(R.id.numero)
date = itemView.findViewById(R.id.date)
plom = itemView.findViewById(R.id.plom)
type = itemView.findViewById(R.id.type)
btn_delete = itemView.findViewById(R.id.btn_supprimer)
btn_update = itemView.findViewById(R.id.btn_modifier)
}
}
}
and here is the ListInter
class ListInter
{
var list= LinkedList<InterItem>()
get() = field
set(value){field = value}
}
RecyclerView can't handle array change event by itself. So, you should notify it by calling the following after you've added element to array:
recyclerView.getAdapter().notifyItemInserted(items.size() - 1)