Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom attributes with class reference

Tags:

android

I'm trying to create a custom attribute that behaves like tools:context, that is with

  • Android Studio auto complete functionallity
  • Project classname reference
  • Support for auto refactory in case I change my class directory

This is my resources.xml

<declare-styleable name="RecyclerView">
    <attr name="adapter" format="string"></attr>
</declare-styleable>

This is the usage

    <example.com.br.appname.RecyclerView
         android:id="@+id/accounts"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_marginTop="8dp"
         app:adapter="example.com.br.appname.AccountAdapter" >
    </example.com.br.appname.RecyclerView>

I've tried to use the format reference but it didn't compile as well.

Error:(17, 22) String types not allowed (at 'adapter' with value 'example.com.br.appname.AccountAdapter').

like image 780
Leandro Silva Avatar asked Apr 29 '17 14:04

Leandro Silva


People also ask

What are custom attributes in C#?

Attributes are metadata extensions that give additional information to the compiler about the elements in the program code at runtime.

What are custom attributes?

A custom attribute is an additional property that you can define to help describe business glossary assets. Labels are keywords that you can define to help describe any type of asset in the metadata repository. Users can use both custom attributes and labels to refine a search in the business glossary.

What are the five kinds of custom attributes that can be created?

The data type of the value of the custom attribute can be text, predefined values, date, number, or relationship.


2 Answers

I don’t think this is possible currently. Other similar custom attrs I can think of, for instance app:layout_behavior from the design library, or simply app:layoutManager from RecyclerView all require the full classname, with none of your requirements.

It might be better to store these in a strings resource file, and remember to check it when refactoring class names.

You can consider filing a feature request, since Android Studio has this functionality in special cases (tools:context, class in <view> and <fragment> tags, classes in Manifest...), but I doubt they would add a new attribute format just for this.

like image 110
natario Avatar answered Oct 02 '22 19:10

natario


so...

  • apparently, YOU CAN!
  • Google does this too.
  • Android Studio understands that the class is being referenced from XML
    • i.e.
      • Refactor > Rename works
      • Find Usages works
      • and so on...

don't specify a format attribute in .../src/main/res/values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="MyCustomView">
        ....
        <attr name="give_me_a_class"/>
        ....
    </declare-styleable>

</resources>

use it in some layout file .../src/main/res/layout/activity__main_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<SomeLayout
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- make sure to use $ dollar signs for nested classes -->
    <MyCustomView
        app:give_me_a_class="class.type.name.Outer$Nested/>

    <MyCustomView
        app:give_me_a_class="class.type.name.AnotherClass/>

</SomeLayout>

parse the class in your view initialization code .../src/main/java/.../MyCustomView.kt

class MyCustomView(
        context:Context,
        attrs:AttributeSet)
    :View(context,attrs)
{
    // parse XML attributes
    ....
    private val giveMeAClass:SomeCustomInterface
    init
    {
        context.theme.obtainStyledAttributes(attrs,R.styleable.ColorPreference,0,0).apply()
        {
            try
            {
                // very important to use the class loader from the passed-in context
                giveMeAClass = context::class.java.classLoader!!
                        .loadClass(getString(R.styleable.MyCustomView_give_me_a_class))
                        .newInstance() // instantiate using 0-args constructor
                        .let {it as SomeCustomInterface}
            }
            finally
            {
                recycle()
            }
        }
    }
like image 38
Eric Avatar answered Oct 02 '22 17:10

Eric