How to use android data binding properly - android

I have the following XML with data binding:
<EditText
android:id="#+id/addvalue"
android:inputType="numberDecimal"
android:digits="0123456789,€"
android:text="0,00 €"/>
<Button
android:id="#+id/add"
android:onClick="#{() -> fragment.addManualPosition(addvalue.text)}"/>
I get the error
"data binding error ****msg:if getId is called on an expression, it should have an id: addvalue.text"
I can not find anything at all that would help me understand what this error is supposed to mean or how to fix it.

I found it myself.
However because there is absolutely nothing written about this error message and it being extremely misleading, I'll keep the question online.
Fix:
The lambda is a perfectly fine Kotlin lambda.
However Data Binding seems to generate Java code.
So it must not be addvalue.text but addvalue.getText() .

Related

What is the difference between a binding.name.text && name.text if data binding is enabled in both cases

I just began to learn data binding and I have some troubles in understanding its technique
in my following code, I have enabled data binding in the app Gradle file in order to use it and get rid of the findviewbyid() ... So I've created the binding variable as lateinit before the oncreate() function and then I initialized it in the on create fun like this: binding = DataBindingUtil.setContentView(this, R.layout.activity_main) and I will give you an example of a view in my XML file to complete on ...
<EditText
android:id="#+id/nickName_editText"
style="#android:style/Widget.DeviceDefault.Light.EditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/Margin"
android:layout_marginTop="#dimen/Margin"
android:layout_marginRight="#dimen/Margin"
android:hint="#string/hint"
android:inputType="textPersonName"
android:textAlignment="center" />
Back to the kotlin file what is the difference between these two lines of code below (*Both lines are working)
nickName_editText.visibility = View.VISIBLE
binding.nickNameEditText.visibility = View.VISIBLE
I know that we want to get rid of the findviewbyid() to make the app faster but why don't we do it like in the first line and we are not calling findViewById() too
I'm new to android development so I might not be able to understand that complicated answers :‑D
Thanks for helping!
These are both forms of data binding, binding.nicknameEditText is Androids implementation, and the recommended approach. "nickName_editText" is Kotlin data binding and has known bug issues, when you get into more complicated views they'll start to pop up.
Expanded your imports in the MainActivity, you'll notice the following import.
import kotlinx.android.synthetic.main.activity_main.*
The start indicates binding for all views in the layout.
If you "remove kotlinx.android.synthetic.main.activity_main.*" you'll notice that the "nickName_EditText" is now undefined.
You can also view the Kotlin byte code by
clicking on tools/Kotlin/show Kotlin ByteCode
If you click on "nickName_editText" you'll notice the bytecode for this section will be highlighted.
Hopefully, this answers all your questions

Android Data Binding BindingAdapter: "Cannot find the setter"

This bound attribute is failing to build, with the error:
Cannot find the setter for attribute "errorText"
#BindingAdapter({"errorText"})
public static void setErrorText(TextInputLayout view, String error) {
view.setError(error);
}
<android.support.design.widget.TextInputLayout
android:id="#+id/email_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:errorEnabled="true"
app:errorText="#{data.usernameError}"
>
....
Why is this not working?
It appears that this error was being caused because the BindingAdapter was not being compiled, as a result of a source error elsewhere in completely unrelated code. That other source error (a typo so a variable being referred to was declared using a different name and so did not exist) was not clear in the build error log but once I saw it in the source code it was easy to fix and in turn fixed the BindingAdapter issue.
If you see this error, check for other possible build errors in the source and build logs.
Firstly, setErrorText is public so you do NOT need define in binding adapter.
It still works without define errorText in BindingAdapter class ( Confirmed ! )
If you want to define in binding adapter, you have to change as below:
#BindingAdapter("app:errorText")
Hopes this help !

Clicking a textview to change fragment

I am trying to build my first android app using Kotlin but I am stuck on a very simple situation. I am using Kotlin android extensions and I am getting a Null pointer exception.
<TextView
android:id="#+id/tvGoToRegisterFrag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/btn_sign_in"
android:text="#string/haven_t_registered_yet"
android:paddingTop="10dp"
android:textColor="#color/colorAccent"
android:layout_centerHorizontal="true"/>
With this I used
tvGoToRegisterFrag.setOnClickListener {
goToRegister()
}
I do know I can use
tvGoToRegisterFrag?.setOnClickListener {
goToRegister()
}
To get rid of crashing but still I am not sure why the object is null and does not do anything when clicked. All of this is done in a Fragment if that changes anything.
One reason you may be getting NPE is that you are using it in the wrong lifecycle method I was having a similar error when using it in onCreateView instead you should only use Kotlin android extensions in onViewCreated.

msg:Cannot find the getter for attribute 'android:text' with value type java.lang.String in data binding?

I am trying to use data binding in the edittext, In the morning it is working fine but suddenly got the error:
****/ data binding error ****msg:Cannot find the getter for attribute 'android:text' with value type java.lang.String on android.widget.EditText. file:/home/itstym/ApkProjects/Reminder/app/src/main/res/layout/activity_main.xml loc:20:4 - 34:40 ****\ data binding error ****
EditText at line 20:4 -34:40
<EditText
app:error="#{login.errorEmail}"
android:text="#={login.userEmailAddress}"
android:hint="Email Address"
android:id="#+id/email_address"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:ems="10"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="24dp"/>
View Holder:
#Bindable
var userEmailAddress:String = String()
set(userEmailAddress){
field=userEmailAddress
notifyPropertyChanged(R.id.email_address)
/*to check Email for validation on every character inserted by user*/
notifyPropertyChanged(BR.errorEmail)
}
get() {
return field
}
What went suddenly wrong?
Solution tried:
1. Invalidate cache and restart.
2. Clean project and Rebuild project.
Remove get() method from userEmailAddress as Kotlin provide Synthetic property access in it, you can direct access userEmailAddress without get()
I'd like to bring the answer from another post here (courtesy of Matej Drobnič) :
"I just had that issue and I have managed to solve it by deleting .idea, .gradle
and gradle folders and let Android Studio recreate whole project from
scratch from gradle files."
I have run into this problem many times. It always involves some cleaning, deleting, restarting. For me, this answer is probably the most reliable way to remedy this flaw of Android Studio.

MvvmCross, platform-specific value converter not being invoked

I am using MvvmCross with Xamarin.Android. I have the Visibility plugin installed. In my Android application project, I have created an Android-specific visibility converter that supports the Invisible state (view not shown, but still taking up layout space):
public class VisibleOrInvisibleValueConverter
: MvxValueConverter<bool, ViewStates>
{
public ViewStates Convert(bool value, Type targetType, CultureInfo cultureInfo, object parameter)
{
MvxTrace.Error("VisibleOrInvisibleValueConverter.Convert");
return value ? ViewStates.Visible : ViewStates.Invisible;
}
}
In my .axml markup, I use this converter like this:
<FrameLayout
android:layout_width="0dip"
android:layout_height="5dip"
android:layout_weight="1"
android:background="#ff0000"
local:MvxBind="Visibility Selected, Converter=VisibleOrInvisible, FallbackValue=0" />
Based on the documentation at https://github.com/MvvmCross/MvvmCross/wiki/Value-Converters#referencing-value-converters-in-touch-and-droid, I believe that MvvmCross will automatically discover the existence of this value converter, since it is in my UI project.
At runtime, the bound value always takes on the fallback value, no matter what the value of Selected is. Based on the documentation at https://github.com/MvvmCross/MvvmCross/wiki/Value-Converters#referencing-value-converters-in-touch-and-droid, this means that either my binding source path is missing, or the value converter threw an exception.
Unfortunately, I think I can rule both of those out. For the first possibility, I tried replacing my custom VisibleOrInvisible converter with the stock MvvmCross Visibility converter, and it worked fine. (That is, the binding worked fine. The stock Visibility converter doesn't support my desired behavior, though.) Anyway, I think this shows that the source path (Selected) does exist.
For the second possibility, I've set a breakpoint in the Convert function of the VisibleOrInvisible converter, and it is never executed. I also added an MvxTrace call in there, and I never see the trace message.
Although my converter is supposed to be automatically discovered, I have also tried explicitly adding my platform-specific assembly to the list of assemblies that implement value converters by overriding the ValueConverterAssemblies property getter in Setup.cs:
protected override List<Assembly> ValueConverterAssemblies
{
get
{
var toReturn = base.ValueConverterAssemblies;
toReturn.Add(typeof (VisibleOrInvisibleValueConverter).Assembly);
return toReturn;
}
}
But this did not help.
I think that MvvmCross is discovering my converter OK. If I intentionally refer to a non-existent converter in my .axml file, I see exception messages in the debug trace at runtime. But when I specify my VisibleOrInvisible converter, these messages do not appear.
My working theory is that an exception is occurring in the process of invoking my converter, before the only line of code in the converter is executed. But I don't know how to get to the bottom of that. No exception messages appear in the debug trace.
Is there a simple step that I'm getting wrong? I've studied the MvvmCross ValueConversion example pretty carefully, and I think I'm doing everything that example does.
I've just taken the ValueConversion sample, upgraded the core csproj file to profile 158 and then inserted your value converter.
This converter was picked up fine - I could see it in the converter list using:
protected override void InitializeLastChance ()
{
base.InitializeLastChance ();
var registry = Mvx.Resolve<IMvxValueConverterLookup> ();
var f = registry.Find("VisibleOrInvisible");
Mvx.Trace ("Custom converter was found : {0}", f != null);
}
However, when I tried to use it I saw binding errors about enum/bool/value type mapping... so I can see there is some problem...
After a little digging, it seems the reason for this problem was because your ValueConverter implements an unusual public ViewStates Convert method, instead of overriding the base class Convert method. To fix this I changed the converter to:
public class VisibleOrInvisibleValueConverter
: MvxValueConverter<bool, ViewStates>
{
protected override ViewStates Convert (bool value, Type targetType, object parameter, CultureInfo culture)
{
MvxTrace.Error("VisibleOrInvisibleValueConverter.Convert");
return value ? ViewStates.Visible : ViewStates.Invisible;
}
}
For more on authoring value converters using the MvxValueConverter<...> helpers, see https://github.com/MvvmCross/MvvmCross/wiki/Value-Converters#using-the-mvxvalueconverter-helper (if there's some other sample or document somewhere that has the wrong sample, then "sorry" and please point it out to whoever owns it so they can fix it)
Further, you may find the source for MvxValueConverter helpful - it's hopefully pretty straight-forward to follow how it implements IMvxValueConverter: https://github.com/MvvmCross/MvvmCross/blob/v3.1/CrossCore/Cirrious.CrossCore/Converters/MvxValueConverter.cs
With that problem solved, the next challenge presented by this question is how to use the FallbackValue. I've not fully analysed the trace I saw from this problem but I did experiment with a few other FallbackValue syntax examples - and these all seemed to work correctly:
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="200dp"
android:background="#ff0000"
local:MvxBind="Visibility VisibleOrInvisible(ThisWillNotBeFound), FallbackValue=Visible" />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="200dp"
android:background="#ff0000"
local:MvxBind="Visibility VisibleOrInvisible(ThisWillNotBeFound), FallbackValue=Invisible" />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="200dp"
android:background="#ffff00"
local:MvxBind="Visibility VisibleOrInvisible(ThisWillNotBeFound, FallbackValue=true)" />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="200dp"
android:background="#0000ff"
local:MvxBind="Visibility VisibleOrInvisible(ThisWillNotBeFound, FallbackValue=false)" />
I'm not sure if the numeric fallback value is working for this case currently - suspect it needs more investigation (will log as a potential issue).

Categories

Resources