Android:How to programmatically set an Activity's theme to Theme.Dialog - android

So I have an Activity (say TestActivity) which needs to act as a normal unthemed Activity as well as a Theme.Dialog at other place. I am trying to reuse same TestActivity for both the tasks.
All I am looking for setting the theme dynamically.
The code is simple:
Here is my activity's onCreate that works with a black background
public void onCreate(Bundle icicle) {
if (Utility.isDialog == true)
setTheme(android.R.style.Theme_Dialog);
super.onCreate(icicle);
requestWindowFeature(Window.FEATURE_NO_TITLE);
.....
and here is the Manifest Entry
<activity android:name=".TestActivity"/>
And in the meantime I found a post that says it can't be done here is the post http://code.google.com/p/android/issues/detail?id=4394 .But there is a strong feeling that it can be done.
All suggestions are welcome.

Would like to give a work around for this problem.
Problem : How to use the same activity as both dialog and full screen based.
Solution :
Define your activity in your AndroidManifest.xml with the theme #android:style/Theme.Dialog
In your respective .Java file, check for an intent extra that defines dialog mode.
If it does not exist, set the Theme to android.R.style.Theme. This is the default theme which is applied if you do not define any theme.
Code :
boolean fDialogMode = getIntent().hasExtra("dialog_mode");
if( ! fDialogMode ) {
super.setTheme(android.R.style.Theme);
}
Alternate Solution:
A more complex solution is to use AlertDialog as below:
Define a ListAdapter class extended from ArrayAdapter.
return 1 in getCount function
#Override
public int getCount() { return 1; }
In the getView function, inflate the layout of the activity you need and do any customization before returning the view.
#Override
public View getView( int position, View view, ViewGroup group ) {
View v = view;
if( v == null ) {
v = getSystemService(Context.LAYOUT_INFLATER_SERVICE).inflate( <layout res id>, null );
}
... Do any customization here ....
return v;
}
This is definitely a second choice option by if you are not doing too much processing in the activity class this could be an option.
Only reason to consider this solution could be that the logic to show it in a dialog is isolated to the places where it is used as a dialog.
Both the options worked for me but for obvious reasons I am taking the first option. :-)

you can use setTheme(..) before calling setContentView(...)and super.oncreate() and it should work fine

Like several others, calls to setTheme in onCreate (before or after my call to super.onCreate) did not work. However, by overriding setTheme, I was able to specify a theme other than that stated in Manifest.xml. Specifically, the following worked without issue:
#Override
public void setTheme(int resid) {
boolean changeTheme = true;
super.setTheme(changeTheme ? android.R.style.Theme_Dialog : resid);
}
I found the above in the discussion at: https://code.google.com/p/android/issues/detail?id=4394

Call Activity.setTheme() in onCreate() before you call setContentView().

use setTheme before calling super.onCreate(savedInstance)

This may not be applicable in your situation, but you can use the theme:
Theme.Holo.DialogWhenLarge
and it will display your activity as a dialog when the screen is large, and as a regular activity when the screen is small.
This is covered in the Android documentation on Dialogs and also contains information on programming a Dialog which can also sun as a full screen fragment.

Default theme library call:
super.setTheme(android.R.style.Theme);
In my case I was using AppCompat Theme, so make sure that on your id you refer the proper library (i.e.):
super.setTheme(android.support.v7.appcompat.R.style.Theme_AppCompat_NoActionBar);

I know that I am late but I would like to post a solution here:
Check the full source code here. This is the code I used when changing theme...
SharedPreferences pref = PreferenceManager
.getDefaultSharedPreferences(this);
String themeName = pref.getString("prefSyncFrequency3", "Theme1");
if (themeName.equals("Africa")) {
setTheme(R.style.AppTheme);
} else if (themeName.equals("Colorful Beach")) {
//Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show();
setTheme(R.style.beach);
} else if (themeName.equals("Abstract")) {
//Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show();
setTheme(R.style.abstract2);
} else if (themeName.equals("Default")) {
setTheme(R.style.defaulttheme);
}

setTheme(android.R.style.Theme_Dialog);

Related

Dynamically recolor activity background before showing it

I need to set custom color both to actionbar and to client area. With the following code my app get succesfully colored, however, I still see the default theme for like 0.5 seconds when the activity starts. How do I remove this gap?
The color is set dynamically, so I guess I cannot use theme definition here. (I will later change my code to get color from an Intent)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_note_screen);
setNoteColor(0xFFFFF8DC);
}
public void setNoteColor(int color) {
getWindow().getDecorView().setBackground(new ColorDrawable(color));
assert getActionBar() != null;
getActionBar().setBackgroundDrawable(new ColorDrawable(color));
}
Funny thing I just read about that yesterday... =)
Here is a great post regarding you issue

only change layout color while orientation change android

I have create dialog box which will be displayed first when i start application (which is coded in onCreate method ) and then question and answer will be displayed on textview
so to solve problem of orientation (so dialog box again not displayed when orientation change) i have used Manifest with:
android:configChanges="keyboardHidden|orientation"
I have res/layout-land, res/layout-port
but to change only COLOR of background
I have used onConfigurationChanged in my Activity (it get's called on rotation).
so now when orientation change the dialog box will not appear again and background is redrawn but the question and answer which is initialized on onCreate() will not display
so how to maintain
Using android:configChanges="keyboardHidden|orientation" means that your activity handles a configuration change itself, and the system will not change the layout.. take a lokk at http://developer.android.com/guide/topics/resources/runtime-changes.html
To reset the text view, in case of not using android:configChanges="keyboardHidden|orientation":
#Override
public Object onRetainNonConfigurationInstance() {
final MyDataObject data = collectMyLoadedData();
//here get the text from the text view, and any other info
return data;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance();
if (data != null) {
//get the text and set it in the text view
}
}
When you change the orientation of the devide, android continue the execution without calling onCreate again, but if you put the code in onResume, this code will executed again.
In OnCreate add for your main layout as
LinearLayout lv=(LinearLayout)findViewById(R.id.mainlayout);
lv.setBackgroundColor(android.R.color.black); // for default potrait or landscape view and after that add this in that activity
#Override
public void onConfigurationChanged(Configuration newConfig) {
Configuration c = getResources().getConfiguration();
if(c.orientation == Configuration.ORIENTATION_PORTRAIT ) {
// portrait
lv.setBackgroundColor(android.R.color.black);
} else if(c.orientation == Configuration.ORIENTATION_LANDSCAPE ){
// landscape
lv.setBackgroundColor(android.R.color.white);
}
}
and also add this in menifest in your activity defined
android:configChanges="keyboardHidden|orientation"
Did you use onSaveInstanceState to save the data and onRestoreInstanceState for retriving the data?
If you have done this then you can display the same dialog box in onRestoreInstanceState.
Well I have a question for you. How did you kept the background color unchanged when changing the orientations? And what to do if the color is unknown or a random color?

How to check if the current activity has a dialog in front?

I am using a third-party library and sometimes it pops up a dialog. Before I finish the current activity, I want to check whether there is a dialog popped up in the current context.
Is there any API for this?
You can check it running over the active fragments of that activity and checking if one of them is DialogFragment, meaning that there's a active dialog on the screen:
public static boolean hasOpenedDialogs(FragmentActivity activity) {
List<Fragment> fragments = activity.getSupportFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment instanceof DialogFragment) {
return true;
}
}
}
return false;
}
I faced a similar problem, and did not want to modify all locations where dialogs were being created and shown. My solution was to look at whether the view I was showing had window focus via the hasWindowFocus() method. This will not work in all situations, but worked in my particular case (this was for an internal recording app used under fairly restricted circumstances).
This solution was not thoroughly tested for robustness but I figured I would post in in case it helped somebody.
This uses reflection and hidden APIs to get the currently active view roots. If an alert dialog shows this will return an additional view root. But careful as even a toast popup will return an additional view root.
I've confirmed compatibility from Android 4.1 to Android 6.0 but of course this may not work in earlier or later Android versions.
I've not checked the behavior for multi-window modes.
#SuppressWarnings("unchecked")
public static List<ViewParent> getViewRoots() {
List<ViewParent> viewRoots = new ArrayList<>();
try {
Object windowManager;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
windowManager = Class.forName("android.view.WindowManagerGlobal")
.getMethod("getInstance").invoke(null);
} else {
Field f = Class.forName("android.view.WindowManagerImpl")
.getDeclaredField("sWindowManager");
f.setAccessible(true);
windowManager = f.get(null);
}
Field rootsField = windowManager.getClass().getDeclaredField("mRoots");
rootsField.setAccessible(true);
Field stoppedField = Class.forName("android.view.ViewRootImpl")
.getDeclaredField("mStopped");
stoppedField.setAccessible(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
List<ViewParent> viewParents = (List<ViewParent>) rootsField.get(windowManager);
// Filter out inactive view roots
for (ViewParent viewParent : viewParents) {
boolean stopped = (boolean) stoppedField.get(viewParent);
if (!stopped) {
viewRoots.add(viewParent);
}
}
} else {
ViewParent[] viewParents = (ViewParent[]) rootsField.get(windowManager);
// Filter out inactive view roots
for (ViewParent viewParent : viewParents) {
boolean stopped = (boolean) stoppedField.get(viewParent);
if (!stopped) {
viewRoots.add(viewParent);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return viewRoots;
}
AFAIK - there is no public API for this.
Recommended way is to have a reference to the dialog, and check for isShowing() and call dismiss() if necessary, but since you're using a third party library, this may not be an options for you.
Your best bet is to check the documentation for the library you use. If that doesn't help, you're out of luck.
Hint: Activity switches to 'paused' state if a dialog pops up. You may be able to 'abuse' this behavior ;)
You can override activity method onWindowFocusChanged(boolean hasFocus) and track the state of your activity.
Normally, if some alert dialog is shown above your activity, the activity does not get onPause() and onResume() events. But it loses focus on alert dialog shown and gains it when it dismisses.
For anyone reading this and wondering how to detect a Dialog above fragment or activity, my problem was that inside my base fragment I wanted to detect if I'm displaying a Dialog on top of my fragment. The dialog itself was displayed from my activity and I didn't want to reach it there, so the solution I came up with (Thanks to all answers related to this kind of question) was to get the view (or you can get the view.rootView) of my fragment and check whether any of its children have the focus or not. If none of its children have no focus it means that there is something (hopefully a Dialog) being displayed above my fragment.
// Code inside my base fragment:
val dialogIsDisplayed = (view as ViewGroup).children.any { it.hasWindowFocus() }
Solution in kotlin
Inside Fragment
val hasWindowFocus = activity?.hasWindowFocus()
In Activity
val hasWindowFocus = hasWindowFocus()
If true, there is no Dialog in the foreground
if FALSE , there is a view/dialog in the foreground and has focus.
I am assuming, you are dealing with third party library and you don't have access to dialog object.
You can get the root view from the activity,
Then you can use tree traversal algorithm to see if you can reach any of the child view. You should not reach any of your child view if alert box is displayed.
When alert view is displayed ( check with Ui Automator ), the only element present in UI tree are from DialogBox / DialogActivity. You can use this trick to see if dialog is displayed on the screen. Though it sounds expensive, it could be optimized.
If you are using Kotlin just:
supportFragmentManager.fragments.any { it is DialogFragment }

How to remove focus without setting focus to another control?

I like my UIs to be intuitive; each screen should naturally and unobtrusively guide the user on to the next step in the app. Barring that, I strive to make things as confusing and confounding as possible.
Just kidding :-)
I've got three TableRows, each containing a read-only and non-focusable EditText control and then a button to its right. Each button starts the same activity but with a different argument. The user makes a selection there and the sub-activity finishes, populating the appropriate EditText with the user's selection.
It's the classic cascading values mechanism; each selection narrows the available options for the next selection, etc. Thus I'm disabling both controls on each of the next rows until the EditText on the current row contains a value.
I need to do one of two things, in this order of preference:
When a button is clicked, immediately remove focus without setting focus to a different button
Set focus to the first button when the activity starts
The problem manifests after the sub-activity returns; the button that was clicked retains focus.
Re: #1 above - There doesn't appear to be a removeFocus() method, or something similar
Re: #2 above - I can use requestFocus() to set focus to the button on the next row, and that works after the sub-activity returns, but for some reason it doesn't work in the parent activity's onCreate().
I need UI consistency in either direction--either no buttons have focus after the sub-activity finishes or each button receives focus depending on its place in the logic flow, including the very first (and only) active button prior to any selection.
Using clearFocus() didn't seem to be working for me either as you found (saw in comments to another answer), but what worked for me in the end was adding:
<LinearLayout
android:id="#+id/my_layout"
android:focusable="true"
android:focusableInTouchMode="true" ...>
to my very top level Layout View (a linear layout). To remove focus from all Buttons/EditTexts etc, you can then just do
LinearLayout myLayout = (LinearLayout) activity.findViewById(R.id.my_layout);
myLayout.requestFocus();
Requesting focus did nothing unless I set the view to be focusable.
Old question, but I came across it when I had a similar issue and thought I'd share what I ended up doing.
The view that gained focus was different each time so I used the very generic:
View current = getCurrentFocus();
if (current != null) current.clearFocus();
You can use View.clearFocus().
Use View.requestFocus() called from onResume().
android:descendantFocusability="beforeDescendants"
using the following in the activity with some layout options below seemed to work as desired.
getWindow().getDecorView().findViewById(android.R.id.content).clearFocus();
in connection with the following parameters on the root view.
<?xml
android:focusable="true"
android:focusableInTouchMode="true"
android:descendantFocusability="beforeDescendants" />
https://developer.android.com/reference/android/view/ViewGroup#attr_android:descendantFocusability
Answer thanks to:
https://forums.xamarin.com/discussion/1856/how-to-disable-auto-focus-on-edit-text
About windowSoftInputMode
There's yet another point of contention to be aware of. By default,
Android will automatically assign initial focus to the first EditText
or focusable control in your Activity. It naturally follows that the
InputMethod (typically the soft keyboard) will respond to the focus
event by showing itself. The windowSoftInputMode attribute in
AndroidManifest.xml, when set to stateAlwaysHidden, instructs the
keyboard to ignore this automatically-assigned initial focus.
<activity
android:name=".MyActivity"
android:windowSoftInputMode="stateAlwaysHidden"/>
great reference
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/ll_root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
LinearLayout llRootView = findViewBindId(R.id.ll_root_view);
llRootView.clearFocus();
I use this when already finished update profile info and remove all focus from EditText in my layout
====> Update: In parent layout content my EditText add line:
android:focusableInTouchMode="true"
What about just adding android:windowSoftInputMode="stateHidden" on your activity in the manifest.
Taken from a smart man commenting on this: https://stackoverflow.com/a/2059394/956975
I tried to disable and enable focusability for view and it worked for me (focus was reset):
focusedView.setFocusable(false);
focusedView.setFocusableInTouchMode(false);
focusedView.setFocusable(true);
focusedView.setFocusableInTouchMode(true);
First of all, it will 100% work........
Create onResume() method.
Inside this onResume() find the view which is focusing again and again by findViewById().
Inside this onResume() set requestFocus() to this view.
Inside this onResume() set clearFocus to this view.
Go in xml of same layout and find that top view which you want to be focused and set focusable true and focusableInTuch true.
Inside this onResume() find the above top view by findViewById
Inside this onResume() set requestFocus() to this view at the last.
And now enjoy......
android:focusableInTouchMode="true"
android:focusable="true"
android:clickable="true"
Add them to your ViewGroup that includes your EditTextView.
It works properly to my Constraint Layout. Hope this help
You could try turning off the main Activity's ability to save its state (thus making it forget what control had text and what had focus). You will need to have some other way of remembering what your EditText's have and repopulating them onResume(). Launch your sub-Activities with startActivityForResult() and create an onActivityResult() handler in your main Activity that will update the EditText's correctly. This way you can set the proper button you want focused onResume() at the same time you repopulate the EditText's by using a myButton.post(new Runnable(){ run() { myButton.requestFocus(); } });
The View.post() method is useful for setting focus initially because that runnable will be executed after the window is created and things settle down, allowing the focus mechanism to function properly by that time. Trying to set focus during onCreate/Start/Resume() usually has issues, I've found.
Please note this is pseudo-code and non-tested, but it's a possible direction you could try.
You do not need to clear focus, just add this code where you want to focus
time_statusTV.setFocusable(true);
time_statusTV.requestFocus();
InputMethodManager imm = (InputMethodManager)this.getSystemService(Service.INPUT_METHOD_SERVICE);
imm.showSoftInput( time_statusTV, 0);
Try the following (calling clearAllEditTextFocuses();)
private final boolean clearAllEditTextFocuses() {
View v = getCurrentFocus();
if(v instanceof EditText) {
final FocusedEditTextItems list = new FocusedEditTextItems();
list.addAndClearFocus((EditText) v);
//Focus von allen EditTexten entfernen
boolean repeat = true;
do {
v = getCurrentFocus();
if(v instanceof EditText) {
if(list.containsView(v))
repeat = false;
else list.addAndClearFocus((EditText) v);
} else repeat = false;
} while(repeat);
final boolean result = !(v instanceof EditText);
//Focus wieder setzen
list.reset();
return result;
} else return false;
}
private final static class FocusedEditTextItem {
private final boolean focusable;
private final boolean focusableInTouchMode;
#NonNull
private final EditText editText;
private FocusedEditTextItem(final #NonNull EditText v) {
editText = v;
focusable = v.isFocusable();
focusableInTouchMode = v.isFocusableInTouchMode();
}
private final void clearFocus() {
if(focusable)
editText.setFocusable(false);
if(focusableInTouchMode)
editText.setFocusableInTouchMode(false);
editText.clearFocus();
}
private final void reset() {
if(focusable)
editText.setFocusable(true);
if(focusableInTouchMode)
editText.setFocusableInTouchMode(true);
}
}
private final static class FocusedEditTextItems extends ArrayList<FocusedEditTextItem> {
private final void addAndClearFocus(final #NonNull EditText v) {
final FocusedEditTextItem item = new FocusedEditTextItem(v);
add(item);
item.clearFocus();
}
private final boolean containsView(final #NonNull View v) {
boolean result = false;
for(FocusedEditTextItem item: this) {
if(item.editText == v) {
result = true;
break;
}
}
return result;
}
private final void reset() {
for(FocusedEditTextItem item: this)
item.reset();
}
}

Toggling View background

I have a View object on my Activity and I'd like to change the background resource of the view. More specifically, I'd like to toggle it.
So I'll need some logic like this:
if (myView.getBackgroundResource() == R.drawable.first) {
myView.setBackgroundResource(R.drawable.second);
}
else {
myView.setBackgroundResource(R.drawable.first);
}
The issue here being that there is no getBackgroundResource() method.
How can I obtain the resource a View is using for its background?
I don't think the View remembers what resource it is using after it gets the Drawable from the resource.
Why not use an instance variable in your Activity, or subclass the View and add an instance variable to it?
Wouldn't it be easier to just have a control variable that maintains the state? Lets you be flexible and allows you any number of drawables.
int[] backgrounds = {
R.drawable.first,
R.drawable.second,
R.drawable.third
};
int currentBg;
void switch() {
currentBg++;
currentBg %= backgrounds.length;
myView.setBackgroundResource(backgrounds[currentBg]);
}
You could use a flag to keep track of which was last set
private static final int FIRST_BG = 0;
private static final int SECOND_BG = 1;
private int mCurrentBg;
...
if (mCurrentBg == FIRST_BG) {
myView.setBackgroundResource(R.drawable.second);
mCurrentBg = SECOND_BG;
}
else {
myView.setBackgroundResource(R.drawable.first);
mCurrentBg = FIRST_BG;
}
You would have to initialize mCurrentBg wherever the background is initially set though.
You can get the ID of a resource via the getResources().getIdentifier("filename", "drawable", "com.example.android.project"); function. As you can see you will need the filename, the type of resource (drawable, layout or whatever) and the package it is in.
EDIT: Updated my logic fail.
I think you might be able to put the setTag() and getTag() methods to use here:
//set the background and tag initially
View v = (View)findViewById(R.id.view);
v.setBackgroundResource(R.drawable.first);
v.setTag(R.drawable.first);
if(v.getTag().equals(R.drawable.first)) {
v.setBackgroundResource(R.drawable.second);
v.setTag(R.drawable.second);
} else {
v.setBackgroundResource(R.drawable.first);
v.setTag(R.drawable.first);
}
I have not tested this, but I think it should work, in theory. The downside is that you add a little overhead by having to manually tag it the first time, but after the initial tagging, you shouldn't have to worry about keeping track of flags.

Categories

Resources