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

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.

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 we define the id in kotlin without any use of third party library?

In java, we used to define the id by using findviewbyid. I am Wondering how we can define the id in kotlin without any use of third party library.
You don't have to define the view id's in Kotlin. All you have to do is use a non declared variable which has same name as the view in layout xml file. This reduces the chance of you running into a bug.
Assume you have this TextView in the layout xml
<TextView
android:id="#+id/mytextview"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="18sp"/>
then this is how you can access this by a variable name mytextview, without declaring it in the file. This is how you would set the text
mytextview.text = "My text view"
You can read more at https://kotlinlang.org/docs/tutorials/android-plugin.html
Another way could be following but I wouldn't suggest it
private var textview: TextView? = null
textview = findViewById(R.id.mytextview) as TextView // old way
textview = findViewById<TextView>(R.id.mytextview) // new way

How to override a button attribute

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.

Where do I find the parameter to pass into the findViewById in Android development

I have just started learning app development and I am running into the problem of not knowing what I should give the findViewById function nor where I can find it.
In your XML layout, each View has an id. You use this id in your findViewById method.
Example:
<TextView
android:id="#+id/my_textview"
android:text="Hello world" />
And the corresponding code in Java to reference this TextView:
TextView tv = (TextView) findViewById(R.id.my_textview);
Give it a resource id. findViewById is a method of View class object.
You can obtain the official reference at:
http://developer.android.com/reference/android/app/Activity.html#findViewById(int)
To start your learning:
http://developer.android.com/guide/index.html
findViewById(int id)
Finds a view that was identified by the id attribute from the XML that was processed in onCreate(Bundle).
The easiest way to find element of the Android app layout is to use its ID. We could add our own IDs to almost every XML tags. To add ID we use android:id attribute.
<TextView
android:id="#+id/this_is_id_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/my_best_text" />
We have to set in brackets a type of View we’re looking for and then give type and name of resources: (view_type) findViewById(R.id.id_name).
TextView newtext = (TextView) findViewById(R.id.this_is_id_name);
For more information you may visit Here.

Android + Data Binding #style

While using the new data binding api, I found that you can't bind to the "style" attribute. Compiler complains that it can't find the style. However, if I simply set the style as is, it'll find it just fine. For example:
doesn't work:
style="#{TextUtils.isEmpty(row.getSubtitle()) ? #style/SubTitle : #style/Title}"
works:
style="#style/SubTitle"
Error:
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
java.lang.RuntimeException: Found data binding errors.
****/ data binding error ****msg:Identifiers must have user defined types from the XML file. SubTitle is missing it file:/~/test/app/src/main/res/layout/row.xml loc:48:71 - 48:78 ****\ data binding error ****
The data binding unfortunately is not supported for styles:
https://code.google.com/p/android-developer-preview/issues/detail?id=2613
Although #bwhite is correct, there may be workarounds you can do. It depends what you need to conditionally change. For instance, if you want to change the font based on the condition (which I needed to do), you can do it by making a custom binding adapter.
In other words, doing something like this:
public class FontBindingAdapter {
#BindingAdapter({"bind:font"})
public static void setFont(TextView textView, String typefaceName){
Typeface typeface = ResourcesCompat.getFont(context, R.font.myfont);
// You'd probably want to actually use `typefaceName` to determine the font to use
textView.setTypeface(typeface);
}
Then in your layout, like this:
<TextView
app:font="#{some_condition ? #string/typeface_string_name_bold: #string/typeface_string_name_bold_light}"
I used this in my code, based on a great post: https://plus.google.com/+LisaWrayZeitouni/posts/LTr5tX5M9mb
I have a found a rather elegant solution for applying styles with data binding. I use the Paris library and then create binding adapters for interested views. for example:
#BindingAdapter("bindTextViewStyle")
fun TextView.bindTextViewStyle(styleResourceId: Int) {
this.style(styleResourceId)
}
and then in XML:
<TextView
app:bindTextViewStyle="#{viewModel.priceStyleResource}"
.../>
viewModel.priceStyleResource is a MutableLiveData in my view model which is set with the style resource ID.
priceStyleResource.value = R.style.QuoteDetailsHeaderItem_Up
EXTRA NOTE
You can also probably make a generic bindStyle binding adapter directly for the View class, but in that case the attribute items specifically for textviews (textColor for example) will not get applied. So up to you to find the right balance and naming.

Categories

Resources