2

Before asking this question, I have researched a bit on it and also looked for similar questions on SO, none of which seemed to answer it.

I am planning to provide visual accessibility feature in my android app where users will be able to negate the color of UI in one go(lets say a simple button to negate the colors).

There are few exceptions though. There are few views(say action bar) whose color is better to be fixed even if user chooses to negate the color, for readability.

I can think of few options to achieve the same:

  1. Maintain two copies of each layout xml - one with normal colored views and one with negated colored views. Now, whenever user chooses to negate the colors, just load the corresponding xml in java.
  2. Maintain two copies of color of each view in color.xml. Define them under two separate themes. Now set the corresponding theme based on what user wants.

Challenges:
Scalability: Given that the app is already using hundreds of views, hundreds of XML, and it is going to be using more and more views in future updates, how can I take care of that?

Questions:
1. Which option would be better for this keeping scalability in mind.
2. Is there any other way of doing the same?
3. Is there any android API which gives the negated color of a view?

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Green goblin
  • 9,898
  • 13
  • 71
  • 100

1 Answers1

1

Seems to me that the best approach is to maintain a normal theme and a negated theme in your app and then depending on the users setting, change themes at runtime.

There is a catch with this approach which is that you have to setTheme before setContentView in your activity. Which means that you can't reflect the changes immediately after the user has clicked a button. That being said, this is the best solution in my opinion as it scales nicely and you can probably have a BaseActivity that all your activites inherit from which can have the check to set your theme.

setTheme(userBlackTheme? android.R.style.Theme_Dark : android.R.style.Theme_Light);
super.onCreate(savedInstanceState); //IMPORTANT: setTheme is before the super call!!!
setContentView(R.layout.main);

If you have a button on the screen and want to do this, then you should be able to swap out all the style attributes on your Views and replace them with Dark attributes. Meaning, while you won't change your theme, you can change the attributes of each of your Views to be those of your dark theme.

You would then have to recursively loop through all your Views and depending on the class of the View change it's attributes to a Dark/Light.

public void recursiveLoopChildren(ViewGroup parent) {
    for (int i = parent.getChildCount() - 1; i >= 0; i--) {
        final View child = parent.getChildAt(i);
        if (child instanceof ViewGroup) {
            ((ViewGroup) child).setBackgroundColor(useDarkTheme? Color.BLACK : Color.WHITE);
            recursiveLoopChildren((ViewGroup) child);

            // DO SOMETHING WITH VIEWGROUP, AFTER CHILDREN HAS BEEN LOOPED 
        } else { 
            if (child instanceof TextView) {
                ((TextView) child).setTextColor(...);
            } 
        } 
    } 
} 

In short, you probably want to implement both of these, with option 1 all new activites will use a dark theme with option 2 when you click on the change theme button, your current activity will be changed. OR... when the button to change the theme is clicked, reload the current activity, not as elegant to the user, perhaps you can reload the same activity with a dark theme and fade it in above the light theme and then call finish() on the light theme providing a nice transition while simplifying your life. Hope this helps.

Ali
  • 12,354
  • 9
  • 54
  • 83
  • Thanks for the detailed response :) I have few doubts on the theme approach though. How would I define each view color inside a theme? And how is it going to fit in a single xml approach model? – Green goblin Jun 24 '15 at 21:01
  • You can define styles for each view inside your theme, it's a bit of a pain, we are very conditioned to using a lot of formatting inline in the layout xml. I would recommend trying it with a simply screen first and then taking it to more complicated screens. I can tell you this, is is possible as mostly it means setting default background color to white or black and setting the primary, secondary and tertiary text color. – Ali Jun 24 '15 at 21:04
  • Formatting inside xml is my least concern(at least now). What I understood from your explanation is: Define two themes. Then define two versions of style of each view inside each theme. What value of color would then be xml view be holding? And I am afraid applying such heavy theme to change each view color would incur a huge runtime cost in app. – Green goblin Jun 24 '15 at 21:09
  • You can theme a screen and time it to see the impact. I don't think you should be concerned, I would app that `setTheme` should also be before the `super` call in onCreate. I've just written a quick sample app to do this and it seems to work as expected. – Ali Jun 24 '15 at 21:46
  • I've added a [gist showing the implementation](https://gist.github.com/alphamu/f2469c28e17b24114fe5). – Ali Jun 24 '15 at 22:11
  • Thanks Ali. I finally did what I wanted. However, I ran into another issue. After using setTheme, it started crashing for non-lollypop devices. I would appreciate if you could help me with this. http://stackoverflow.com/questions/31146340/kitkat-specific-android-content-res-resourcesnotfoundexception-file-res-drawa – Green goblin Jun 30 '15 at 19:04
  • Solved, oddly I had the same problem 2 days ago. – Ali Jul 01 '15 at 12:38