Using roomdatabase query for search between products - android

before get to my issue let me tell you how my app works.
I have A little Grocery App and fetch data from an api with retrofit and after that save it to Roomdatabase.
For better Ui experiment I need to implement searchview with an edittext on my main screen .
So , I decide to code a query in my dao and get all data by title filter .
But the problem is that , when I fill the edittext and click on button to get the product that I filter it nothing happened and doesn't any search .
Well , I guess maybe my problem would be with my code that I implement in repository and viewmodel to insert data to roomdatabase . if not , what's wrong with my code ?
I will be appreciated if you look at my code .
and here is my code :
This is room table :
#Entity(tableName = "newTable")
data class RoomEntity(
#PrimaryKey
(autoGenerate = true)
val id : Int? ,
#ColumnInfo val title: String,
#ColumnInfo val image: String
)
Dao :
#Dao
interface RoomDaoQuery {
#Query("SELECT * FROM newTable")
fun getAllProduct () : LiveData<List<RoomEntity>>
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertDataToDatabase(model : List<RoomEntity>)
#Query("SELECT * FROM newTable WHERE title LIKE '%' || :search || '%'")
fun searchByName(search: String): List<RoomEntity>
}
Repository :
class Repository(private val database: DatabaseRoom) {
fun getAllProduct() = database.GetDao.getAllProduct()
private fun retrofit(): ApiRetrofit {
return Retrofit.Builder()
.baseUrl("http://192.168.43.106/")
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
.build()
.create(ApiRetrofit::class.java)
}
suspend fun fettchAllDat(): List<RoomEntity> {
return retrofit().getProduct()
}
suspend fun insertToDatabase(model : List<RoomEntity>) {
database.GetDao.insertDataToDatabase(fettchAllDat())
}
// this is for local search
fun searchWithName (title : String) : List<RoomEntity> {
return database.GetDao.searchByName(title)
}
}
Viewmodel:
class ViewmodelRoom(application: Application) : AndroidViewModel(application) {
val product = MutableLiveData<List<RoomEntity>>()
private val repository = Repository(DatabaseRoom.getInstance(application))
private var viewModelJob = SupervisorJob()
private val viewModelScope = CoroutineScope(viewModelJob + Dispatchers.Default)
fun getAllProduct() = repository.getAllProduct()
fun setup() {
viewModelScope.launch{
product.postValue(repository.fettchAllDat())
insertall()
}
}
fun insertall() {
viewModelScope.launch {
repository.insertToDatabase(repository.fettchAllDat())
}
}
fun searchByTitle(title : String) = CoroutineScope(Dispatchers.Default).launch{
repository.searchWithName(title)
}
}
and MainActivity :
class MainActivity : AppCompatActivity() {
val viewModel: ViewmodelRoom by lazy {
ViewModelProvider(this).get(ViewmodelRoom::class.java)
}
#RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val editText: EditText = findViewById(R.id.edittext)
val search: ImageView = findViewById(R.id.searchview)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)
search.setOnClickListener {
viewModel.searchByTitle(editText.text.toString())
editText.text.clear()
}
editText.addTextChangedListener(object : TextWatcher {
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
viewModel.searchByTitle(editText.text.toString())
}
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})
if (isNetworkAvaliable(applicationContext)) {
viewModel.setup()
viewModel.product.observe(this, Observer {
recyclerView.apply {
layoutManager = GridLayoutManager(this#MainActivity, 2)
adapter = RecyclerAdapterMain(it, this#MainActivity)
}
})
} else {
viewModel.getAllProduct().observe(this, Observer { list ->
recyclerView.apply {
layoutManager = GridLayoutManager(this#MainActivity, 2)
adapter = RecyclerAdapterMain(list, this#MainActivity)
}
})
}
}

finally I get to a proper result .
I put my code here , I hope maybe useful for someone .
the Dao :
#Query("SELECT * FROM newTable WHERE title LIKE :name")
fun search (name : String) :LiveData<List<RoomEntity>>
Repository :
fun search(name : String): LiveData<List<RoomEntity>>{
return database.GetDao.search(name)
}
fun search(name : String) : LiveData<List<RoomEntity>> {
return repository.search(name)
}
MainActivity :
val editText: EditText = findViewById(R.id.edittext)
val search: ImageView = findViewById(R.id.searchview)
recyclerView = findViewById(R.id.recyclerview)
search.setOnClickListener {
// this is an extention function that observe data
searchProduct(editText.text.toString())
}
editText.addTextChangedListener(object : TextWatcher {
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
searchProduct(editText.text.toString())
}
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})
private fun searchProduct(title : String) {
var searchText = title
searchText = "%$title%"
viewModel.search(searchText).observe(this#MainActivity , Observer {
d("main" , "$it")
recyclerView.apply {
layoutManager = GridLayoutManager(this#MainActivity, 2)
adapter = RecyclerAdapterMain(it, this#MainActivity)
}
})
}

Related

how to implement search viewmodel and show it in recyclerview in kotlin

I am developing tvshows app where I am implementing following logic user search tvshows and filtered result has to show in recyclerview but I want to implement filtering functionality in viewmodel
how can I achieve that
below interface class
interface ApiInterface {
#GET("search/shows")
suspend fun searchShows( #Query("q") query: String): Call<TvMazeResponse>
}
below TvRepository.kt
class TvRepository(private val apiInterface: ApiInterface) {
suspend fun getShows() = apiInterface.searchShows("")
}
below adapter class
class TvAdapter : RecyclerView.Adapter<TvAdapter.ViewHolder>(), Filterable {
lateinit var tvMazeList: MutableList<TvMazeResponse>
lateinit var filterResult: ArrayList<TvMazeResponse>
override fun getItemCount(): Int =
filterResult.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.tv_item, parent,
false
)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(filterResult[position])
}
fun addData(list: List<TvMazeResponse>) {
tvMazeList = list as MutableList<TvMazeResponse>
filterResult = tvMazeList as ArrayList<TvMazeResponse>
notifyDataSetChanged()
}
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val charString = constraint?.toString() ?: ""
if (charString.isEmpty()) filterResult =
tvMazeList as ArrayList<TvMazeResponse> else {
val filteredList = ArrayList<TvMazeResponse>()
tvMazeList
.filter {
(it.name.contains(constraint!!)) or
(it.language.contains(constraint))
}
.forEach { filteredList.add(it) }
filterResult = filteredList
}
return FilterResults().apply { values = filterResult }
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
filterResult = if (results?.values == null)
ArrayList()
else
results.values as ArrayList<TvMazeResponse>
notifyDataSetChanged()
}
}
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(result: TvMazeResponse) {
with(itemView) {
Picasso.get().load(result.image.medium).into(imageView)
}
}
}
}
below Constants.kt
object Constants {
const val BASE_URL = "https://api.tvmaze.com/"
}
below TvMazeResponse.kt
data class TvMazeResponse(
#SerializedName("averageRuntime")
val averageRuntime: Int,
#SerializedName("dvdCountry")
val dvdCountry: Any,
#SerializedName("externals")
val externals: Externals,
#SerializedName("genres")
val genres: List<String>,
#SerializedName("id")
val id: Int,
#SerializedName("image")
val image: Image,
#SerializedName("language")
val language: String,
#SerializedName("_links")
val links: Links,
#SerializedName("name")
val name: String,
#SerializedName("network")
val network: Network,
#SerializedName("officialSite")
val officialSite: String,
#SerializedName("premiered")
val premiered: String,
#SerializedName("rating")
val rating: Rating,
#SerializedName("runtime")
val runtime: Int,
#SerializedName("schedule")
val schedule: Schedule,
#SerializedName("status")
val status: String,
#SerializedName("summary")
val summary: String,
#SerializedName("type")
val type: String,
#SerializedName("updated")
val updated: Int,
#SerializedName("url")
val url: String,
#SerializedName("webChannel")
val webChannel: Any,
#SerializedName("weight")
val weight: Int
)
below TvViewModel.kt
class TvViewModel(apiInterface: ApiInterface) : ViewModel() {
}
I want to implement filter and search function in viewmodel how can I achieve that any help and tips greatly appreciated
In TvRepository change the getShows function to
suspend fun getShows(searchString:String) = apiInterface.searchShows(searchString)
Then in the ViewModel change the constructor to get an instance of the TVRepository and call API as shown below
class TvViewModel( tvRepository: TvRepository) : ViewModel() {
fun getShows(searchParameter:String){
viewModelScope.launch(Dispatchers.IO){
val response= tvRepository.getShows().awaitResponse()
if(response.isSuccessful{
//api success you can get result from response.body
}
else{
//api failed
}
}
}
}

How to give business logic to the ViewModel Kotlin?

I'm working on a training project as a student, but I can't figure out how to transfer my business logic from NewItemActivity to the NewItemViewModel. It turned out that I have all the logic for validating and creating the ItemModel inside the fragment. It's not good to do this, these are all parts of the business logic that need to be given to the viewModel. How can I transfer business logic to the ViewModel, but at the same time keep the application working correctly? Otherwise I tried and everything broke for me.
NewItemActivity.kt
class NewItemActivity : AppCompatActivity() {
private val calendar: Calendar = Calendar.getInstance()
private val viewModel: NewItemViewModel by viewModels(factoryProducer = {
NewItemViewModel.Factory()
})
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new_item)
val saveButton = findViewById<Button>(R.id.saveButton)
val editDate = findViewById<EditText>(R.id.editDate)
val date = SimpleDateFormat("dd.MM.yyyy")
val dateDefault = date.format(calendar.timeInMillis)
editDate.setText(dateDefault)
editDate.setOnClickListener {
showDatePickerDialog()
}
saveButton.setOnClickListener {
checkStateDescriptionLayout()
if (checkStateTitleLayout()) return#setOnClickListener
if (checkStateDescriptionLayout()) return#setOnClickListener
val newItem = ItemModel(
title = editTitle.text.toString(),
description = editDescription.text.toString(),
date = Date(),
isFavorite = false
)
viewModel.saveNewItem(newItem)
Toast.makeText(this, "New item added", Toast.LENGTH_SHORT).show()
finish()
}
textChangedListener()
}
private fun showDatePickerDialog() {
val datePickerDialog = DatePickerDialog(
this#NewItemActivity,
{ _, year, monthOfYear, dayOfMonth ->
val selectedDate: String =
dayOfMonth.toString() + "." + (monthOfYear + 1) + "." + year
editDate?.setText(selectedDate)
},
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
)
datePickerDialog.datePicker.maxDate = calendar.timeInMillis
datePickerDialog.show()
}
private fun checkStateTitleLayout(): Boolean {
val titleLayout = findViewById<TextInputLayout>(R.id.editTitleLayout)
val checkTitleLayoutState = titleLayout.editText?.text?.toString()
val fieldIsRequired = getString(R.string.fieldIsRequired)
val error: Boolean = checkTitleLayoutState!!.isEmpty()
if (error) titleLayout.error = fieldIsRequired
return error
}
private fun checkStateDescriptionLayout(): Boolean {
val descriptionLayout = findViewById<TextInputLayout>(R.id.editDescriptionLayout)
val checkDescriptionLayoutState = descriptionLayout.editText?.text?.toString()
val fieldIsRequired = getString(R.string.fieldIsRequired)
val error: Boolean = checkDescriptionLayoutState!!.isEmpty()
if (error) descriptionLayout.error = fieldIsRequired
return error
}
private fun textChangedListener() {
val titleLayout = findViewById<TextInputLayout>(R.id.editTitleLayout)
val descriptionLayout = findViewById<TextInputLayout>(R.id.editDescriptionLayout)
titleLayout.editText?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable?) {
titleLayout.error = null
titleLayout.isErrorEnabled = false
}
})
descriptionLayout.editText?.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
override fun afterTextChanged(s: Editable?) {
descriptionLayout.error = null
descriptionLayout.isErrorEnabled = false
}
})
}
}
NewItemViewModel.kt
class NewItemViewModel(
private val repository: MyItemsRepository
) : ViewModel() {
fun saveNewItem(item: ItemModel) = repository.saveNewItem(item)
class Factory : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return NewItemViewModel(MyItemsRepositoryImpl.getInstance()) as T
}
}
}
It seems like your main business logic is contained in onCreate, and that's not a big problem when you have little business logic, plus your logic is basically one event (saving the ItemModel object).
If you still want to move more logic to the ViewModel, move the creation of ItemModel to the ViewModel and provide functions to change each parameter of ItemModel there. It should look like this:
class NewItemViewModel(
private val repository: MyItemsRepository
) : ViewModel() {
private var title = ""
private var description = ""
private var date = Date()
private var isFavorite = false
fun setTitle(s: String) { title = s }
fun setDescription(s: String) { description = s }
fun setDate(d: Date) { date = d }
fun setIsFavorite(b: Boolean) { isFavorite = b }
fun saveNewItem() = repository.saveNewItem(Item(title, description, date, isFavorite))
class Factory : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return NewItemViewModel(MyItemsRepositoryImpl.getInstance()) as T
}
}
}
You might think it's more trouble than it's worth, and you will be right. However, there's no harm to preparing for more complicated logic ;)
P.S. I have not tested this code, but it should be basically correct.

Android Kotlin RecyclerView Search Item with EditText with data from Data Class

So I have a RecyclerView which data is populated with data from some Data Class. I want to Apply a search item function for this recyclerview. Back when I don't use Data Classes, I followed this tutorial (The tutorial is in Java).
Now that I'm using a data class since I fetch the data that populate the recyclerview from an API endpoint, I'm quite confused on how to apply the search function in the current recyclerview.
(I fetch the data using library Retrofit if you wondering.)
This is the code snippet from the fragment:
RefundListFragment.kt
private fun fetchRefundListData() {
NetworkConfig().getRefundListDetailService().getRefundList().enqueue(object :
Callback<RefundListPOJODataClass> {
override fun onFailure(call: Call<RefundListPOJODataClass>, t: Throwable) {
...
...
...
}
override fun onResponse(
call: Call<RefundListPOJODataClass>,
response: Response<RefundListPOJODataClass>
) {
binding.refundProgressBar.visibility = View.GONE
binding.rvRefundList.adapter =
response.body()?.let { RefundListAdapter(it, this#RefundListFragment) }
fun filterString(text: String) {
val filteredList: List<RefundListPOJODataClass> = ArrayList()
for (item in response.body()?.data!!) {
if (item != null) {
if (item.buyerName?.toLowerCase()?.contains(text.toLowerCase())!!) {
filteredList.add(item)
}
}
}
adapter.filterList(filteredList)
}
binding.refundListSearchBar.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
filterString(s.toString())
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
}
})
}
However it returned an error:
Unresolved reference: add
This is the recyclerview Adapter:
RefundListAdapter.kt
class RefundListItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val refundedOrderId: TextView = itemView.refundedOrderId
private val orderDateTime: TextView = itemView.orderDateTime
private val refundedCustomerName: TextView = itemView.refundedCustomerName
fun bind(
refundHistory: RefundListPOJODataClassDataItem,
clickListener: RefundListOnItemClickListener
) {
refundedOrderId.text = refundHistory.orderId
orderDateTime.text = refundHistory.orderDate
refundedCustomerName.text = refundHistory.buyerName
itemView.setOnClickListener {
clickListener.onItemClicked(refundHistory)
}
}
}
class RefundListAdapter(
private var refundList: RefundListPOJODataClass,
private val itemClickListener: RefundListOnItemClickListener
) : RecyclerView.Adapter<RefundListItemHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RefundListItemHolder {
val v = LayoutInflater.from(parent.context)
.inflate(R.layout.refund_list_item_layout, parent, false)
return RefundListItemHolder(v)
}
override fun getItemCount(): Int {
if (refundList.data != null) {
return refundList.data!!.size
} else {
return 0
}
}
override fun onBindViewHolder(holder: RefundListItemHolder, position: Int) {
val refundHistory = refundList.data?.get(position)
if (refundHistory != null) {
holder.bind(refundHistory, itemClickListener)
}
}
fun filterList(filteredList: List<RefundListPOJODataClass>) { //This is the function I called in the fragment
refundList = filteredList.get(0)
notifyDataSetChanged()
}
}
interface RefundListOnItemClickListener {
fun onItemClicked(refundHistory: RefundListPOJODataClassDataItem)
}
And this is how the data class looks like
RefundListPOJODataClass.kt
data class RefundListPOJODataClass(
#field:SerializedName("data")
val data: List<RefundListPOJODataClassDataItem?>? = null,
#field:SerializedName("error")
val error: Error? = null
)
data class RefundListPOJODataClassError(
#field:SerializedName("msg")
val msg: String? = null,
#field:SerializedName("code")
val code: Int? = null,
#field:SerializedName("status")
val status: Boolean? = null
)
data class RefundListPOJODataClassDataItem(
#field:SerializedName("order_date")
val orderDate: String? = null,
#field:SerializedName("buyer_name")
val buyerName: String? = null,
#field:SerializedName("phone_number")
val phoneNumber: String? = null,
#field:SerializedName("status_refund")
val statusRefund: String? = null,
#field:SerializedName("order_id")
val orderId: String? = null,
#field:SerializedName("refund")
val refund: Int? = null,
#field:SerializedName("refund_date")
val refundDate: String? = null
)
I want to search the recyclerview item by the attribute buyerName. How can I achieve it ? What should i change in my code? If there's any detail I missed to tell, Just let me know.
Your filtered list is of type List, which is immutable.
Change it to array list or MutableList:
val filteredList: ArrayList<RefundListPOJODataClass> = ArrayList()

problem populate spinner android kotlin with room database

I want to populate spinner with room database in kotlin but items in spinner are different. the number of items are true. exactly I want to make ArrayList of title in Category Class and show them in spinner in a fragment
#Entity(tableName = "cat_table")
class Category(val title: String,
val fulType: Int,
val SaleP: Long,
val BuyP: Long,
var created: Date = Date()
) {
#PrimaryKey(autoGenerate = true)
var id: Int = 0
}`
by this an want to show all title in ArrayList and set to spinner
private fun initData() {
val packageTypesAdapter = context?.let {ArrayAdapter<Any>(it,android.R.layout.simple_spinner_item)}
catViewModel!!.allCats.observe(viewLifecycleOwner, Observer { packageTypes ->
packageTypes?.forEach {packageTypesAdapter!!.add(it)}
})
spinner2.adapter = packageTypesAdapter
spinner2.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>, view: View, i: Int, l: Long) {
Toast.makeText(context, "$packageTypesAdapter", Toast.LENGTH_SHORT).show()
//if (i == 0) {
// loadAllTodos()
//} else {
// val string: String = parent.getItemAtPosition(position).toString()
// loadFilteredTodos(string)
//}
}
override fun onNothingSelected(adapterView: AdapterView<*>) {
}
}
}`
if use this query
#get:Query("SELECT * FROM cat_table WHERE title ")
val allCatTitle: LiveData<List<Category>>
spinner nothing to show and below query are like this picture
#get:Query("SELECT * FROM cat_table ORDER BY created DESC")
val allCatByDate: LiveData<List<Category>>
Please check the photo
In case someone else bumps into the same issue. Here's how I did it a while ago (Kotlin).
Dao
#Query("SELECT deviceName FROM device_table WHERE deviceType = :deviceType")
fun getDevice(deviceType: String): LiveData<List<String>>
Repository
fun getDevice(deviceType: String): LiveData<List<String>> {
return deviceDao.getDevice(deviceType)
}
ViewModel
fun getDevice(deviceType: String): LiveData<List<String>> {
return repository.getDevice(deviceType)
}
And finally in my fragment:
private fun initSpinnerData() {
val spinner3 = view?.findViewById<Spinner>(R.id.spinnerRecord_devices)
if (spinner3 != null) {
val allDevices = context?.let {
ArrayAdapter<Any>(it, R.layout.spinner_text)
}
mDevicesViewModel.getDevice("Appliance")
.observe(viewLifecycleOwner, { devices ->
devices?.forEach {
allDevices?.add(it)
}
})
spinner3.adapter = allDevices
spinner3.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
Toast.makeText(requireContext(), "$allDevices", Toast.LENGTH_LONG).show()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
}
}

Instantiating an Interface Listener in Kotlin

I cannot, for the life of me, instantiate an interface outside of a fragment in Kotlin or Kotlin for Android. It was standard procedure in Java to say something like:
MyInterface mInterfaceListener = new MyInterface(this);
mInterfaceListener.invokeSomeGenericMethod();
Note that mInterfaceListener is referring to an Interface, not an onCLickListener or anything like that
How are interfaces instantiated in Kotlin? How do I make a "listener" and trigger an interface's functions?
Below are some attempts in a very simple app I am doing for learning purposes. Notice the variable mPresenterListener which is an Interface
class QuoteActivity : QuoteContract.ViewOps, AppCompatActivity() {
private lateinit var vText: TextView
private lateinit var vFab: FloatingActionButton
private lateinit var mPresenterListener: QuoteContract.PresenterOperations
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mPresenterListener = this.mPresenterListener
vText=findViewById(R.id.main_quote)
vFab=findViewById(R.id.main_fab)
vFab.setOnClickListener{
mPresenterListener.onQuoteRequested()
}
}
override fun displayQuote(quote: String) {
vText.text = quote
}
}
And my presenter:
class QuotePresenter(private val viewListener: QuoteContract.ViewOps): QuoteContract.PresenterOperations {
private lateinit var modelListener: QuoteContract.ModelOperations
init {
modelListener = this.modelListener
}
override fun onQuoteRequested() {
modelListener.generateQuote()
}
override fun onQuoteGenerated(quote: String) {
viewListener.displayQuote(quote)
}
}
The interface:
interface QuoteContract {
//Methods available to Presenter (Presenter -> View)
interface ViewOps{
fun displayQuote(quote: String)
}
//Ops offered from presenter to view (Presenter->View)
interface PresenterOperations {
//Presenter->View
fun onQuoteRequested()
//Presenter->Model
fun onQuoteGenerated(quote: String)
}
//Ops offered from Model to Presenter (Model -> Presenter)
interface ModelOperations {
fun generateQuote()
}
}
You can do watchers/listeners like this:
val textView: TextView = this.findViewById(R.id.amountEdit)
val watcher = object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
val inputAmount = textView.text.toString
val amount = if (!inputAmount.isEmpty()) inputAmount.toDouble() else 0.0
conversionViewModel?.convert(amount)
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
println("before text changed called..")
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
println("text changed..")
}
}
and then:
textView.addTextChangedListener(watcher)
Notice the object in declaring the watcher. Can do the same with a listener.
Also we can use listener without some interface and with default value like:
val someButtonListener: (isChecked: Boolean) -> Unit = {_ -> }
val someButtonListener: (v: View) -> Unit = {_ -> }

Categories

Resources