How to override a button attribute - android

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.

Related

Android BindAdapter with custom attribute with custom values (similar to visible attribute)

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" />

How to define a custom BindingMethod for a TextView?

I am starting to work with the new data binding API. I want to bind on a TextView a custom property where I change the text and background at once. As far I understood the api there is a #BindingMethod annotation for this, but the documentation is there a little weak.
I want something like this:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Important"
tools:background="#drawable/bg_badge_important"
tools:textColor="#fff"
android:id="#+id/badge"
custom:badge="#{item.badge}"/>
Where this item.badge is a string like important or notice. For i18n I cannot use this string directly but this will help me to choose the right text and the right background image.
Can you give me a small example or a reference how to use this attribute? I just found one answer on Stack Overflow, but this does not answer my question.
Just create a public static method on your codebase and annotate with #BindingAdapter.
Here is an example for fonts:
https://plus.google.com/+LisaWrayZeitouni/posts/LTr5tX5M9mb
Edit by rekire I ended in using this code:
#BindingAdapter({"bind:badge"})
public static void setBadge(TextView textView, String value) {
switch(value) {
case "email":
textView.setText(R.string.badge_email);
textView.setBackgroundResource(R.drawable.bg_badge_email);
break;
// other cases
}
}

Use MVVM Cross to bind an XML string formatted at run time to an Android TextView

I am using MVVM Cross in Xamarin Studio:
I have a text view, and I want to do something like this:
<TextView
android:id="#+id/title1"
android:layout_toRightOf="#+id/thumb1"
local:MvxBind="FormattedText Item.Description;"
/>
Where Item.Description is set at runtime, and is equal to something like:
"<b>The header</b>\\n\\nThe sub text"
or another formatted string.
I am aware I can do this if the text string is static by using a resource file, but my text is not static.
Bonus points if you give me a solution that would work in Android and iOS XML!
This sounds like a great time to learn about MvvmCross Value Converters.
From the Wiki:
Value Converters in MvvmCross are used to provide mappings to/from
logical values in the view models and presented values in the user
interface.
In this case you can make a new class inherited from MvxValueConverter and override the Convert method. You will do the string formatting inside the Convert method. Then in your binding you can reference the Value Converter and MvvmCross will automatically call the Value Converter before it displays the bound data.
Here is an example Value Converter which takes a float? as input and outputs a formatted currency string:
public class CurrencyValueConverter : MvxValueConverter<float?, string>
{
protected override string Convert(float? value, Type targetType, object parameter, CultureInfo culture)
{
return !value.HasValue ? null : string.Format(culture, "{0:C}", value.Value);
}
}
Then inside the AXML you can reference the Value Converter using the following syntax:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:text="[CashOnDeliveryCharges]"
local:MvxBind="Text Currency(CashOnDeliveryCharges), FallbackValue='N/A'" />
For those of you who require simple formatting such as adding dashes to phone numbers etc, see Trevor's excellent and informative answer above.
If you need things like bold, underline etc, and you're working cross platform in MVVM Cross but without the aid of Xamarin forms, you'll need to use raw XML and android:layout_toRightOf="#+id/the_id_of_the_previous_element" in combination with things like android:textStyle="bold"
E.g
<TextView
android:id="#+id/id1"
android:textStyle="bold"
local:MvxBind="Text YourPropertyToBind;"
/>
<TextView
android:id="#+id/id2"
android:layout_toRightOf="#+id/id1"
android:text="the_second_bit_of_text_this_is_static_not_bound_if_you_want"
/>
<TextView
android:id="#+id/id3"
android:layout_toRightOf="#+id/id2"
android:textStyle="bold"
local:MvxBind="Text The_Next_Bit_Of_Text;"
/>

MvvmCross Validation Binding visibility

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

Android: Get an attribute's value from an XML item

Is there an easy way to grab a attribute value from an xml item in your Java class definition? I'm looking for something like this:
// In xml layout:
<TextView android:id="#+id/MyXMLitem" android:textColor="#000000" />
// in Java Class definition
String some_text_color;
some_text_color = R.id.MyXMLitem.attr.textColor; // I'd like this to return "#000000"
I know you can grab similar xml attributes from the converted objects using getters/setters like View.getText()... I'm just wondering if there's a way to grab an xml attribute right from the item itself.
Views take the XML values and store them into class level variables in their constructors so it's not possible to get values from the object itself once the layout has been created.
You can see this in the source of the View object at https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/View.java
if you search for AttributeSet (which is the object used to pass the XML layout values to the constructor).
You can use XmlResourceParser to read data straight from XML resources.
<TextView android:id="#+id/MyXMLitem" android:textColor="#000000" />
You can use getCurrentTextColor().
TextView tv = (TextView)findViewbyId(R.id.MyXMLitem);
String color = Integer.toHexString(tv.getCurrentTextColor());
It returns ff000000 instead of #000000 though.

Categories

Resources