How to set the theme for the application, to avoid wrong color transitions? - android-theme

Background
I'm developing a themes chooser feature in my "app manager" app, and I've succeeded setting the theme dynamically for each of the activities.
AGAIN : this is not about setting the theme for the activities. This actually works fine for me.
The problem
The acitivties are showing the correct theme, but the application itself, when launching the app, is showing the wrong one, no matter what I do.
This is a problem because when the user opens the app, he will see the background of the theme of the app, and only after a few moments the activity will be shown with the theme the user has chosen.
So, if the application has a white background, and the user has chosen a theme with a black background, the order will be:
Application shows a white background -> activity is starting and shows a black background.
In screenshots:
So this is wrong. In this case, I need it to show black-to-black background.
Only if the user has chosen a Holo-light based theme (which the app has by default), it works fine, as the color matches the one on the activity that is shown right when opening the app.
What I've tried
I had an idea of setting the app's theme to be empty of everything, hoping that no transition will be shown, using something like:
<application
...
android:theme="#android:style/Theme.Translucent.NoTitleBar" >
In fact, some people here suggested a similar solution.
This works, but it causes a bad experience, since on some devices it takes some time till the first activity is shown, and as such, there is a significant amount of time the user sees nothing at all, as if the app isn't being launched.
The question
How do I solve this?
I've already tried setting the theme in the class that extends from Application, but it doesn't do anything, no matter where in this class I call it.

A bit late, but this may be the answer. I discovered it by chance.
No entrance activity, no custom animations, no hacking. Simply an attribute in theme. Android buried this deep inside its resources.
Add the following attribute to your app theme:
<!--
~ From Theme.NoDisplay, this disables the empty preview window probably
~ with an incorrect theme.
-->
<item name="android:windowDisablePreview">true</item>
And your are done. Enjoy it!

Transparent application theme with fade-in animation
My original suggestion was to use a Transparent full screen application theme (no action bar).
Combined with that, I always suggest an alpha-animation to fade across from the application theme to the activity theme. This prevents jarring to the user when the action bar appears.
OP's code would remain almost identical, except for changing the manifest theme, and adding the alpha animation in your onCreate() method of some base activity class as in examples below:
manifest theme defined as:
android:theme="#android:style/Theme.Translucent.NoTitleBar"
base activity onCreate() method:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// set your custom theme here before setting layout
super.setTheme(android.R.style.Theme_Holo_Light_DarkActionBar);
setContentView(R.layout.activity_main);
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
basic fade in:
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
basic fade out (not really needed, but for completeness):
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
Of course the animation durations here are way longer than you'd put in production - they are long so you can see them in your development stages.
Update #1:
It has been noted subsequently in comments by #EmanuelMoecklin, #androiddeveloper that this was considered. It is also included in answer by dentex. However, as the OP states, the weakness particularly on older devices is that the user gets no feedback when they try to launch the app. It appears the app takes too long to launch.
On KitKat, this is not the case, since the status bar & soft-keys change from transparent to black, while the rest of the screen is still transparent.
Another take on this approach would be to use a full-screen black background as the application theme. This is what was done by Bitspin for Timely, who were bought by Google apparently on the basis of the stunning UI in that app. It seems this method is therefore quite acceptable in many cases.
Update #2:
In order to speed up the perception of the launch, an alternative to the plain black theme is to use a full-screen image with the app's logo in the centre - "splash screen" style. Again fading across to the activity once launched.
This is not possible for the transparent theme, using a transparent full-screen image. Android ignores the transparency of the image (or overlays the transparent image onto a black background). This was pointed out by OP in the comments.
We can either have a transparent theme without an image, or an opaque theme with an image (an interesting topic for another question perhaps).
A note on using Manifest aliases
Another suggestion by #sergio91pt is to use aliases for different activities in the manifest.
While this can be a useful technique in some circumstances, in this case it has some drawbacks:
Any HOME screen shortcut the user has created for the activity will stop working when the main launcher alias is changed i.e. each time the user changes themes.
Some devices / launchers are quite slow to activate & deactivate the different aliases. In my experience this can take seconds (Galaxy Nexus 4.1 iirc), during which time you either have no visible launch icon, or you have 2 icons.
Each possibly theme requires a different alias - this may prove cumbersome if there are many different themes.

To fix any flickering (action bar, title...) upon app's start, I have set into the manifest
android:theme="#android:style/Theme.NoTitleBar"
for both my main activities (a tab container and a settings activity, from where I switch the themes, based on holo dark and light)
If you use some "launcher activity" or "splash activity" apply Theme.NoTitleBar also for them, then:
having declared Theme.NoTitleBar, for each activity, in onCreate you have to:
set the title properly with setTitle(...) and THEN
set the theme with setTheme(R.style.CustomAppTheme) BEFORE setContentView(...)
(and you already do this);
This will prevent the flashing of the action bar/title when switching theme (if done "on-the-fly") and upon app's start.
If you want a custom action bar appearance, this means that the default holo action bar will not flash before yours.

The transition color is retrieved from the activity theme on the manifest (or the application if not set).
Currently the only way around this limitation is to create a dummy subclass for each real Activity, eg. MyActivityLight, to declare a different theme. Activity alias won't work, the attribute will be ignored.
For activities with IntentFilter's, you should only maintain one of each "type" enabled, using PackageManager#setComponentEnabledSetting(). Note that the change may take some seconds.
For activities that are started by class name, you can infer the correct prefix according to the user's theme.
So lets suppose you have 2 themes: AppTheme.Dark and AppTheme.Light and some activities.
The dark theme is the default one.
Original manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example">
<application android:theme="#style/AppTheme.Dark">
<activity
android:name=".PrivateActivity"
android:exported="false" />
<activity android:name=".ShowActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
</manifest>
Change all activities above as abstract classes and create dummy subclasses suffixed by Light and Dark.
Then the manifest should be changed like this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example">
<!-- No application theme -->
<application>
<activity android:name=".PrivateActivityDark"
android:theme="#style/AppTheme.Dark"
android:exported="false" />
<activity android:name=".PrivateActivityLight"
android:theme="#style/AppTheme.Light"
android:exported="false"
android:enabled="false" />
<activity
android:name=".ShowActivityDark"
android:theme="#style/AppTheme.Dark">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity
android:name=".ShowActivityLight"
android:enabled="false"
android:theme="#style/AppTheme.Light">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
</manifest>
Then you could have something like this to get the themed Activity class, given an abstract Activity:
public static ComponentName getThemedActivityName(
Context ctx,
Class<? extends Activity> clazz) {
// Probably gets some value off SharedPreferences
boolean darkTheme = isUsingDarkTheme(ctx);
String baseName = clazz.getName();
String name += (darkTheme) ? "Dark" : "Light";
return new ComponentName(ctx, name);
}
public static void startThemedActivity(
Activity ctx,
Class<? extends Activity> clazz) {
Intent intent = new Intent();
intent.setComponent(getThemedActivityName(ctx, clazz));
ctx.startActivity(intent);
}
And also change the enabled status where needed, when the theme is changed.
public void onThemeChanged(Context ctx, boolean dark) {
// save theme to SharedPreferences or similar and...
final PackageManager pm = ctx.getPackageManager();
final String pckgName = ctx.getPackageName();
final PackageInfo pckgInfo;
try {
final int flags = PackageManager.GET_ACTIVITIES
| PackageManager.GET_DISABLED_COMPONENTS;
pckgInfo = pm.getPackageInfo(pckgName, flags);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
final ActivityInfo[] activities = pckgInfo.activities;
for (ActivityInfo info: activities) {
final boolean enable;
if (info.theme == R.style.AppTheme_Light) {
enable = !dark;
} else if (info.theme == R.style.AppTheme_Dark) {
enable = dark;
} else {
continue;
}
final int state = (enable) ?
PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
final String name = info.targetActivity;
final ComponentName cmp = new ComponentName(pckgName, name);
pm.setComponentEnabledSetting(cmp, state, PackageManager.DONT_KILL_APP);
}
}
If doing IPC on a loop scares you, you can do this asynchronously on a helper thread, as long as multiple calls to onThemeChanged() run sequentially.
Note that in this example I change the enabled status of all activities (that have a known theme) but only had to do that for the ones with intent filters. If the activities aren't hardcoded its easier this way.
Important Note: As Richard Le Mesurier and other have pointed out, using this technique on Launcher Activities removes or disables the shortcut on the home screen, if it exists. This is just a solution for non launcher Activities.

Related

Android Studio 3.6 - how use layouts in Design Palette?

I am new to Android development and Android studio. I'm using Android Studio 3.6.1 . I'm having trouble understanding the "Layouts" options in the Design Palette. First, I don't see a "RelativeLayout" option. I realize many feel this is superceded by the ConstraintLayout, but it seems to me it should be a choice. Second, I don't see how to even use these layout options. If I drag one to an existing design, nothing happens. I thought maybe it would replace the root layout, but doesn't seem to. It doesn't create a child layout (if such a thing is possible). My code is just a simple "MainActivity" class with "setContentView(R.layout.activity_main);" in the onCreate() method. I have been tinkering with the activity_main.xml file to learn UI concepts. I can type in manual changes to change the layout to RelativeLayout, but it wasn't obvious what the classpath of the RelativeLayout class was, it is not in the same package as the ConstraintLayout class. I'm trying to use the power of the Android Studio IDE to discover options and build code, but I'm not finding how to use it for Layout options. I tried emptying the "activity_main.xml" file and then dragging a Layout from the palette, but nothing happens. I can delete the "activity_main.xml" file and create a new one, but when it prompts for a layout, there is no discovery to help choose one, it seems you have to know the package.
How can these Layouts in the Design Palette actually be used in the IDE? Can RelativeLayout be added to the list? Are there other missing Layouts?
package com.example.test;
import android.os.Bundle;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
<?xml version="1.0" encoding="utf-8"?>
<android.widget.RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
</android.widget.RelativeLayout>
Something changed and I can now right-click a Layout and "add to design" or drag it to the design. I can also right-click the Layout in the component tree and convert to another layout, including RelativeLayout. I had added a Calendar component to my design for the heck of it and built and run in the emulator. After that, I deleted the Calendar and found I was able to now drag the layout components or convert the root component. So I'm comfortable with use of Layouts in the Palette. I am still unclear how to discover Layouts when creating a new layout XML file when it asks for "root tag". But I realize you don't need a full path, just the class name it seems, apparently the IDE finds the class. The glue behind the scenes I'll need to learn more about.

Can't remove padding from Activity Dialog

I have an activity styled as a popup:
[Activity(Theme="#android:style/Theme.Dialog")]
public class GameOverPopup : Activity
But I'm always stuck with a white padding around my controls (ignore the black border, it's not part of the popup):
I've tried setting the padding, margin, etc. to 0dp but always have that extra space.
How can I remove it?
UPDATE:
So I've tried the following according to suggestions:
In Styles.xml:
<style name="PopupDialog" parent="android:style/Theme.Dialog">
<item name="android:padding">0dp</item>
</style>
And then in my Activity (in the OnCreate Override):
SetTheme(Resource.Style.PopupDialog);
Yet I still have the padding :(
As I recall, the auto-generated activities in Eclipse include the Android activity-wide margins by default. You might check to make sure you have removed these lines in your XML layout if you used the default template.
EDIT, to sum up some comments and avoid creating a discussion: it appears that the problem may be due to the default padding in the dialog box style. Try creating a style with that style as its parent, and manually set the padding/margins as needed: http://developer.android.com/guide/topics/ui/themes.html#DefiningStyles.
Got it!!
Problem is that I set the theme in code on the OnCreate AS WELL as an attribute to the activity. So I remove the code from OnCreate and put the following attribute in my popup Activity class:
[Activity(Theme = "#style/PopupDialog")]
public class GameOverPopup : Activity
And my Styles.xml is:
<style name="PopupDialog" parent="android:style/Theme.Dialog">
<item name="android:windowBackground">#null</item>
<item name="android:padding">0dp</item>
</style>

Using appcompat support library, Overflow not working on API 7?

I am using v7 appcompat support library. The action items work showing in the actionBar just fine on either newer or older devices.
However, I don't get Overflow on API 7. For example:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:appcompat="http://schemas.android.com/apk/res-auto" >
<item
android:id="#+id/menuAction"
android:icon="#android:drawable/ic_menu_directions"
android:title="#string/barButtonTitle"
appcompat:showAsAction="always|withText"/>
<item
android:id="#+id/menuAction1"
android:title="#android:string/untitled"
appcompat:showAsAction="never"/>
</menu>
appcompat:showAsAction="always|withText" works, it shows as an Action Item in ActionBar. However, appcompat:showAsAction="never" doesn't show in the Overflown as it does in newer platform. It is hidden on the bottom, and revealed when you pressed on the older menu button.
Is that the intended behavior from appcompat support library? or am I missing anything to get the Overflow working on the older platform?
Is that the intended behavior from appcompat support library?
Yes, it is
The overflow button (3 dots) is only shown if the devices don't have a hardware menu button.
Every gingerbread-device must have a menu-button, so the overflow button won´t show on API level 7
For more information about this topic, the following blog-post might be interesting:
http://android-developers.blogspot.de/2012/01/say-goodbye-to-menu-button.html
The overflow do not show in older versions as far as I have researched, but you can use in your manifest (inside the activity level) the following (for pre ICS):
<activity ...>
<meta-data android:name="android.support.UI_OPTIONS"
android:value="splitActionBarWhenNarrow" />
</activity>
this will show the action itens at the bottom of the display, you have to add this in all activities you want to have this behavior. For ICS > you can add the following in the manifest at the application level, this will have effect in all activities.
<application android:uiOptions="splitActionBarWhenNarrow">
</application>

Android - tablet to phone screensize

I have an app that has a screen like this (on a 10" tablet) :
Now I need to amend the app to also work on a phone. As the screens will be smaller, I want to take the "split view" UI and change it so that the left hand side list view is shown on its own, then on selecting a row the appropriate right hand side list view is then shown.
How do I handle this in the app, as one activity currently handles both listviews, and I guess the phone will need two one for each listview.
How do I detect which one to do?
thanks
See Supporting Different Screen Sizes.
Typically this is done using Fragments, but the basic idea is the same whether you use fragments or not. You create two different layouts for your Activity depending on the screen size.
Save the default layout single-pane for phones at res/layout/activity_main.xml
Save the dual-pane tablet layout at res/layout/activity_main_twopane.xml
Then you use layout alias files with the screen size qualifiers described in the link to determine when the tablet layout should be used. For example to show the dual-pane layout on large screens and on screens with at least 600dp in the widest direction (includes large screen phones such as the Galaxy S3), you could do this:
res/values-large/layout.xml contains:
<resources>
<item name="activity_main" type="layout">#layout/activity_main_twopane</item>
<bool name="twopane">true</bool>
</resources>
res/values-sw600dp/layout.xml contains:
<resources>
<item name="activity_main" type="layout">#layout/activity_main_twopane</item>
<bool name="twopane">true</bool>
</resources>
The Android system will take care of loading the proper layout file (either res/layout/activity_main.xml or res/layout/activity_main_twopane.xml) when your Activity loads the layout:
setContentView(R.layout.activity_main);
Just remember that the views that don't exist in the single-pane layout will be null when you try to access them (e.g., there won't be two ListViews anymore). Checking whether a certain View exists is one way to detect which layout you are using.
Also note the use of optional Boolean resources in XML files. This is a handy way to pass the "is it a large screen or small screen" variable to your Java code. You can access Boolean resources in your Activity like this:
boolean isTwoPane = getResources().getBoolean(R.bool.twopane);
You should have a look at the MasterDetailedFlow Navigation template. Eclipse:NewProject>check create Activity>select "MasterDetailedFlow" Navigatoin type. Have a look at Data Binding on Android

Android "tools" namespace in layout xml documentation

Per the question here,
What's "tools:context" in Android layout files?
The 'tools' namespace reference (xmlns:tools="http://schemas.android.com/tools") has begun to appear in my layouts recently, and I want to know more. The original post only described the 'tools:context' attribute, but I have also noticed usage of the "tools:listitem" attribute appearing when I have designated a preview layout item for a listview, i.e.
<ListView
android:id="#+id/lvCustomer"
tools:listitem="#layout/customer_list_item" >
</ListView>
Are there more elements?
What brought me to this 'tools' namespace is that I want to be able to have 'preview-only' text (i.e. in a TextView or EditText) when using the layout designer in eclipse.
Currently, I assign the 'text' or 'hint' property for previewing text when arranging my layouts... but then I always have to remember to clear the preview value from within the code.
Ideally, instead of
<string name="preview_customer_name">Billy Bob's Roadhouse Pub</string>
...
<TextView
android:id="#+id/tvCustomerName"
android:text="#string/preview_customer_name"
</TextView>
have a something like:
<TextView
android:id="#+id/tvCustomerName"
tools:previewText="#string/preview_customer_name"
</TextView>
Thanks-
We've just added support for designtime attributes like this in Android Studio 0.2.11. See http://tools.android.com/tips/layout-designtime-attributes for more.
Think of them as design time helpers only.They do not get processed in actual view rendering at run time.
For example you want to set background of some view in your layout design when working on android studio so that you can make clear distinction where that particular view is.So you would normally do that with
android:background="#color/<some-color>"
Now risk is that sometimes we forget to remove that color and it gets shipped in apk.
instead you can do as follows:
tools:background="#color/<some-color>"
These changes will be local to android studio and will never get transferred to apk.
And also check out http://tools.android.com/tech-docs/tools-attributes for more options.
You will find tool attribute when you set object in graphical layout.
Listview (in graphical mode) -> right Click -> Preview List Content -> Choose Layout...
produces:
tools:listitem="#layout/customer_list_item"
See in layout XML below. There are 2 namespace in use "xmlns:android" and "xmlns:tools".
Tools namespace is used when the developer wants to define placeholder content that is only used in preview or in design time. Tools namespace is removed when we compiled the app.
So in the code below, I want to show the placeholder image (image_empty) that will only be visible at design time, and image1 will the actual image that will be shown when the application launch

Resources