Android: data-binidng: disable click - android-databinding

I want to disable click when variable isAgree is false:
here code:
<?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>
<import type="android.view.View" />
<variable
name="handler"
type="myproject.ui.SubscribeBrandDialogFragment" />
</data>
<TextView
android:id="#+id/subscribeTextView"
android:layout_width="185dp"
android:layout_height="40dp"
android:layout_marginTop="#dimen/default_margin"
android:background="#{handler.isAgree ? #drawable/border_enable_bg : #drawable/border_disable_bg}"
android:gravity="center"
android:onClick="#{handler.isAgree ? handler.onClickSubscribe() : null}"
android:textColor="#{handler.isAgree ? #color/color_primary : #color/disable_text_color}" />
</layout>
But I get error in android:onClick :
e: [kapt] An exception occurred: android.databinding.tool.util.LoggedErrorException: Found data binding errors.
****/ data binding error ****msg:Cannot find the setter for attribute 'android:onClick' with parameter type void on android.widget.TextView.
file:myproject\layout\subscribe_brand_dialog.xml
loc:100:31 - 100:81
****\ data binding error ****
at android.databinding.tool.processing.Scope.assertNoError(Scope.java:112)
at android.databinding.annotationprocessor.ProcessDataBinding.doProcess(ProcessDataBinding.java:101)
at android.databinding.annotationprocessor.ProcessDataBinding.process(ProcessDataBinding.java:65)
at org.jetbrains.kotlin.kapt3.ProcessorWrapper.process(annotationProcessing.kt:131)
I know that I can fix this by java code. But I want fix this ONLY in xml layout.

Use lambda expression.
android:onClick="#{() -> handler.isAgree ? handler.onClickSubscribe() : null}"

Well a couple things.
First, I would use the controls properties the way they are meant to be used. Setting null on the click handler does not appropriately handle the UI element, you are merely hacking.
You should be disabling the click property or focus property based on your boolean, rather then toggling click handlers to null.
Secondly typically when you are doing Terinaray statements in xml click events I have seen the handler::methodName used rather then handler.methodName to ensure it is handled property in the generated databinding classes for onClick.
Lastly, if you already have the agreed boolean, why not just handle the click in the code. You literally saved yourself 1 line for another line if you think about it.
myClick()
if(isAgree) . //you saved this
.
now you have to do this
ObservableBoolean<> myBool = new ObservableField()
myBool.set(true/false)
of you are adding the
#Bindable to a method.
Either way you are not really saving code by doing the boolean check in the xml and you lose your ability to unit test it or debug. Just my two cents.

Related

Android Data Binding "cannot find method"

This is seemingly old at this point. I have been reviewing Android Data Binding Documentation
As well pouring over posts here on SO however nothing seems to work.
No matter how I format the XML I get the same results. When I originally got this working (using lambda without passing arguments) took me a lot of trial and error. Now that i need to pass View in an onClick, im back to trial and error yet nothing is functional.
MainViewModel.java
private void navClicked(#NonNull View view) {
switch (view.getId()) {
case R.id.btn1:
break;
case R.id.btn2:
break;
}
}
public void testBtn() {}
activity_main.xml
<data>
<variable
name="mainViewModel"
type="com.example.viewmodel.MainViewModel" />
</data>
<!-- Works perfectly -->
<!-- however I would need a method for every button and that becomes redundant -->
<Button
android:id="#+id/testBtn"
android:onClick="#{() -> mainViewModel.testBtn()}"
android:text="#string/testBtn" />
<!-- "msg":"cannot find method navClicked(android.view.View) in class com.example.viewmodel.MainViewModel" -->
<Button
android:id="#+id/btn1"
android:onClick="#{(v) -> mainViewModel.navClicked(v)}"
android:text="#string/btn1" />
<!-- "msg":"cannot find method navClicked(android.view.View) in class com.example.viewmodel.MainViewModel" -->
<Button
android:id="#+id/btn2"
android:onClick="#{mainViewModel::navClicked}"
android:text="#string/btn2" />
<!-- "msg":"Could not find identifier \u0027v\u0027\n\nCheck that the identifier is spelled correctly, and that no \u003cimport\u003e or \u003cvariable\u003e tags are missing." -->
<!-- This is missing (view) in the lambda - makes sense to fail -->
<Button
android:id="#+id/btn3"
android:onClick="#{() -> mainViewModel.navClicked(v)}"
android:text="#string/btn2" />
navClicked() was private...
// **denotes change, will not compile like that**
**public** void navClicked(#NonNull View view) {
switch (view.getId()) {
case R.id.btn1:
break;
case R.id.btn2:
break;
}
}
Once I tried this and it worked:
XML:
android:onClick="#{() -> model.clickEvent(123)}"
Model:
public void clickEvent(int id) {...}
I used string for id but I think it also works with integer.

How can I configure a live template that generates a builder method in IntelliJ IDEA?

I oftentimes need to create builder methods in my code. These methods are similar to getters, but they return this and they use with instead of get.
To be faster with that task I'd like to create a live-template in IDEA.
This how far I got:
(in ~/.IntelliJIdea14/config/templates/user.xml this looks like this:)
<template name="builderMethod" value="public $CLASS_NAME$ with$VAR_GET$(final $TYPE$ $PARAM_NAME$) {
this.$VAR$ = $PARAM_NAME$;
return this;
}" description="create a builder method" toReformat="true" toShortenFQNames="true">
<variable name="CLASS_NAME" expression="className()" defaultValue="" alwaysStopAt="true" />
<variable name="VAR" expression="complete()" defaultValue="" alwaysStopAt="true" />
<variable name="PARAM_NAME" expression="VAR" defaultValue="" alwaysStopAt="true" />
<variable name="TYPE" expression="typeOfVariable("this." + VAR)" defaultValue="" alwaysStopAt="true" />
<variable name="VAR_GET" expression="capitalize(VAR)" defaultValue="" alwaysStopAt="true" />
<context>
<option name="JAVA_EXPRESSION" value="false" />
<option name="JAVA_DECLARATION" value="true" />
</context>
</template>
This nearly works, except for typeOfVariable("this." + VAR) which does not. I just guessed how to call this method, because I could not find any documentation on the syntax used in the expressions except for this page which does not even mention a script language name or something that would make googling for easier.
How do I fix the call to typeOfVariable?
Bonus question: How can I get complete() for VAR to only show fields?
Similar question but not does not even have a start: Live Template for Fluent-API Builder in IntelliJ
Replace typeOfVariable("this." + VAR) with typeOfVariable(VAR).
Edit:
Another way to generate builder methods is to use an appropriate setter template (instead of live template).
https://www.jetbrains.com/help/idea/2016.1/generate-setter-dialog.html
There is already a built-in setter template named "Builder" which generates setters such as:
public Foo setBar(int bar) {
this.bar = bar;
return this;
}
You can create your own template (e.g by copying it) and change it so that the method prefix is with.
And to make the generated method parameter final go to settings:
Editor | Code Style | Java
Select the Code Generation tab
Tick Make generated parameters final
IntelliJ IDEA add final to auto-generated setters

IZPACK custom DataValidator unable to read custom panel user input

I have created a sample installer using IZPACK API. In this, i have created a custom panel class by extending IzPanel. After that i created a panel data validator by implementing DataValidator interface.
I have given its entry in install.xml as shown below:
<panel classname="TestInstallation" id ="TestInstallation">
<validator classname="com.izforge.izpack.panels.TestValidator"/>
</panel>
Validator is running fine and showing error message. Here, I need to show error message depending upon wrong user input combination entered in panel multiple fields. But, i am unable to read user entered data in my custom data validator(TestValidator) and getting null. AutomatedInstallData.getAttribute("") as well as AutomatedInstallData.getVariable("") both methods are returning null in my custom data validator.
Please help and let me know if i am missing something here.
Thanks in Advance !!!
Since you have you have your own validator by implementing the DataValidator intereface, you can get the user input from the InstallData object in your validateData overriden method. Eg.
#Override
public Status validateData(InstallData data) {
if (data.getVariables().get("MyFieldVariable");) {
return Status.OK;
} else {
return Status.ERROR;
}
}
"MyFieldVariable" is the name of the variable used in your custom panel. I guess you must have several input fields to validate against. But at least in this example it would be:
<field type="text" variable="MyFieldVariable">
<spec txt="My own field to validate" id="MyFieldVariable" size="15" set="" />
</field>
For the error to be displayed an Status.ERROR is returned from validateData(InstallData data) and you should override in your validator:
#Override
public String getErrorMessageId() {
return errorMsg;
}
This is available and tested with izpack 5.0.0-rc1. Mind that you should also then have the right maven dependencies:
<dependencies>
<dependency>
<groupId>org.codehaus.izpack</groupId>
<artifactId>izpack-panel</artifactId>
<version>5.0.0-rc1</version>
</dependency>
<dependency>
<groupId>org.codehaus.izpack</groupId>
<artifactId>izpack-api</artifactId>
<version>5.0.0-rc1</version>
</dependency>
</dependencies>

Can I give a XBL handler a name to call it?

Can I give a XBL handler a name, so I can call it from my javascript like I do with XBL methods?
It seems not, but you can create an XBL Method and call it from both your handler and other JavaScript. Something like:
<handler event="mouseover">
this.handleMouseOver();
</handler>
...
<method name="handleMouseOver">
<body>...
//and in javascript code:
yourObj.handleMouseOver();

Calling a method from XBL

From a XBL method, when I need to call another method, I do like:
<method name="myMethod_1">
<body>
<![CDATA[
// do staff
]]>
</body>
</method>
<method name="myMethod_2">
<body>
<![CDATA[
document.getElementById("thisElementID").myMethod_1();
]]>
</body>
</method>
I would like to know if is there a way to call the local method without need the element id? I've tried this.myMethod_1() but it says the method don't exist.
In the specific case of an event listener, there is another way around the problem, and that is to pass the element itself as the listener. Of course you only get one handleEvent method, so this is less useful if you're listening to lots of different events on lots of different event targets.
<implementation implements="nsIDOMEventListener">
<method name="handleEvent">
<parameter name="aEvent"/>
<body>
<![CDATA[
// do stuff
]]>
</body>
</method>
can you show us code calling myMethod_2? If you call it like: document.getElement(...).myMethod_2() that's fine, but if you have something like someElement.addEventHandler("click", myxbl.myMethod_2,...); that won't work since event target will be this.
This is important for determining what is this in that context
EDIT: (Tom's reply)
ow, think I got it.. it's exactly this the problem.. I'm calling it from a keypress listener of another document, and the "this" was not what I think..