2014-06-21 5 views
6

Предположим, у меня есть приложение с 2 темами: мужское и женское. Темы просто изменяют цветовую палитру и несколько чертежей, чтобы удовлетворить предпочтения пользователя.Тема Зависимые Android-строки

Огромное спасибо http://www.androidengineer.com/2010/06/using-themes-in-android-applications.html за его советы по изготовлению этой работы.

Но теперь можно сказать, что я хочу получить небольшое приложение с приложением, а не только изменить цвета и рисунки, но я также хочу изменить строки. Например, я мог бы добавить тему пирата, а затем «Отправить» будет «Arrrrgh!».

Итак, мой основной вопрос: как я могу изменить строки во всем приложении через пользовательские темы?

Edit:

выдумывает: приложение имеет 12 кнопок и 32 вид текста Я хотел бы иметь тему зависимой, и я хотел бы сделать это без гигантского отображения или убивания пользовательских ATTRS.

Все 3 из существующих решений будут работать. Ищете что-то более чистое, хотя я не знаю, что такое зверь существует.

+0

http://www.41post.com/4941/programming/android-get-string-resource-at-another-xml-namespace –

+0

@IllegalArgument Мне должно быть что-то не хватает в этом сообщении в блоге. Я не понимаю, почему он даже делает то, что делает, потому что он все еще определяет 3 отдельные строки и имеет 3 отдельные кнопки, указывающие на эти строки. –

ответ

2

Скажем, у меня есть приложение с 2 темы: мужского и женского. Темы просто изменяют цветовую палитру и несколько чертежей, чтобы удовлетворить предпочтения пользователя.

Как насчет того, чтобы мы притворились, что вы делаете что-то еще? Это анти-шаблон дизайна, ассоциирующий определенные цвета на основе пола (например, «девушки, такие как розовый»).

Это не значит, что ваша техническая цель плохая, просто это действительно стереотипный пример.

Например, я мог бы добавить тему пирата, а затем «Отправить» будет «Arrrrgh!».

Только в случае, если «Отменить» отобразится «Аваст!».

Как я могу изменить строки во всем приложении через пользовательские темы?

Вы не сказали, откуда эти струны. Являются ли они строковыми ресурсами? Записи базы данных? Что вы получаете из веб-службы? Что-то другое?

На данный момент я предполагаю, что это строковые ресурсы. По определению вам нужно будет иметь N копий строк, по одной на тему.

Поскольку пол и пиратский статус не отслеживаются Android как возможные квалификаторы набора ресурсов, вы не можете иметь эти строковые ресурсы в разных наборах ресурсов. Хотя они могут быть в разных файлах (например, res/values/strings_theme1.xml), имена файлов не являются частью идентификаторов ресурсов для строк. Таким образом, вам придется использовать какой-то префикс/суффикс, чтобы отслеживать, какие строки принадлежат тем (например, @string/btn_submit_theme1).

Если эти строки не изменяются во время выполнения - это все, что находится в вашем ресурсе макета - вы можете взять страницу с Chris Jenkins' Calligraphy library. Он имеет свой собственный подкласс LayoutInflater, используемый для перегрузки некоторых стандартных атрибутов XML. В его случае основное внимание уделяется android:fontFamily, где он поддерживает это сопоставление с файлом шрифта в активах.

В вашем случае вы можете перегрузить android:text. В вашем файле макета вместо того, чтобы указывать на какую-либо из ваших фактических строк, вы могли бы получить это базовое имя нужного вам строкового ресурса, без какого-либо идентификатора темы (например, если реальные строки: @string/btn_submit_theme1 и kin, вы можете иметь android:text="btn_submit"). Ваш подкласс LayoutInflater захватит это значение, добавит суффикс имени темы, используйте getIdentifier() на вашем Resources, чтобы найти фактический идентификатор ресурса строки, и оттуда получить строку, привязанную к вашей теме.

Отклонение от этого было бы поместить базовое имя в android:tag вместо android:text. android:text может указывать на один из ваших реальных ресурсов строки, чтобы помочь с дизайном графического интерфейса и т. Д. Ваш LayoutInflater захватит тег и будет использовать его для получения правильной строки во время выполнения.

Если вы будете заменять текст другим текстом, вырванным из тематических ресурсов строки, вы можете изолировать свою логику get-the-string-given-the-base-name в статическом утилите, где бы вы ни применили.

Хотя получение этого права первоначально потребует немного работы, оно будет масштабироваться до произвольной сложности с точки зрения количества видимых виджетах и ​​строках пользовательского интерфейса. Вы все равно должны помнить, чтобы добавлять значения для всех тем для любых новых строк, которые вы определяете (бонусные баллы для создания пользовательской проверки Lint или задачи Gradle для проверки этого).

+0

Думаю, я мог бы использовать мускусный/цветочный, а не мужской/женский, но я сказал: пользователя ", так что пол пользователя может не иметь никакого отношения к их решению по теме;) Вы были правы в предположении, что я хотел бы использовать строковые ресурсы. Мне нравится, когда вы взяли это, и это кажется разумным выполнимым. Когда я получу какой-то код, брошенный вместе, я опубликую здесь подробное описание. С уважением! –

1

Поскольку ресурс всего лишь int в глубине души, вы можете хранить их во время выполнения, и они заменяют их процедурно, как вы их используете.


<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <string name="OK_NORMAL">Okay</string> 
    <string name="OK_PIRATE">Yaaarrrr!</string> 
    <string name="OK_NINJA">Hooooaaa!</string> 
</resources> 

public enum ThemeMode { 
    PIRATE, 
    NINJA, 
    NORMAL; 
} 

public class MyThemeStrings { 
    public static int OK_PIRATE = R.string.OK_PIRATE; 
    public static int OK_NINJA = R.string.OK_NINJA; 
    public static int OK_NORMAL = R.string.OK_NORMAL; 
} 

public setOkButtonText(ThemeMode themeMode) { 
    // buttonOk is instantiated elsewhere 
    switch (themeMode) { 
     case PIRATE: 
      buttonOk.setText(MyThemeStrings.OK_PIRATE); 
      break; 
     case NINJA: 
      buttonOk.setText(MyThemeStrings.OK_NINJA); 
      break; 
     default: 
      Log.e(TAG, "Unhandled ThemeMode: " + themeMode.name()); 
      // no break here so it flows into the NORMAL base case as a default 
     case NORMAL: 
      buttonOk.setText(MyThemeStrings.OK_NORMAL); 
      break; 
    } 
} 

Хотя, написав все, что есть про возможно, лучший способ сделать это через отдельные XML-файлы. Я рассмотрю его сейчас и напишу второе решение, если найду его.

+0

Я боюсь, что гигантское картографирование, скорее всего, для меня. Ваше решение будет работать, но, черт возьми, это может привести к большому количеству кода :) –

+1

Пока вы можете упорядочить/отвлечь все это на свой собственный пакет и файлы string_something.xml, а затем просто разоблачить один метод getter, принимающий 'enum Аргумент темы для каждой строки. Я бы сказал, что она не слишком громоздка и не позволяет интернационализации с отдельными папками «values-locale». Но да, есть несколько вещей, чтобы следить за расширением функциональности. – indivisible

1

Хорошо, у меня есть второй вариант, который может быть проще в обслуживании и содержать ваш код в чистоте, хотя он может быть более ресурсным голодным из-за загрузки массива для каждой строки. Я не тестировал это, но предлагаю это как другой вариант, но я бы не использовал его, если вы предлагаете слишком много вариантов темы.


public enum ThemeMode { 
    NORMAL(0), 
    PIRATE(1), 
    NINJA(2); 

    private int index; 
    private ThemeMode(int index) { 
     this.index = index; 
    } 

    public int getIndex() { 
     return this.index; 
    } 
} 

<resources> 
    <!-- ALWAYS define strings in the correct order according to 
     the index values defined in the enum --> 
    <string-array 
     name="OK_ARRAY"> 
     <item>OK</item> 
     <item>Yaarrrr!</item> 
     <item>Hooaaaa!</item> 
    </string-array> 
    <string-array 
     name="CANCEL_ARRAY"> 
     <item>Cancel</item> 
     <item>Naarrrrr!</item> 
     <item>Wha!</item> 
    </string-array> 
</resources> 

public setButtonTexts(Context context, ThemeMode themeMode) { 
    // buttons are instantiated elsewhere 
    buttonOk.setText(context.getResources() 
      .getStringArray(R.array.CANCEL_ARRAY)[themeMode.getIndex()]); 
    buttonCancel.setText(context.getResources() 
      .getStringArray(R.array.OK_ARRAY)[themeMode.getIndex()]); 
} 
+0

Это сокращает небольшой код. Ницца.Я собираюсь продолжать копать какое-то время, но это может быть мое решение. –

+1

Как я уже сказал наверху, будьте осторожны при использовании этого со слишком большим количеством вариантов темы, поскольку каждая тема добавляет еще одну строку в каждый массив. В зависимости от того, сколько вы загружаете в любое время и сколько тем вы хотите предложить, это может увеличить потребление ресурсов вашего приложения на заметную сумму. Если вы планируете делать это для большого количества ресурсов во всем своем приложении, определенно стоит потратить несколько бенчмаркинга на три предлагаемые здесь решения. Если это всего лишь пара мест как незначительная новинка, чем просто пойти с самыми легкими в реализации, так как это не имеет большого значения. – indivisible

7

Да, это может быть сделано, и вот как: сначала вы должны будете определить атрибут темы, например, так:

<attr name="myStringAttr" format="string|reference" /> 

Затем, у вас г темы, добавьте эту строку

<item name="myStringAttr">Yarrrrr!</item> 

или

<item name="myStringAttr">@string/yarrrrr</item> 

Вы можете использовать этот атрибут в файле XML, как так (обратите внимание на ? вместо @).

<TextView 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="?attr/myStringAttr" /> 

или из кода, например, так:

public CharSequence resolveMyStringAttr(Context context) 
{ 
    Theme theme = context.getTheme(); 
    TypedValue value = new TypedValue(); 

    if (!theme.resolveAttribute(R.attr.myStringAttr, value, true)) { 
     return null; 
    } 

    return value.string; 
} 
+0

Это фантастика. Он масштабируется? Итак, есть (сделать это) 12 кнопок и 32 TextViews. Если я правильно прочитаю ваше решение; У меня должен быть специальный attr для каждой строки, я хотел бы настроить тему, правильно? –

+1

Ну, это недостаток этого подхода. В этом случае вам придется добавить 44 новых атрибута, но я не могу придумать другого способа сделать это, используя только XML. – caspase

+0

Очень ценится. Я тоже не мог думать ни о каком другом, но я не хотел никого подшучивать над творчеством, выложив их с ответом :) –

1

Итак, у меня не было возможности проверить это, но, прочитав файл в Locale, похоже, что вы можете создать свое собственное местоположение.

http://developer.android.com/reference/java/util/Locale.html

и помощь от другого StackOverflow

Set Locale programmatically

Немного комбинации приводит меня к:

Locale pirate = new Locale("Pirate"); 
Configuration config = new Configuration(); 
config.locale = pirate; 
this.getActivity().getBaseContext().getResources() 
    .updateConfiguration(config, 
    this.getActivity().getBaseContext().getResources().getDisplayMetrics()); 

Я считаю, что это позволит вам иметь RES/значения -pirate/strings как фактический действительный ресурс, который будет использоваться, когда вы будете пиратом. Любые строки или настройки, которые вы не переопределяете, затем возвращаются к параметрам res/values ​​/ ... по умолчанию, поэтому вы можете сделать это для столько тем, сколько хотите. Опять же, если это работает.

+0

Это довольно фантастично. Мне нужно собрать пример для этого ответа;) –

+0

Если это сработает, я думаю, что он будет гораздо менее задействован, чем другие методы. Вам просто нужно выбрать этот новый «язык» где-нибудь, а затем, возможно, сохранить его как часть SharedPreferences для этого приложения. – Zeppie

Смежные вопросы