I have 2 edit texts in my view and I'm using data binding. What I want to achieve is whenever the first text view had 5 characters, the focus passes on to the next edit text, and whenever that edit text also had 5 characters, the focus should be removed from the whole view.
This is the code I wrote for my binding adapter:
#BindingAdapter("setMaxLength", "nextPart")
fun EditText.onTextChange(maxLength: Int, nextPart: EditText) {
filters = arrayOf<InputFilter>(InputFilter.LengthFilter(maxLength))
addOnTextChangeListener {
if (it.length == maxLength) {
clearFocus()
nextPart.requestFocus()
}
}
}
I don't know how I should pass these 2 arguments to my function in xml.
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/firstPart"
nextPart="alphabet"
setMaxLength="#{5}" />
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/alphabet"
setMaxLength="#{5}" />
This code has build issues with error:
Cannot find a setter for <androidx.appcompat.widget.AppCompatEditText setMaxLength> that accepts parameter type 'int'
If a binding adapter provides the setter, check that the adapter is annotated correctly and that the parameter type matches.
I'm not sure how I could do this, also addOnTextChangeListener is a text watcher and the functionality is tested.
Am I even on the right path here? Any ideas would be appreciated.
Try doing it like this
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/firstPart"
nextPart="#{alphabet}"
setMaxLength="#{5}" />
Related
I've successfully made my first bind adapter and I wish to know a bit more about it.
I want to know how to make an attribute that can get only specific strings for a different state for my view.
For example every view has the visibility attribute that it can be"gone", "visible", "invisible"
<TextView
android:id="#+id/loading_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="#id/inventory_items_recycler"
app:layout_constraintEnd_toEndOf="#+id/inventory_items_recycler"
app:layout_constraintStart_toStartOf="#id/inventory_items_recycler"
app:layout_constraintBottom_toBottomOf="#id/inventory_items_recycler"
android:textSize="18sp"
android:visibility="gone"
app:item_id="#{ItemID.BLACK_GLOVES.ordinal()}"
/>
I've made a custom attribute called item_id that get a number that represent enum value. And in my binding utils I have this code:
#BindingAdapter("item_id")
public static void setItemName(TextView tv, int itemId) {
tv.setText(ItemData.get(ItemID.values()[itemId]).getName());
}
I prefer to have something similar to the visibility attribute that it value can be either "visible", "invisible" or "gone"
Bonus::
I wish android studio can auto-complete me for the possibilities that I can use.
You could pass directly the enum to your binding adapter, instead of converting it first to an int and than back to enum.
#BindingAdapter("item_id")
public static void setItemName(TextView tv, ItemID itemId) {
..
}
Then you could pass directly the enum in your xml:
app:item_id="#{ItemID.BLACK_GLOVES}"
This way you'll have a limited number of possibilities to enter and will be less likely to accidentally enter a meaningless integer.
However, binding adapters and custom attibutes are different. With a binding adapter, you still need to use the syntax of binding expression, ie: "#{ }".
android:visibility , on the other hand, is an attribute. You can also define custom attributes for your custom views and get something similar (have a limited number of input options and IDE shows you your options etc). But you shouldn't confuse that with binding adapters. These are two different concepts.
Try this:
#BindingAdapter("isGone")
#JvmStatic
fun View.setVisibility(isGone: Boolean) {
if (isGone) this.visibility = View.GONE else View.VISIBLE
}
Inside your xml:
<com.google.android.material.checkbox.MaterialCheckBox
android:id="#+id/cb_class"
style="#style/TextStyleNormal.White"
android:layout_marginStart="#dimen/margin_large"
isGone="#{isSharedDailyActivity}"// it take boolean value
app:buttonTint="#color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:useMaterialThemeColors="true" />
I have created BindingAdapter to make clickable part inside TextView. I have used 3 parameters, one string resource with placeholder for clickable part (textWithPlaceholder), second part with clickable part (textClickablePhrase) and last one for listener (onPhraseClick). When building the app, it fails with upcoming error.
Cannot find the setter for attribute 'app:onPhraseClick' with parameter type lambda on android.widget.TextView.
The strange thing is when I try the same code without first two parameters (the strings), it can build. Even if I try to build app without last parameter (listener), it can build. So the code works but it doesn't work together.
<TextView
<!-- another attributes -->
app:textWithPlaceholder="#{#string/text_with_placeholder}"
app:textClickablePhrase="#{#string/clickable_phrase}"
app:onPhraseClick="#{() -> viewModel.onPhraseClicked()}" />
#BindingAdapter("textWithPlaceholder", "textClickablePhrase", "onPhraseClick")
#JvmStatic
fun TextView.setClickablePhrase(
textWithPlaceholder: String,
textClickablePhrase: String,
onPhraseClick: View.OnClickListener) {
// setting Spannable and click listener
}
I expect the 3 parameters should work together but they works only separated.
EDIT:
It can build even like this but I think it is not right solution because it is not defined right. All parameters are mandatory and it should be able to be defined in this way and not to use workaround to make it optional.
#BindingAdapter(value = ["textWithPlaceholder", "textClickablePhrase", "onPhraseClick"], requireAll = false)
Consider the following bindingAdapter
#BindingAdapter({"pager"})
public static void bindViewPagerTabs(final TabLayout view, final ViewPager pagerView)
{
view.setupWithViewPager(pagerView, true);
}
and the setting in the xml is :
<com.google.android.material.tabs.TabLayout
android:id="#+id/card_control_tab_layout"
style="#style/tab_in_toolbar_style"
android:background="#color/colorPrimary"
app:tabGravity="fill"
app:pager="#{(pager_r)}"
app:tabMode="scrollable" />
Gives the following error :
error: cannot find symbol class ActivityCardControlBindingImpl
But if I changed the pager Id to anything without the underscore or any special characters like #+id/pager it works perfectly, any valid reason ?
It works if you write app:pager="#{(pagerR)}" instead of app:pager="#{(pager_r)}". Also you don't need the brackets, so you could just write app:pager="#{pagerR}".
EDIT:
But if I changed the pager Id to anything without the underscore or any special characters like #+id/pager it works perfectly, any valid reason ?
I don't know about other special characters, but when you use the underscore in your view ids the snake case view ids like "pager_r" get converted by databinding to camel came variables like "pagerR". So as my original answer states, if you use #+id/pager_r you can use the variable pagerR.
I found the same answer as Janosch Pelzer's with a litte more explsnation here
Cannot refer to other View ID in Android data binding
I should be able to use a #BindingAdapter in Android dataBinding so that i can override a certain attribute. I am able to do it with a cusutom attribute but with a android built-in attribute how is it accomplished?
what i have so far:
in my viewModel i have a method that is annotated with #BindingAdapter and looks like this:
#BindingAdapter({"android:text"})
public static void setText(Button view,boolean language) {//i need to pass one more variable in here for area code , its just a integer, but how ?
if("french".equals(language))//i want to test if french && area code
view.setText("si vous play");
else if ("English".equals(language)) //i want to test if french && area code
view.setText("please");
}
but i have a few problems. Lets see the xml :
<Button
android:id="#+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{`french,844`}"/>
See my issue, i want to pass in more then one parameter to dataBinding. But how ? Do i have to make a POJO object and pass it in ? even if i did that how do i set the object from xml ?
So if someone can just tell me the following i'll be fine:
1. how to pass multiple values to a bindingAdapter and
2. How to override a built-in android attribute in any view.
If you want to set two different values in your BindingAdapter, you should use two different attributes:
<Button
android:id="#+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{`french`}"
app:areaCode="#{`844`}"/>
Then have two different attributes in your BindingAdapter:
#BindingAdapter({"android:text", "areaCode"})
public static void setText(Button view, String language, String areaCode) {
...
}
But it would probably be better to set a different "app:language" as that would be more clear to the developer.
I followed the instructions here to bind my view model validation to the input form.
Using MVVMCross to bind to error messages
The problem I have now is that there is a lot of extra spacing on the form due to the validation elements. How do I make these spacing problems go away? It's a bit difficult to use a Visibility converter due to the fact there is no property for each field. Same problem with Android and iOS. I suppose maybe some sort of custom visibility converter?
I think a quick fix might be to use a binding like Visible Errors['Email'] - however, you're reporting that's not working (so transferred that to https://github.com/MvvmCross/MvvmCross/issues/494 - thanks)
Since that doesn't work directly, then you you should be able to bind the Visible boolean property using something like (in Android):
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#ff0000"
android:text="My error text"
local:MvxBind="Visible ErrorExists(Errors['Email'],FallbackValue=null)"
/>
where ErrorExists is:
public class ErrorExistsValueConverter : MvxValueConverter
{
public override object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value != null);
}
}
For iOS, if you are showing/hiding UIViews, then you would need to ensure your UI layout auto-updates - e.g. using constraints
As an alternative UI technique, you should also be able to use binding on the background color of an EditText - similar to the color binding in https://github.com/MvvmCross/MvvmCross-Tutorials/blob/master/ValueConversion/ValueConversion.UI.Droid/Resources/Layout/View_Colors.axml