Android Data Binding "cannot find method" - android-databinding

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.

Related

How can I post the same data to two different handlers depending on the button clicked?

[See updates at bottom]
I have a Razor page with a form on it. I want to have two buttons on that form, that perform a slightly different action - both using the same posted form data.
I tried using the asp-page-handler helper on the second button, but it doesn't seem to add anything to the HTML (I would expect it to add a formaction attribute to the <button> element, but it doesn't add anything at all).
Here's an example page:
#page "{id?}"
#model IndexModel
#tagHelperPrefix x:
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Current value is #Model.Foo</p>
<x:form method="post">
<input type="text" name="foo" />
<button type="submit">Default</button>
<button type="submit" x:asp-page-handler="Alternative">Alternative</button>
</x:form>
... and here's the corresponding page model:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace MyWebApplication.Pages
{
public class IndexModel : PageModel
{
[BindProperty]
public string Foo { get; set; }
public void OnGet(int? id)
{
}
public void OnPostAsync(string foo)
{
Foo = foo;
}
public void OnPostAlternativeAsync(string foo)
{
Foo = foo.ToUpper();
}
}
}
This is rendered as:
...where the generated HTML for the form is:
<form method="post">
<input type="text" name="foo" />
<button type="submit">Default</button>
<button type="submit" x:asp-page-handler="Alternative">Alternative</button>
</form>
The fact that the x:asp-page-handler attribute is still in the generated HTML makes me think that the Razor engine hasn't recognized it. I've tried taking off the x: prefix, but that didn't help.
What am I doing wrong?
UPDATE
OK, I tried removing the tag prefix and removing the #tagHelperPrefix line, and that made a difference. A formaction is added to the second <button> element as expected.
However:
that's really annoying - the #tagHelperPrefix is not something I want to lose, and
now both buttons are triggering the "Alternative" action, even though only one of them has the formaction!
Here's the new generated HTML:
<form method="post">
<input type="text" name="foo" />
<button type="submit">Default</button>
<button type="submit" formaction="/?handler=Alternative">Alternative</button>
</form>
SECOND UPDATE
OK, so If I put asp-page-handler="" on the "default" button, then each button goes to the correct handler, which is fine.
The last question that remains, then, is: how can I make this work with the tag helper prefix?
[Answering my own question in case this helps others.]
It turns out that:
The tag-helper-prefix only applies to elements, not attributes, so it should be asp-page-handler="..." rather than x:asp-page-handler="..." even if the tag-helper-prefix is x:.
Those asp- attributes are only recognized within a tag that is tag-helper-enabled - which is all elements when no tag-helper-prefix is specified, or only elements with the tag-helper-prefix where one is specified. In my case, I had to change <button ...> to <x:button ...>.
If you specify asp-page-handler for one button, you need to specify it on all the buttons, even if you specify it as "" to get the default action.

Android: data-binidng: disable click

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.

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>

How to disable autocomplete for Struts tags(HTML:text)

For normal HTML input tag,disabling autocomplete is simple as given below:
<input type="email" name="email" autocomplete="off">
Whereas its does not work for Struts tags given below:
<html:text property="" styleId="Field" maxlength="4" size="4" tabindex="14"
onblur="check(this);" value="" />
How to disable autocomplete for Struts tags?
Autocomplete attribute is not passed through to the rendered HTML by the tag.
You can do so by writing your own custom tag that extends the tag to accept the autocomplete attribute and pass it through to the rendered tag.
check these links ::
Struts 2 + Disable Form Autocomplete
http://www.coderanch.com/t/54020/Struts/form-input-tags-turning-autocomplete
I've met the same issue. Editing the tld attibutes did not help me. I resolved it by adding the attribute via JavaScript code. Here is an example:
<bean:define id="autoComplete" scope="request" type="java.lang.String"
value="<%=String.valueOf(ApplicationConfiguration.getAutoComplete()) %>" />
<script>
var ttip;
var ttip2;
Ext.onReady(function() {
var form = document.forms['formName'];
var elem = form.elements["passortField"];
elem.setAttribute( "autocomplete", "${autoComplete}" );
ApplicationConfiguration.getAutoComplete() - returns either on or off, depending on application configuration
Another option is to write your Own TextTag class something like this:
public class TextTagNoAutoComplete extends BaseFieldTag {
public TextTagNoAutoComplete() {
super();
this.type = "text";
doReadonly = true;
}
protected void prepareOtherAttributes(StringBuffer handlers) {
prepareAttribute(handlers, "autocomplete", "false");
}
}
and point textnac to this class in your tld mapping! ..and Viola ! Not the best reusable code. Provided the fact that, Struts 1.x is in no way going to be revisited, this sortta monkey patching is more than enough in my point of view :)
You can use redisplay="false" which is the equivalent in struts-html for autocomplete.
We can use the attributes which are not supported in <htm-text> inside \"
<html:text property="userName" styleId="firstname\" placeholder=\"Email*\"
autocomplete=\"off" styleClass="ql-inpt" readonly="true" />