Kotlin Android - Unable to get #BindingAdapter to work - android

I keep getting the binding error when trying to use the #BindingAdapter. Try for 3 days and follow numerous online articles on this subject, but still getting the below error.
#BindingAdapter("focusableColor")
fun setFocusableColor(v:CardView, color:Int) {
println("hello")
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding:ActivityMainBinding =
DataBindingUtil.setContentView(this,R.layout.activity_main)
etc...
}
In current_task_layout.xml
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="task" type="com.edenhan.simplytask.Task">
</variable>
</data>
<android.support.v7.widget.CardView
android:id="#+id/card_view">
.....
focusableColor="#{1}"/>
Error encountered:
Found data binding errors.
****/ data binding error ****msg:Cannot find the setter for attribute ‘focusableColor’ with parameter type int on
android.support.v7.widget.CardView.
file:D:\…….\app\src\main\res\layout\current_task_layout.xml

Have you tried moving the binding out of the companion object?
You should put it in a kotlin file and make it a top level function. For example:
Bindings.kt
#BindingAdapter("focusableColor")
fun setFocusableColor(v:CardView, color:Int) {..}
And put the binding xml in the app namespace
Also, see Kotlin custom attribute databinding
edit: full example
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
#BindingAdapter("focusableColor")
fun setColor(card: CardView, #ColorInt color: Int) {
// or whatever
card.setBackgroundColor(color)
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v7.widget.CardView
android:id="#+id/card"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:focusableColor="#{1}"/>
</android.support.constraint.ConstraintLayout>
</layout>

Below defined full code for load image using BindingAdapter with Kotlin
ImageLoader.kt
import android.widget.ImageView
import androidx.databinding.BindingAdapter
import androidx.databinding.ObservableField
class ImageLoader {
val imageResource = ObservableField(R.drawable.ic_launcher_background)
companion object {
#JvmStatic
#BindingAdapter("android:src")
fun setImage(imageView: ImageView, imageRes: Int) {
imageView.setImageResource(imageRes)
}
}
}
activity_home.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="imageLoader" type="com.sample.testdemo.ImageLoader"/>
</data>
<RelativeLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/app_name"
android:src="#{imageLoader.imageResource}"
android:layout_centerInParent="true"/>
</RelativeLayout>
</layout>
HomeActivity.kt
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.sample.testdemo.databinding.ActivityHomeBinding
class HomeActivity : AppCompatActivity() {
lateinit var binding: ActivityHomeBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_home)
binding.imageLoader = ImageLoader()
}
}
Note: Don't forget to add below line at the top of app level build.gradle
apply plugin: 'kotlin-kapt'

Related

Problems with webview

I'm creating an application that displays a webview and has a button that opens the Android settings. My problem is that when I add this button, the webview stops working and only show a white screen that doesn't charge. What am I doing wrong? I leave the code below:
PD: If I delete the code starting from "//init code" it works....
MainActivity.kt
package com.becas.apn_mexico
import android.content.Intent
import android.os.Bundle
import android.provider.Settings
import android.view.View
import com.becas.apn_mexico.base.BaseActivity
import com.becas.apn_mexico.databinding.ActivityMainBinding
import com.google.android.material.floatingactionbutton.FloatingActionButton
class MainActivity : BaseActivity<ActivityMainBinding>() {
companion object {
private const val URL_LINK_WEBSITE = "https://apn.tutorialez.net/index.php/landing-page/"
}
override fun setupViewBinding(): ActivityMainBinding {
return ActivityMainBinding.inflate(layoutInflater)
}
override fun setupViewModel() {
}
override fun setupUI(savedInstanceState: Bundle?) {
binding.mainWebview.loadUrlExt(URL_LINK_WEBSITE)
showAdBanner(binding.adsView.adsPhoneTabSpecialSmartBanner)
showAdInterstitial(getString(R.string.admob_interstitial))
}
// Init Code
private lateinit var fab: FloatingActionButton
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
fab = findViewById(R.id.abrir_configuracion)
// Configurando el setonclicklistener
fab.setOnClickListener(View.OnClickListener{
val i = Intent(Settings.ACTION_APN_SETTINGS)
startActivity(i) })
}
}
ActivityMain.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.becas.apn_mexico.MainActivity">
<WebView
android:id="#+id/main_webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/ads_view" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/abrir_configuracion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_marginRight="10dp"
android:layout_alignParentRight="true"
android:layout_above="#id/ads_view"
android:clickable="true"
android:backgroundTint="#color/colorPrimary"
android:contentDescription="#string/open_config_apn"
app:srcCompat="#drawable/ic_baseline_settings_24" />
<include
android:id="#+id/ads_view"
layout="#layout/ads_phone_tab_special_smart_banner" />
</RelativeLayout>
I think your problem is that you are not using the viewbinding to inflate your view:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// You need to get here the binding and put it into the ContentView method
val view = binding.root
setContentView(view)
fab = findViewById(R.id.abrir_configuracion)
// Configurando el setonclicklistener
fab.setOnClickListener(View.OnClickListener{
val i = Intent(Settings.ACTION_APN_SETTINGS)
startActivity(i) })
}
Alternatively, the problem is in your layout. Can you remove the
<include
android:id="#+id/ads_view"
layout="#layout/ads_phone_tab_special_smart_banner" />
From your layout and let me know if you get the expected result?

Kotlin-Android First App Unresolved Reference TextView with Button

I'm new to android and kotlin development and I am having trouble with my first application.
I am looking to edit my TextView on button click but the compiler is complaining about an unresolved reference.
Here is my activity_main.xml file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="#+id/click_me"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onTap"
android:text="Click me"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="64dp"
android:text="Not clicked..."
app:layout_constraintStart_toStartOf="#+id/click_me"
app:layout_constraintTop_toBottomOf="#+id/click_me" />
</androidx.constraintlayout.widget.ConstraintLayout>
and here is my MainActivity.kt file
package com.example.helloworld
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
//Method will be called on button click
fun onTap(view: View) {
textView.text = "Clicked"
}
}
}
you should define the textview as variable and then you could change it
val textView = findViewById(R.id.text_view_id)  
textView.setText("string").toString()  
val textViewValue = textView.text  
As other user already commented, you should use view binding.
On your app Gradle script.
android {
...
buildFeatures {
viewBinding = true
}
}
Then on your MainActivity
private lateinit var binding: ActivityMainBinding
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.yourButtonID.setOnClickListener {buttonTtapped()}
}
fun buttonTtapped() {
binding.yourTextViewID.text = "Clicked"
}
}
Something like this
you need to apply this in your build.gradle file
plugins {
id 'kotlin-android-extensions'}
and press sync now
then back to your MainActivity it will suggest u to import that view
Kotlin has a feature Say goodbye to findViewById refered for Kotlin Android Extensions. You need to import the import kotlinx.android.synthetic.main.activity_main.* so that you directly use the ID name of the view directly.
To get this work you need to add this plugin in the module-level build.gradle file
apply plugin: 'kotlin-android-extensions'
Another approach by findViewById
class SampleActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun onTap(view: View){
val myTextView = findViewById<TextView>(R.id.textView)
myTextView.text = "absc"
}
}

Not Getting Edit Text Data

I am New to Android . I don't know how to get the data of EditText in Android .
Here is my Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity2">
<EditText
android:id="#+id/edOne"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<EditText
android:id="#+id/edSecond"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="#+id/btnAdd"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Here is my code :
val one=edOne.text.toString()
btnAdd.setOnClickListener{
val two=edOne.text.toString()
Log.e("Tag",one +" "+two)
}
one is calling from onCreate() method . and at the time of creation of you onCreate() you don't have any data in editText one .So you can't get any data from one if you have not added from xml .
So just move your one code into click listener.
val one=edOne.text.toString()
btnAdd.setOnClickListener{
val two=edOne.text.toString()
Log.e("Tag",one +" "+two)
}
You can access related View elements with findViewById and use them later.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.EditText
class MainActivity : AppCompatActivity() {
private lateinit var edOne : EditText
private lateinit var btnAdd : Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
edOne = findViewById(R.id.edOne)
btnAdd = findViewById(R.id.btnAdd)
val one=edOne.text.toString()
btnAdd.setOnClickListener {
val two=edOne.text.toString()
Log.e("Tag",one +" "+two)
}
}
}

BindingAdapter is not working as expected

I am trying to add a custom binding to my TextView. Please find my code below
MainActivity.kt
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.builders.bindingadaptertest.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
val model = ViewModelProvider(this).get(MainViewModel::class.java)
}
}
MainViewModel.kt
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class MainViewModel: ViewModel() {
var data = MutableLiveData<String>()
init {
data.value = "This works"
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.builders.bindingadaptertest.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:customBind="#{viewModel.data}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
BindingAdapters.kt
#BindingAdapter("app:customBind")
fun customBind(view: TextView, data: String?) {
Log.d("DEBUG_CUSTOM", "Setting custom [$data] listner")
view.text = data
}
Doubt: When I run this project, The log cat shows the data passing to customBind is null.
2020-11-30 09:37:33.351 4682-4682/com.builders.bindingadaptertest D/DEBUG_CUSTOM: Setting custom [null] listner
Can anyone help me to get the correct data from ViewModel into the binding adapter?
It's null, because in your databinding viewModel is not set.
You forgot to add binding.viewModel = model
Also, some possible suggestions:
No need to leave the LiveData reference mutable, just use val
I'd prefer an extension function for the binding adapter: fun TextView.customBind(data: String?)
You should set a lifecycleOwner on your binding, otherwise your activity may keep an indirect reference on your vm, which will cause leaks on orientation changes

How to use TextInputLayout in preferenceScreen

I am trying to use TextInputLayout in a preference screen so as have a nice neat password box that has a show password option. I know there are ways and means of doing it with checkboxes and scripting but I would really love to be to just use the TextInputLayout, maybe wrapped or in a custom widget. Gradle dependencies are correct as it works fine in other activity screens.
The following preferences.xml crashes with an "Error inflating class com.google.android.material.textfield.TextInputLayout"
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
</androidx.preference.PreferenceScreen>
Ok I worked out the answer (and learned a lot in the process). Took a lot of research and trial and error but I hope the following might prove helpful to someone similarly frustrated.
The custom preference layout:
password.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/textLay"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="66dp"
android:hint="#string/Set_Password"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:passwordToggleEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="#android:id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
The preference screen layout:
preferences.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.toggen.myapplication.PasswordPreference
android:key="pref_password"
android:layout="#layout/password" />
</PreferenceScreen>
SettingsActivity.kt
package com.toggen.myapplication
import kotlinx.android.synthetic.main.password0.view.*
import androidx.appcompat.app.AppCompatActivity
import android.util.AttributeSet
import android.widget.EditText
import android.content.Context
import androidx.preference.*
import android.os.Bundle
class PasswordPreference(context: Context, attrs: AttributeSet?) : Preference(context, attrs){
private var passW: EditText? = null
override fun onBindViewHolder(holder: PreferenceViewHolder?) {
super.onBindViewHolder(holder)
passW = holder?.itemView?.textLay?.editText?.apply{ setText(getPersistedString("admin")) }
}
override fun onDetached() {
super.onDetached()
passW?.apply{ persistString("$text") }
}
}
class SettingsActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if(savedInstanceState == null) {
supportActionBar?.title = "Settings"
supportFragmentManager.beginTransaction()
.replace(android.R.id.content, SettingsFragment()).commit()
}
}
override fun onSupportNavigateUp() = onBackPressed().run { true }
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) = setPreferencesFromResource(R.xml.preferences, rootKey)
}
}
Finally the answer that I've been looking for!! THANK YOU!!
Only modification that I would make is to use:
android:dialogLayout="#layout/password"
instead of:
android:layout="#layout/password"

Categories

Resources