1

I know how to create a custom style attribute definition and get the value in a custom view (from this link):

TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0);
String str = a.getString(R.styleable.MyCustomView_my_custom_attribute);

But I´m wondering if it´s possible to define a "global" tag that I can apply to any view, either custom or from Android SDK.

Let´s say I want to execute a static method to the views containing that attribute, but avoiding any reference to the view (like FindViewById):

<TextView
    android:id="@+id/textView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    whatever:custom_global_tag="key" />

MyStaticClass.Process(View view, String key) {}

Is this even possible?

Edit

Just to clarify, what I really need is a string. The goal is transforming an string into another and assign the new value to the text property.

My env is Xamarin but any native approach could be translated to Xamarin so all suggestions are welcome

Community
  • 1
  • 1
xleon
  • 6,201
  • 3
  • 36
  • 52

4 Answers4

1

Try Theme?

A theme is a style applied to an entire Activity or application, rather than an individual View (as in the example above). When a style is applied as a theme, every View in the Activity or application will apply each style property that it supports.

Note: just work for property the view supports.

Edit #1

if you want to pass a different string on each view, just use android:tag

something like:

<TextView
    android:id="@+id/textView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:tag="key" />

and get the string

String key = textView.getTag();

Edit #2

set a tag to identify which view you want.

in the activity, iterate all the views to find the identified view like

final ViewGroup viewGroup = (ViewGroup)((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
for(int i = 0; i < viewGroup.getChildCount(); i++) {
    View childView = viewGroup.getChildAt(i);
    if ("identificaton".equals(childView.getTag())) {
        // do some work to child view.
    }
}

the viewGroup part can check the post

Community
  • 1
  • 1
AssIstne
  • 466
  • 4
  • 13
0

I haven't tried it myself but theoretically, you can do it. See the Calligraphy library for an example.

You can declare global attributes (like they do in the Framework) directly in a values resource file:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="myCustomAttr" format="string"/>
</resources>

Then you can add it to any view without any prefix (you will have a Lint error though):

<TextView
    android:id="@+id/textView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    myCustomAttr="key"
    tools:ignore="MissingPrefix" />

And finally, you should be able to retrieve it from your view AttributeSet (given in the view constructor).

String myCustomValue = attrs.getAttributeValue(R.attr.myCustomAttr);

There is a problem if you don't create a Custom View though: to my knowledge, you don't have access to the AttributeSet after a View is created. You will have to hook yourself somewhere during the layout inflation...

Julien Arzul
  • 961
  • 9
  • 7
  • This works in Calligraphy without custom views because it hijacks the LayoutInflater by creating a proxy Context. This is a complex approach, especially because AppCompat also does it, so I don't recommend it. – BladeCoder Feb 07 '17 at 03:25
  • Yes you're right. The solution of a global attribute works only if the OP wants to apply it to multiple Custom View (instead of having a declare-styleable for each Custom View). Hooking in the layout inflation process is complex and error-prone and I would not recommend it either. There is probably some way to target the view the OP wants to modify. – Julien Arzul Feb 07 '17 at 03:44
0

You can do this with a @BindingAdapter method, using the Data Binding Library.

BladeCoder
  • 12,779
  • 3
  • 59
  • 51
0

Based on your help (@assistne) I ended up with a recursive method that will scan the whole view tree looking for views with a tag starting with "translate:". So far it works like a charm:

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:tag="translate:TextWithLineBreakCharacters" />
<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:tag="translate:Animals"/>

Helper class (Xamarin/C#):

public static class I18NAndroidHelper
{
    public static void Translate(object view)
    {
        if(view is TextView)
        {
            var textView = (TextView)view;
            var tag = (string)textView.Tag;
            if(tag != null && tag.StartsWith("translate:", StringComparison.Ordinal))
            {
                var key = tag.Split(new [] {":"}, StringSplitOptions.None)[1];
                textView.Text = key.Translate(); // Translate() is an string extension method
            }
        }
        else if(view is ViewGroup)
        {
            var viewGroup = (ViewGroup)view;
            var childCount = viewGroup.ChildCount;
            for(var i = 0; i < childCount; i++)
                Translate(viewGroup.GetChildAt(i));
        }
        else if(view is Activity)
        {
            var activity = (Activity)view;
            var content = activity.FindViewById<ViewGroup>(Android.Resource.Id.Content);
            Translate(content);
        }
    }
}

On Activity OnCreate:

I18NAndroidHelper.Translate(this);
xleon
  • 6,201
  • 3
  • 36
  • 52