как: определить тему (стиль) для пользовательского виджета

<p>я написал собственный виджет для элемента управления, который мы широко используем в нашем приложении. класс виджета происходит от <code>imagebutton</code> и расширяет его несколькими простыми способами. я определил стиль, который я могу применить к виджету по мере его использования, но я бы предпочел настроить его с помощью темы. в <code>r.styleable</code> я вижу атрибуты стиля виджета, такие как <code>imagebuttonstyle</code> и <code>textviewstyle</code>. есть ли способ создать что-то подобное для пользовательского виджета, который я написал?</p>

<p>да, есть один способ:</p><p>предположим, у вас есть объявление атрибутов для вашего виджета (в <code>attrs.xml</code>):</p><pre><code><declare-styleable name="customimagebutton"> <attr name="customattr" format="string"/> </declare-styleable> </code></pre><p>объявите атрибут, который вы будете использовать для ссылки на стиль (в <code>attrs.xml</code>):</p><pre><code><declare-styleable name="customtheme"> <attr name="customimagebuttonstyle" format="reference"/> </declare-styleable> </code></pre><p>объявите набор значений атрибутов по умолчанию для виджета (в <code>styles.xml</code>):</p><pre><code><style name="widget.imagebutton.custom" parent="android:style/widget.imagebutton"> <item name="customattr">some value</item> </style> </code></pre><p>объявите собственную тему (в <code>themes.xml</code>):</p><pre><code><style name="theme.custom" parent="@android:style/theme"> <item name="customimagebuttonstyle">@style/widget.imagebutton.custom</item> </style> </code></pre><p>используйте этот атрибут в качестве третьего аргумента в конструкторе вашего виджета (в <code>customimagebutton.java</code>):</p><pre><code>public class customimagebutton extends imagebutton { private string customattr; public customimagebutton( context context ) { this( context, null ); } public customimagebutton( context context, attributeset attrs ) { this( context, attrs, r.attr.customimagebuttonstyle ); } public customimagebutton( context context, attributeset attrs, int defstyle ) { super( context, attrs, defstyle ); final typedarray array = context.obtainstyledattributes( attrs, r.styleable.customimagebutton, defstyle, r.style.widget\_imagebutton\_custom ); // see below this.customattr = array.getstring( r.styleable.customimagebutton\_customattr, "" ); array.recycle(); } } </code></pre><p>теперь вам нужно применить <code>theme.custom</code> ко всем действиям, которые используют <code>customimagebutton</code> (в androidmanifest.xml):</p><pre><code><activity android:name=".myactivity" android:theme="@style/theme.custom"/> </code></pre><p>это все. теперь <code>customimagebutton</code> пытается загрузить значения атрибутов по умолчанию из атрибута <code>customimagebuttonstyle</code> текущей темы. если в теме нет такого атрибута или значение атрибута равно <code>@null</code>, то будет использован последний аргумент <code>obtainstyledattributes</code>: в этом случае <code>widget.imagebutton.custom</code>.</p><p>вы можете изменить имена всех экземпляров и всех файлов (кроме <code>androidmanifest.xml</code>), но было бы лучше использовать соглашение об именовании android.</p>

<p>еще один аспект в дополнение к отличному ответу майкла - переопределение пользовательских атрибутов в темах. предположим, у вас есть несколько пользовательских представлений, которые все ссылаются на пользовательский атрибут "custom\_background".</p><pre><code><declare-styleable name="mycustomstylables"> <attr name="custom\_background" format="color"/> </declare-styleable> </code></pre><p>в теме вы определяете значение</p><pre><code><style name="mycolorfultheme" parent="apptheme"> <item name="custom\_background">#ff0000</item> </style> </code></pre><p>или же</p><pre><code><style name="myboringtheme" parent="apptheme"> <item name="custom\_background">#ffffff</item> </style> </code></pre><p>вы можете обратиться к атрибуту в стиле</p><pre><code><style name="mydefaultlabelstyle" parent="apptheme"> <item name="android:background">?background\_label</item> </style> </code></pre><p>обратите внимание на знак вопроса, который также используется для ссылки android, как в</p><pre><code>?android:attr/colorbackground </code></pre><p>как многие из вас заметили, вы можете - и, вероятно, должны - использовать ссылки @color вместо жестко закодированных цветов.</p><p>так почему бы просто не сделать</p><pre><code><item name="android:background">@color/my\_background\_color</item> </code></pre><p>вы не можете изменить определение "my\_background\_color" во время выполнения, тогда как вы можете легко переключать темы.</p>