Instantiating an Interface Listener in Kotlin - android

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 = {_ -> }

Related

How To get value of SeekBar and use it on another class or method

I'm new in Kotlin and I want to use three Seekbar on my app for RGB control;
I want to set seekBarManger function to manage my seekbar on SeekBarManger class and use seekbar.progress value on other class
but I cant get value of my seekbar.
the Toast is Show but I want to use the value of seekbar.progrees in other method and class.
please help me..!!!!
this is my seekbar class :
class SeekBarManager() {
}
fun seekBarManage(context: Context, seekBar: SeekBar) {
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) { }
override fun onStartTrackingTouch(p0: SeekBar?) {}
override fun onStopTrackingTouch(p0: SeekBar?) {
val result = seekBar.progress
Toast.makeText(context, "Progress is: $result%", Toast.LENGTH_SHORT).show()
}
})
and this is my MainActivity :
open class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val seekRed = findViewById<SeekBar>(R.id.seekBar_red)
val seekGreen = findViewById<SeekBar>(R.id.seekBar_green)
val seekBlue = findViewById<SeekBar>(R.id.seekBar_blue)
val seekEndRed = seekBarManage(this , seekRed)
val seekEndGreen = seekBarManage(this , seekGreen)
val seekEndBlue = seekBarManage(this , seekBlue)
the Toast is Show but I want to use the value of seekbar.progrees in other method and class
the easiest way is to use a companion object in your Main class and just call a method and pass your seek bar object like below :
open class MainActivity : AppCompatActivity() {
companion object {
fun getSeekBarProgress(context: Context, seekBar: SeekBar) {
val result = seekBar.progress
Toast.makeText(context, "Progress is: $result%", Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val seekRed = findViewById<SeekBar>(R.id.seekBar_red)
val seekGreen = findViewById<SeekBar>(R.id.seekBar_green)
val seekBlue = findViewById<SeekBar>(R.id.seekBar_blue)
val seekEndRed = seekBarManage(this, seekRed)
val seekEndGreen = seekBarManage(this, seekGreen)
val seekEndBlue = seekBarManage(this, seekBlue)
}
}
and SeekBarManager class :
class SeekBarManager() {
}
fun seekBarManage(context: Context, seekBar: SeekBar) {
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {}
override fun onStartTrackingTouch(p0: SeekBar?) {}
override fun onStopTrackingTouch(p0: SeekBar?) {
MainActivity.getSeekBarProgress(context, seekBar)
}
})
}

How to get Call Back in another class when text size 4 from TextWatcher

This is my GenericTextEnterPin Class
class GenericTextEnterPinPassword (private val view: View, private val editText: ArrayList<EditText>) : 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?) {
val string = s.toString()
when (view.id) {
R.id.otp_edit_box5 -> {
if (string.length == 1) editText[1].requestFocus()
}
R.id.otp_edit_box6 -> {
if (string.length == 1) editText[2].requestFocus() else if (string.isEmpty()) editText[0].requestFocus()
}
R.id.otp_edit_box7 -> {
if (string.length == 1) editText[3].requestFocus() else if (string.isEmpty()) editText[1].requestFocus()
}
R.id.otp_edit_box8 -> {
if (string.isEmpty()) editText[2].requestFocus()
// here we are getting 4 size i want sethere and want to get callback in another class
}
}
}
}
i want to getCall Back in my widget class where we are displaying some other view but i am not getting how to get call back in another class below is my class where i want to call back .
class AppLockWidgetImpl #Inject constructor(
private val context: Context,
override val onClicked: SingleLiveData<CallToAction>
) : AppLockWidget {
}
You can pass your callback as a lambda function to GenericTextEnterPinPassword.
class GenericTextEnterPinPassword (
private val view: View,
private val editText: ArrayList<EditText>,
private val callback: () -> Unit
): TextWatcher {
// ...
R.id.otp_edit_box8 -> {
if (string.isEmpty()) editText[2].requestFocus()
callback()
}
// ...
}
Usage:
val textWatcher = GenericTextConfirmPassword(otp_edit_box11, edit) {
// Whatever you wish to do upon callback
}
otp_edit_box11.addTextChangedListener(textWatcher)

Using roomdatabase query for search between products

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)
}
})
}

How do I resolve this diamond problem in Kotlin?

I have an Interface that I use as the common data source for my RecyclerView Adapters, which looks like this:
interface GenericRVAdapterDataSource {
fun getCellCount() : Int
fun getViewModelForCell(position : Int) : CellViewModel
}
Now, I have two other Interfaces that extend this one:
interface GroupHomeDataSource : GenericRVAdapterDataSource {
fun getJoinedGroupsCount() : Int
fun getJoinedGroupViewModel(forIndex : Int) : GroupHomeCellViewModel
override fun getCellCount(): Int = getJoinedGroupsCount()
override fun getViewModelForCell(position: Int): CellViewModel = getJoinedGroupViewModel(position)
}
and:
interface GroupSuggestedDataSource : GenericRVAdapterDataSource {
fun getSuggestedGroupsCellCount() : Int
fun getSuggestedGroupViewModelForCell(atIndex : Int) : GroupHomeCellViewModel
override fun getCellCount(): Int = getSuggestedGroupsCellCount()
override fun getViewModelForCell(position: Int): CellViewModel = getSuggestedGroupViewModelForCell(position)
}
However, when I implement both interfaces into the class:
class GroupHomeViewModel(app : Application) : AndroidViewModel(app), GroupHomeDataSource, GroupSuggestedDataSource, GroupsHomeInteractionLogic {...}
I got the error:
Class 'GroupHomeViewModel' must override public open fun getCellCount(): Int defined in GroupHomeDataSource because it inherits multiple interface methods of it
For now, I've avoided the problem by just storing both interfaces as variables:
val joinedGroupsDataSource = object: GroupHomeDataSource {
override fun getJoinedGroupsCount(): Int = joinedGroupsList.size
override fun getJoinedGroupViewModel(forIndex: Int): GroupHomeCellViewModel = joinedGroupsList[forIndex]
}
val suggestedGroupsDataSource = object: GroupSuggestedDataSource {
override fun getSuggestedGroupsCellCount(): Int = suggestedGroupsList.size
override fun getSuggestedGroupViewModelForCell(atIndex: Int): GroupHomeCellViewModel = suggestedGroupsList[atIndex]
}
However, I'm not sure that's the most effective way to resolve this diamond problem - if I can even call it that.
Do I just do what the compiler tells me to do and implement getCellCount() and redirect it to one of the interfaces' implementations using:
//MARK:- super interface implementation
override fun getCellCount(): Int {
return super<GroupHomeDataSource>.getCellCount()
//Or: return super<GroupSuggestedDataSource>.getCellCount()
}
override fun getViewModelForCell(position: Int): CellViewModel {
return super<GroupHomeDataSource>.getViewModelForCell(position)
//Or: return super<GroupSuggestedDataSource>.getViewModelForCell(position)
}
//ENDMARK
Or do I implement that method while determining which of the interfaces calls for it (is there a method for this)?
The compiler cannot choose between multiple implementations on its own. But, the whole implementation looks a little overwhelmed. Usually you shouldn't create extended DataSource for each case, use a Generic interface instead. If GroupHomeViewModel provides multiple data sources, just create different properties, as you did.
interface CellViewModel
interface GroupHomeCellViewModel : CellViewModel
interface RVAdapterDataSource<T : CellViewModel> {
fun getCellCount() : Int
fun getViewModelForCell(position : Int) : T
}
class ListAdapterDataSource<T : CellViewModel>(
private val list: List<T>
) : RVAdapterDataSource<T> {
override fun getCellCount() = list.size
override fun getViewModelForCell(forIndex: Int) = list[forIndex]
}
class GroupHomeViewModel(
joinedGroupList: List<GroupHomeCellViewModel>,
suggestedGroupList: List<GroupHomeCellViewModel>
) {
val joinedGroupsDataSource = ListAdapterDataSource(joinedGroupList)
val suggestedGroupsDataSource = ListAdapterDataSource(suggestedGroupList)
}

Retrieve Variable from onBindViewHolder in Kotlin

I am trying to retrieve the variable value in my main class so, I can load this value into my database. I am not sure how to retrieve onBindViewHolder value in MainClas. I am able to display the item in the activity.
Complete Code requested by user.
class TestProjectMenuDetail() : AppCompatActivity() {
var itemName=""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView_main.setBackgroundColor(Color.WHITE)
recyclerView_main.layoutManager= LinearLayoutManager(this)
supportActionBar?.title=navBarTitle
fetchJSON()
}
//Retrieve value from OnBindViewHolder
fun setVariable(ItemName:String)
{
itemName=ItemName
}
}
private class MenuDetailListAdapter(val TestProjectMenudetails:Array<TestProjectMenuDetails>,context: Context): RecyclerView.Adapter<TestProjectDetailMenuViewHolder>()
{
private val TestVar:TestMenuDetail= context as TestMenuDetail
override fun onBindViewHolder(p0: TestProjectDetailMenuViewHolder, p1: Int) {
val TestProjectmenudetail=TestProjectMenudetails.get(p1)
p0?.customView?.itemname.text=TestProjectmenudetail.menu
TestVar.setVariable(TestProjectmenudetail.menu)
}
override fun getItemCount(): Int {
return TestProjectMenudetails.size
}
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): TestProjectDetailMenuViewHolder {
val layoutInflater= LayoutInflater.from(p0?.context)
val customView=layoutInflater.inflate(R.layout.activity_TestProject_menu_detail,p0,false)
return TestProjectDetailMenuViewHolder(customView)
}
}
#Suppress("DEPRECATION")
class TestProjectDetailMenuViewHolder(val customView: View, var Menus:TestProjectMenu?=null): RecyclerView.ViewHolder(customView)
{
companion object {
val DISHES_TITLE_NAME="ITEM_NAME"
val intialcount:Int=0
}
init {
customView.setOnClickListener {
}
}
fun AddClick()
{
val intent=Intent(customView.context,TestProjectMenuList::class.java)
customView.context.startActivity(intent)
}
}
In MainClas declare a variable to hold the value you want to pass from onBindViewHolder like activityVar and then create a method:
fun setVariable(myVariable: Int) {
activityVar = myVariable
}
replace Int with the proper data type.
Change your adapter class's header like this:
private class TestDetailListAdapter(context: Context, val ItemTestdetails:Array<ItemTestDetails>)
so you need to pass to it first the context of your activity by passing this
and inside onBindViewHolder add this:
val mainclas: MainClas = context as MainClas
mainclas.setVariable(variablename)
replace variablename with the name of the variable you want to pass

Categories

Resources