Instantiating type T in Kotlin - kotlin

In short, I would like to omit the repeated getT() in the example below. I have read Instantiating a generic type in Kotlin, but what does it mean 'to take () -> T as a parameter'? How can I apply that to below?
interface Food
{
var isHeated:Boolean;
var name:String;
}
abstract class Cooker<T:Food>
{
abstract fun getT():T;
abstract fun enhance(t:T);
fun cook(): T
{
var food = getT();
food.isHeated = true;
food.name = "heated " + food.name;
enhance(food);
return food;
}
}
class PotatoChip:Food
{
override var isHeated = false;
override var name = "potato chip";
}
class PotatoChipCooker:Cooker<PotatoChip>()
{
override fun getT(): PotatoChip {
return PotatoChip();
}
override fun enhance(t:PotatoChip)
{
t.name = "salted " + t.name;
}
}
class Pancake:Food
{
override var isHeated = false;
override var name = "pancake";
}
class PancakeCooker:Cooker<Pancake>()
{
override fun getT(): Pancake {
return Pancake();
}
override fun enhance(t:Pancake)
{
t.name = t.name + " coated with maple syrup";
}
}
fun main(args: Array<String>)
{
val result = PotatoChipCooker().cook();
println(result.name);
val result2 = PancakeCooker().cook();
println(result2.name);
}

You could make the initialization function part of the primary constructor. As a result, implementing classes will have to pass a function that specifies how the corresponding type is being created:
abstract class Cooker<T : Food>(private val initT: () -> T) {
abstract fun enhance(t: T)
fun cook(): T {
val food = initT()
food.isHeated = true
food.name = "heated $name"
enhance(food)
return food
}
}
class PotatoChipCooker : Cooker<PotatoChip>({ PotatoChip() }) {
override fun enhance(t: PotatoChip) {
t.name = "salted ${t.name}"
}
}
class PancakeCooker : Cooker<Pancake>({ Pancake() }) {
override fun enhance(t: Pancake) {
t.name = "${t.name} coated with maple syrup"
}
}
Note that I removed the optional semicolons and used string templates instead of concatenations. Also, the cook method can be simplified to:
fun cook() = initT().apply {
isHeated = true
name = "heated $name"
enhance(this)
}

Related

Filter searchView from RecycleView with Adapter

Adapter class
class AppListAdapter(private val context: Context, initialChecked: ArrayList<String> = arrayListOf()) : RecyclerView.Adapter<AppListAdapter.AppViewHolder>() {
public val appList = arrayListOf<ApplicationInfo>()
private val checkedAppList = arrayListOf<Boolean>()
private val packageManager: PackageManager = context.packageManager
init {
context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA).sortedBy { it.loadLabel(packageManager).toString() }.forEach { info ->
if (info.packageName != context.packageName) {
if (info.flags and ApplicationInfo.FLAG_SYSTEM == 0) {
appList.add(info)
checkedAppList.add(initialChecked.contains(info.packageName))
}
}
}
}
inner class AppViewHolder(private val item: ItemAppBinding) : RecyclerView.ViewHolder(item.root) {
fun bind(data: ApplicationInfo, position: Int) {
item.txApp.text = data.loadLabel(packageManager)
item.imgIcon.setImageDrawable(data.loadIcon(packageManager))
item.cbApp.isChecked = checkedAppList[position]
item.cbApp.setOnCheckedChangeListener { _, checked ->
checkedAppList[position] = checked
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder {
return AppViewHolder(ItemAppBinding.inflate(LayoutInflater.from(context), parent, false))
}
override fun onBindViewHolder(holder: AppViewHolder, position: Int) {
holder.bind(appList[position], position)
}
override fun getItemCount(): Int {
return appList.size
}
on MainActivity
binding.searchView2.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
binding.searchView2.clearFocus()
// how to write code filtered by query?
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
// how to write code filtered by newText?
return false
}
})
I'm newbie in kotlin..anyone can help?
I believe you want to filter items with "ApplicationInfo.txApp", so i will write the code for it.
First you need your adapter class to extend Filterable like below, and add one more list to hold all items:
class AppListAdapter(private val context: Context, initialChecked: ArrayList<String> = arrayListOf()) : RecyclerView.Adapter<AppListAdapter.AppViewHolder>(), Filterable {
public val appList = arrayListOf<ApplicationInfo>()
public val appListFull = ArrayList<ApplicationInfo>(appList)
// This full list because of when you delete all the typing to searchView
// so it will get back to that full form.
Then override it's function and write your own filter to work, paste this code to your adapter:
override fun getFilter(): Filter {
return exampleFilter
}
private val exampleFilter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults? {
val filteredList: ArrayList<ApplicationInfo> = ArrayList()
if (constraint == null || constraint.isEmpty()) {
// when searchview is empty
filteredList.addAll(appListFull)
} else {
// when you type something
// it also uses Locale in case capital letters different.
val filterPattern = constraint.toString().lowercase(Locale.getDefault()).trim()
for (item in appListFull) {
val txApp = item.txApp
if (txApp.lowercase(Locale.getDefault()).contains(filterPattern)) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
#SuppressLint("NotifyDataSetChanged") #Suppress("UNCHECKED_CAST")
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
appList.clear()
appList.addAll(results!!.values as ArrayList<ApplicationInfo>)
notifyDataSetChanged()
}
}
And finally call this filter method in your searchview:
binding.searchView2.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
yourAdapter.filter.filter(query)
yourAdapter.notifyDataSetChanged()
binding.searchView2.clearFocus()
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
yourAdapter.filter.filter(newText)
yourAdapter.notifyDataSetChanged()
return false
}
})
These should work i'm using something similar to that, if not let me know with the problem.

Method param require nothing instead of typed param

There's superclass with typed parameter:
abstract inner class CalendarViewHolder<T : CalendarItem>(view: View) :
RecyclerView.ViewHolder(view) {
open fun bindData(data: T) {}
}
I expect that the method bindData will accept params which are objects of CalendarItem subclasses.
But in onBindViewHolder I got the following error:
Type mismatch.
Required:
Nothing
Found:
CalendarItem
Here's the implementation of the method:
override fun onBindViewHolder(holder: CalendarViewHolder<out CalendarItem>, position: Int) {
val item = items[position]
holder.bindData(item)
if (item is DayItem) {
if (holder is OnClickStrategy) {
holder.onClickListener = {
selectedDayOfYear = item.date?.get(Calendar.DAY_OF_YEAR) ?: 0
notifyItemChanged(position)
if (previousClickedPosition != -1) {
notifyItemChanged(previousClickedPosition)
}
previousClickedPosition = holder.adapterPosition
}
}
}
}
Here's the full code of adapter:
class CalendarAdapter :
RecyclerView.Adapter<CalendarAdapter.CalendarViewHolder<out CalendarItem>>() {
private val items = mutableListOf<CalendarItem>()
private var selectedDayOfYear = -1
private var previousClickedPosition = -1
abstract inner class CalendarViewHolder<T : CalendarItem>(view: View) :
RecyclerView.ViewHolder(view) {
open fun bindData(data: T) {}
}
abstract inner class CalendarDateViewHolder(view: View) : CalendarViewHolder<DayItem>(view) {
protected lateinit var tvDate: AppCompatTextView
protected lateinit var tvMonth: AppCompatTextView
protected lateinit var tvDayDescription: AppCompatTextView
protected lateinit var clRoot: ConstraintLayout
#CallSuper
override fun bindData(data: DayItem) {
tvDate = itemView.findViewById(R.id.tv_day_number)
tvMonth = itemView.findViewById(R.id.tv_month)
tvDayDescription = itemView.findViewById(R.id.tv_day_description)
clRoot = itemView.findViewById(R.id.cl_root)
}
}
interface OnClickStrategy {
var onClickListener: () -> Unit
}
inner class CalendarEmptyViewHolder(view: View) : CalendarViewHolder<EmptyDayItem>(view)
inner class CalendarWithDateViewHolder(view: View) :
CalendarDateViewHolder(view), OnClickStrategy {
override lateinit var onClickListener: () -> Unit
override fun bindData(data: DayItem) {
super.bindData(data)
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
clRoot.setOnClickListener {
onClickListener.invoke()
}
clRoot.background = ContextCompat.getDrawable(
itemView.context,
when (selectedDayOfYear) {
data.date?.get(Calendar.DAY_OF_YEAR) -> R.drawable.item_selected_background
else -> R.drawable.item_default_background
}
)
tvDate.setTextColor(
ContextCompat.getColor(
itemView.context,
if (selectedDayOfYear == data.date?.get(Calendar.DAY_OF_YEAR)) R.color.white
else R.color.black
)
)
}
}
inner class CalendarTodayDateViewHolder(view: View) : CalendarDateViewHolder(view) {
override fun bindData(data: DayItem) {
super.bindData(data)
tvDayDescription.text = "hoy"
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
clRoot.background = ContextCompat.getDrawable(
itemView.context,
R.drawable.item_today_background
)
val monthName = when (data.date?.get(Calendar.MONTH)) {
0 -> "January"
1 -> "February"
else -> "March"
}
tvMonth.text = monthName
tvDate.setTextColor(ContextCompat.getColor(itemView.context, R.color.white))
tvDayDescription.setTextColor(ContextCompat.getColor(itemView.context, R.color.white))
tvMonth.setTextColor(ContextCompat.getColor(itemView.context, R.color.white))
}
}
inner class CalendarNewMonthDateViewHolder(view: View) : CalendarDateViewHolder(view),
OnClickStrategy {
override lateinit var onClickListener: () -> Unit
override fun bindData(data: DayItem) {
super.bindData(data)
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
val monthName = when (data.date?.get(Calendar.MONTH)) {
0 -> "January"
1 -> "February"
else -> "March"
}
tvMonth.text = monthName
clRoot.setOnClickListener {
onClickListener.invoke()
}
tvDate.setTextColor(
ContextCompat.getColor(
itemView.context,
if (selectedDayOfYear == data.date?.get(Calendar.DAY_OF_YEAR)) R.color.white
else R.color.black
)
)
clRoot.background = ContextCompat.getDrawable(
itemView.context,
when (selectedDayOfYear) {
data.date?.get(Calendar.DAY_OF_YEAR) -> R.drawable.item_selected_background
else -> R.drawable.item_default_background
}
)
}
}
inner class CalendarDisabledDateViewHolder(view: View) : CalendarDateViewHolder(view) {
override fun bindData(data: DayItem) {
super.bindData(data)
tvDate.text = data.date?.get(Calendar.DAY_OF_MONTH)?.toString()
clRoot.background = ContextCompat.getDrawable(
itemView.context,
R.drawable.item_disabled_background
)
tvDate.setTextColor(ContextCompat.getColor(itemView.context, R.color.silver))
}
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
) = when (viewType) {
CALENDAR_EMPTY_ITEM_TYPE -> onCreateCalendarEmptyViewHolder(parent)
CALENDAR_TODAY_DATE_ITEM_TYPE -> onCreateCalendarTodayDateViewHolder(parent)
CALENDAR_NEW_MONTH_DATE_ITEM_TYPE -> onCreateCalendarNewMonthDateViewHolder(parent)
CALENDAR_WITH_DATE_ITEM_TYPE -> onCreateCalendarWithDateViewHolder(parent)
else -> onCreateCalendarDisabledDateViewHolder(parent)
}
override fun getItemViewType(position: Int) = when (val item = items[position]) {
EmptyDayItem -> CALENDAR_EMPTY_ITEM_TYPE
is DayItem -> when {
item.isToday() -> CALENDAR_TODAY_DATE_ITEM_TYPE
item.isNewMonth() -> CALENDAR_NEW_MONTH_DATE_ITEM_TYPE
item.isDateEnabled -> CALENDAR_WITH_DATE_ITEM_TYPE
else -> CALENDAR_DISABLED_DATE_ITEM_TYPE
}
else -> UNKNOWN_ITEM_TYPE
}
private fun onCreateCalendarWithDateViewHolder(parent: ViewGroup) = CalendarWithDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
private fun onCreateCalendarEmptyViewHolder(parent: ViewGroup): CalendarViewHolder<EmptyDayItem> =
CalendarEmptyViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_empty, parent, false)
)
private fun onCreateCalendarTodayDateViewHolder(parent: ViewGroup) =
CalendarTodayDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
private fun onCreateCalendarNewMonthDateViewHolder(parent: ViewGroup) =
CalendarNewMonthDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
private fun onCreateCalendarDisabledDateViewHolder(parent: ViewGroup) =
CalendarDisabledDateViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_calendar_day, parent, false)
)
override fun onBindViewHolder(holder: CalendarViewHolder<out CalendarItem>, position: Int) {
val item = items[position]
holder.bindData(item)
if (item is DayItem) {
if (holder is OnClickStrategy) {
holder.onClickListener = {
selectedDayOfYear = item.date?.get(Calendar.DAY_OF_YEAR) ?: 0
notifyItemChanged(position)
if (previousClickedPosition != -1) {
notifyItemChanged(previousClickedPosition)
}
previousClickedPosition = holder.adapterPosition
}
}
}
}
override fun getItemCount() = items.size
fun setItems(items: MutableList<DayItem>) {
val localItems: MutableList<CalendarItem> = items.toMutableList()
if (!items.isNullOrEmpty()) {
val firstDayNumber = items[0].date?.get(Calendar.DAY_OF_WEEK) ?: 0
for (i in 1 until firstDayNumber) {
localItems.add(0, EmptyDayItem)
}
this.items.clear()
this.items.addAll(localItems)
}
}
companion object {
private const val CALENDAR_EMPTY_ITEM_TYPE = 0
private const val CALENDAR_TODAY_DATE_ITEM_TYPE = 1
private const val CALENDAR_NEW_MONTH_DATE_ITEM_TYPE = 2
private const val CALENDAR_WITH_DATE_ITEM_TYPE = 3
private const val CALENDAR_DISABLED_DATE_ITEM_TYPE = 4
private const val CALENDAR_HEADER_ITEM_TYPE = 5
private const val UNKNOWN_ITEM_TYPE = -1
}
}
sealed class CalendarItem
class DayItem(
val date: Calendar? = null,
val isDateEnabled: Boolean = true
): CalendarItem() {
fun isToday() = if (date != null) isSameDay(date, Calendar.getInstance()) else false
fun isNewMonth() = date?.get(Calendar.DAY_OF_MONTH) == 1
private fun isSameDay(cal1: Calendar, cal2: Calendar) =
cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)
}
object EmptyDayItem : CalendarItem()
What's the reason of the problem?
You specified the type as out CalendarItem, but the bind function consumes the item of type T, so it is in an in position, not out. So by marking it as out at the class declaration, you have restricted it to Nothing wherever T appears as a function parameter type.
There's no way to make generics work for you here, as I'm assuming you're trying to get it to allow you to bind items without casting. onCreateViewHolder has your ViewHolder in an out position, which means your particular ViewHolder cannot have invariant or contravariant (in) type if you want to be able to return different types of your ViewHolder subclass from onCreateViewHolder.
It's just not logically possible for generics to safely allow you to pass these items to the appropriate types, and you will have to use casting.
I suggest you eliminate the generic type from your abstract ViewHolder, and make each subclass throw an error if it gets the wrong type. Then you will quickly get exceptions when you test your app if there are any type mismatches and you will know the problem lies in either getItemViewType or onCreateViewHolder, where you've hooked up your classes and types incorrectly.
I also suggest using View Binding, because it will make these classes much more streamlined. No lateinit view properties or findViewById calls.
For example:
abstract class CalendarViewHolder(view: View): RecyclerView.ViewHolder(view) {
abstract fun bindData(data: CalendarItem): Any
// Any return type here allows subclasses to use "= with(binding)"
// to reduce nesting.
}
class CalendarDateViewHolder(private val binding: ItemCalendarDayBinding): MyViewHolder(binding.root) {
override fun bind(item: CalendarItem) = with(binding) {
item as? DayItem ?: error("Wrong item type.")
// item is now smart cast as DayItem
// bind the DayItem data to the views.
// can access the views directly by name in with block.
}
}
private fun onCreateCalendarWithDateViewHolder(parent: ViewGroup) = CalendarWithDateViewHolder(
ItemCalendarDayBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
And I suggest also getting rid of all those onCreate functions and adding constructors that use the parent ViewGroup, just to streamline things, for example:
class CalendarDateViewHolder private constructor(private val binding: ItemCalendarDayBinding): MyViewHolder(binding.root) {
constructor(parent: ViewGroup): this(ItemCalendarDayBinding.inflate(LayoutInflater.from(parent.context), parent, false))
//...
}

Unresolved reference: myViewHolder after switching to View Binding

After switching from removing kotlin_extensions and switching to view binding, I received a "Unresolved reference: myViewHolder" in my onBindViewHolder method and when I replace "myViewHolder" with "holder", it then gives me a "Unresolved reference: bind". How do I resolve this.
MyAdapter
class MyAdapter(private val context: Context, private val mHelper : TaskDbHelper) : RecyclerView.Adapter<MyAdapter.MyViewHolder>(),
SwipeAndDragHelper.ActionCompletionContract {
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun binding() {
}
}
private var touchHelper: ItemTouchHelper? = null
private var list = mutableListOf<MyObject>()
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
initList()
super.onAttachedToRecyclerView(recyclerView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(CellCardsBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
#RequiresApi(Build.VERSION_CODES.P)
#SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
myViewHolder.bind(myObject)
val activity: Activity = context as Activity
holder.binding.text.setOnClickListener{
activity.launchActivity<AddNoteActivity>(42) {
putExtra("PositionInList", position.toString())
putExtra("TaskTitle", myObject.title)
putExtra("TaskText", myObject.text)
}
}
activity.findViewById<RecyclerView>(R.id.recyclerView).setOnTouchListener { _, event ->
when (event.actionMasked) {
MotionEvent.ACTION_UP -> {
updateNotesPositionInDb()
false
}
else -> {
false
}
}
}
holder.binding.title.setOnTouchListener { _, event ->
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
touchHelper!!.startDrag(holder)
false
}
else -> {
false
}
}
}
}
private fun initList() {
list.clear()
val db = mHelper.readableDatabase
val cursor = db.query(
TaskContract.TaskEntry.TABLE,
arrayOf(
TaskContract.TaskEntry.ID,
TaskContract.TaskEntry.COL_TASK_TITLE,
TaskContract.TaskEntry.COL_TASK_TEXT,
TaskContract.TaskEntry.COL_TASK_DATE),null, null, null, null, TaskContract.TaskEntry.ID)
while (cursor.moveToNext()) {
val id = cursor.getColumnIndex(TaskContract.TaskEntry.ID)
val idTitle = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TITLE)
val idText = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TEXT)
val idDate = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_DATE)
list.add(MyObject(cursor.getString(id), cursor.getString(idTitle), cursor.getString(idText), cursor.getString(idDate)))
}
notifyDataSetChanged()
cursor.close()
db.close()
}
override fun getItemCount(): Int {
return list.size
}
override fun onViewMoved(oldPosition: Int, newPosition: Int) {
val target = list[oldPosition]
list.removeAt(oldPosition)
list.add(newPosition, target)
notifyItemMoved(oldPosition, newPosition)
}
override fun onViewSwiped(position: Int) {
deleteTask(list[position].ID)
list.removeAt(position)
notifyItemRemoved(position)
updateNotesPositionInDb()
}
fun setTouchHelper(touchHelper: ItemTouchHelper) {
this.touchHelper = touchHelper
}
fun addTask(taskTitle : String, taskText: String) {
val values = ContentValues()
val sdf = SimpleDateFormat("dd/MM/yyyy/", Locale.US)
val date = sdf.format(Date())
values.put(TaskContract.TaskEntry.ID, list.size)
values.put(TaskContract.TaskEntry.COL_TASK_TITLE, taskTitle)
values.put(TaskContract.TaskEntry.COL_TASK_TEXT, taskText)
values.put(TaskContract.TaskEntry.COL_TASK_DATE, date)
val db = mHelper.readableDatabase
db.insertWithOnConflict(TaskContract.TaskEntry.TABLE,
null,
values,
SQLiteDatabase.CONFLICT_REPLACE)
db.close()
list.add(MyObject(list.size.toString(), taskTitle, taskText, date))
notifyItemInserted(list.size)
}
fun addTask() {
val test: Activity = context as Activity
test.launchActivity<AddNoteActivity>(42) {
/* putExtra("user", "854")
p utExtra("user2", "46850") */
}
}
private fun deleteTask(taskId: String) {
val db = mHelper.readableDatabase
db.delete(TaskContract.TaskEntry.TABLE,
"id=$taskId", null)
db.close()
}
fun modifyTask(taskPosition: String, taskTitle: String, taskText: String) {
val target = list[taskPosition.toInt()]
target.title = taskTitle
target.text = taskText
val values = ContentValues()
val sdf = SimpleDateFormat("dd/MM/yyyy/", Locale.US)
val date = sdf.format(Date())
values.put(TaskContract.TaskEntry.ID, taskPosition)
values.put(TaskContract.TaskEntry.COL_TASK_TITLE, taskTitle)
values.put(TaskContract.TaskEntry.COL_TASK_TEXT, taskText)
values.put(TaskContract.TaskEntry.COL_TASK_DATE, date)
val db = mHelper.readableDatabase
db.update(TaskContract.TaskEntry.TABLE,
values, TaskContract.TaskEntry.ID + "=" + target.ID, null)
db.close()
notifyItemChanged(taskPosition.toInt())
}
private fun updateNotesPositionInDb() {
val db = mHelper.readableDatabase
var i = 0
while (i < list.size) {
val values = ContentValues()
values.put(TaskContract.TaskEntry.ID, i)
db.update(TaskContract.TaskEntry.TABLE,
values, TaskContract.TaskEntry.ID + "=? AND " + TaskContract.TaskEntry.COL_TASK_TITLE + "=?", arrayOf(list[i].ID, list[i].title))
i++
}
db.close()
}
I've tried reading Android Studio's official documentation, but it cannot solve my specific problem.
in your class MyViewHolder you have method called binding and you need also to implement it and add paramter
shoud be
class MyViewHolder(private val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun bind(data:MyObject) {
binding.yourView=data.title ...
}
}
in onBindViewHolder
..
holder.bind(myObject)
After switching from removing kotlin_extensions and switching to view binding, I received a "Unresolved reference: myViewHolder" in my onBindViewHolder method
Well, your onBindViewHolder method is passing a variable called holder and you're trying to use a variable called myViewHolder, so that seems like a problem.
// --------------------this-----v
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
// v--- doesn't match this
myViewHolder.bind(myObject)
and when I replace "myViewHolder" with "holder", it then gives me a "Unresolved reference: bind". How do I resolve this.
Your MyViewHolder class has a method called binding that takes no arguments. There is no bind method that takes a "myObject".
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun binding() {
}
}
Edit
You should pass an instance of the data class
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun bind(object: MyObject) {
// Set variables on binding
}
}
Then pass an instance from your list via onBindViewHolder:
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
holder.bind(myObject)
Please check this blog post for more.
I got my answer.
class MyAdapter(private val context: Context, private val mHelper : TaskDbHelper) : RecyclerView.Adapter<MyAdapter.MyViewHolder>(),
SwipeAndDragHelper.ActionCompletionContract {
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
private val titleView: TextView = itemView.findViewById<View>(R.id.title) as TextView
val textView: TextView = itemView.findViewById<View>(R.id.text) as TextView
private val dateTextView: TextView = itemView.findViewById<View>(R.id.date) as TextView
fun binding (myObject: MyObject) {
titleView.text = myObject.title
textView.text = myObject.text
dateTextView.text = myObject.date
}
}
I simply initialised the view I wanted to reference in my layout and called them in the binding() function.

How to put methods in companion object of parent class so that they can be inherited in children classes?

I have two data classes which are very similar to each other.
I want to write a parent class for both of them so they can inherit the common functionality.
My problem is that some methods I want to inherit are needed to be inside companion object.
A) data class Link
import org.json.JSONArray
import org.json.JSONObject
data class Link(
val name: String,
val url: String
) {
var selected: Boolean = false
fun toggle() { selected = selected.not() }
companion object {
fun fromJson(obj: JSONObject): Link = with(obj) {
Link(getString("name"), getString("url"))
}
fun fromJson(arr: JSONArray): List<Link> = with(arr) {
List(length()) {
fromJson(getJSONObject(it))
}
}
fun toJson(list: List<Link>): JSONArray = JSONArray().apply {
list.forEach {
put(it.toJson())
}
}
}
fun toJson(): JSONObject = JSONObject().apply {
put("name", name)
put("url", url)
}
}
B) data class DownloadStatus
import org.json.JSONArray
import org.json.JSONObject
data class DownloadStatus(
val name: String,
val url: String,
val path: String,
var progress: Int = 0
) {
var selected: Boolean = false
fun toggle() { selected = selected.not() }
companion object {
fun fromJson(obj: JSONObject): DownloadStatus = with(obj) {
DownloadStatus(getString("name"), getString("url"), getString("path"), getInt("progress"))
}
fun fromJson(arr: JSONArray): List<DownloadStatus> = with(arr) {
List(length()) {
fromJson(getJSONObject(it))
}
}
fun toJson(list: List<DownloadStatus>): JSONArray = JSONArray().apply {
list.forEach {
put(it.toJson())
}
}
}
fun toJson(): JSONObject = JSONObject().apply {
put("name", name)
put("url", url)
put("path", path)
put("progress", progress)
}
}
abstract class Parent
abstract class Parent {
var selected: Boolean = false
fun toggle() { selected = selected.not() }
companion object {
}
abstract fun toJson(): JSONObject
}
I am stuck here. How to put the methods in companion object of the parent class?

How to set up Viewmodel class properly?

I'm trying to follow some tutotial from github about MVVM model and i'm stuck at viewmodel class because there's an error says
Not enough information to infer type variable T
and
Type mismatch.
Required:Resource<Movie>
Found:Unit
And when i check my other class like ApiService, Dao, NetworkBoundResource, ApiResponse, Resources and respository everthing fine like this
ApiService :
interface ApiService {
#GET("3/movie/popular")
fun getMyMovie(#Query("api_key") api : String = "32bbbffe944d16d1d2a3ee46cfc6aaa0"
) : Flow<ApiResponse<MovieResponse.Movie>>
}
MovieDao:
#Dao
interface MovieDao : BaseDao<Movie> {
// #Insert(onConflict = OnConflictStrategy.REPLACE)
// fun insertMovie(movie: List<Movie>)
#Query("SELECT * FROM `movie` ORDER by movie_id DESC")
fun getMyMovie() : Flow<Movie>
#Query("SELECT * FROM `movie` ORDER by movie_id DESC")
fun findAllMovie() : Maybe<List<Movie>>
#Query("SELECT * FROM `movie` ORDER by movie_id DESC")
fun streamAll() : Flowable<List<Movie>>
#Query("DELETE FROM `movie`")
fun deleteAll()
}
MovieRespository:
class MovieRespository (val apiService: ApiService, val movieDao: MovieDao) {
fun getListMovie() : Flow<Resource<Movie>> {
return networkBoundResource(
fetchFromLocal = { movieDao.getMyMovie() },
shouldFetchFromRemote = {true},
fetchFromRemote = {apiService.getMyMovie()},
processRemoteResponse = {},
saveRemoteData = {movieDao.insert(
it.results.let {
it.map { data -> Movie.from(data) }
}
)},
onFetchFailed = {_, _ ->}
).flowOn(Dispatchers.IO)
}
NeteorkBoundResource:
inline fun <DB, REMOTE> networkBoundResource(
crossinline fetchFromLocal: () -> Flow<DB>,
crossinline shouldFetchFromRemote: (DB?) -> Boolean = { true },
crossinline fetchFromRemote: () -> Flow<ApiResponse<REMOTE>>,
crossinline processRemoteResponse: (response: ApiSuccessResponse<REMOTE>) -> Unit = { Unit },
crossinline saveRemoteData: (REMOTE) -> Unit = { Unit },
crossinline onFetchFailed: (errorBody: String?, statusCode: Int) -> Unit = { _: String?, _: Int -> Unit }
) = flow<Resource<DB>> {
emit(Resource.Loading(null))
val localData = fetchFromLocal().first()
if (shouldFetchFromRemote(localData)) {
emit(Resource.Loading(localData))
fetchFromRemote().collect { apiResponse ->
when (apiResponse) {
is ApiSuccessResponse -> {
processRemoteResponse(apiResponse)
apiResponse.body?.let { saveRemoteData(it) }
emitAll(fetchFromLocal().map { dbData ->
Resource.Success(dbData)
})
}
is ApiErrorResponse -> {
onFetchFailed(apiResponse.errorMessage, apiResponse.statusCode)
emitAll(fetchFromLocal().map {
Resource.Error(
apiResponse.errorMessage,
it
)
})
}
}
}
} else {
emitAll(fetchFromLocal().map { Resource.Success(it) })
}
}
ApiResponse:
sealed class ApiResponse<T> {
companion object {
fun <T> create(error: Throwable): ApiErrorResponse<T> {
return ApiErrorResponse(
error.message ?: "Unknown error",
0
)
}
fun <T> create(response: Response<T>): ApiResponse<T> {
return if (response.isSuccessful) {
val body = response.body()
val headers = response.headers()
if (body == null || response.code() == 204) {
ApiEmptyResponse()
} else {
ApiSuccessResponse(
body,
headers
)
}
} else {
val msg = response.errorBody()?.string()
val errorMsg = if (msg.isNullOrEmpty()) {
response.message()
} else {
msg
}
ApiErrorResponse(
errorMsg ?: "Unknown error",
response.code()
)
}
}
}
}
/**
* separate class for HTTP 204 responses so that we can make ApiSuccessResponse's body non-null.
*/
class ApiEmptyResponse<T> : ApiResponse<T>()
data class ApiSuccessResponse<T>(
val body: T?,
val headers: okhttp3.Headers
) : ApiResponse<T>()
data class ApiErrorResponse<T>(val errorMessage: String, val statusCode: Int) : ApiResponse<T>()
Resource:
data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
// data class Loading<T>(val loadingData: T?) : Resource<T>(Status.LOADING, loadingData, null)
// data class Success<T>(val successData: T?) : Resource<T>(Status.SUCCESS, successData, null)
// data class Error<T>(val msg: String, val error: T?) : Resource<T>(Status.ERROR, error, msg)
companion object {
fun <T> Success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data,null)
}
fun <T> Error(msg: String, data: T? = null): Resource<T> {
return Resource(Status.ERROR, data, msg)
}
fun <T> Loading(data: T? = null): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
MainViewModel:
class MainViewModel(private val movieRespository: MovieRespository) : ViewModel() {
#ExperimentalCoroutinesApi
val getListMovies: LiveData<Resource<Movie>> = movieRespository.getListMovie().map {
when(it.status){
Resource.Loading() ->{}
Resource.Success() ->{}
Resource.Error() ->{}
}
}.asLiveData(viewModelScope.coroutineContext)
}
to be specific this what the error looks like and this is the link of tutorial i learn
https://github.com/hadiyarajesh/flower
viewModel Class Error
You get the error, because Inside the when, you are trying to construct a new instance of Resource.Loading() etc, but those require a type, so it would need to be something like Resource.Loading<Movie>().
Tht being said, you are doing when(it.status), so the cases in the when, should not be a Resource.Loading, but Status.LOADING instead:
class MainViewModel(private val movieRespository: MovieRespository) : ViewModel() {
#ExperimentalCoroutinesApi
val getListMovies: LiveData<Resource<Movie>> = movieRespository.getListMovie().map {
when(it.status){
Status.LOADING ->{}
Status.SUCCESS ->{}
Status.ERROR ->{}
}
return#map it
}.asLiveData(viewModelScope.coroutineContext)
}
Also, since you are declaring LiveData<Resource<Movie>>, you need to return a Resource<Movie> from the map {} (we could drop return#map, it is just to be explicit)