Using Themes in Android - java

Im trying to switch between Holo.Light and Holo (Dark) so everything in my application is changed to the theme the user sets in the preferences.
I've been looking at a few open source apps that this is done in and cant seem to make it work with my project, Any help on this would be greately appreciated.
The Current ISSUE im running into inside DashboardActivity.java is:
"I've having issues with "setTheme(Integer.parseInt( pref.getString("
DashboardActivity (Updated)
public class DashboardActivity extends Activity {
public static final int THEME_BLACK = R.style.DarkThemeAndroid;
public static final int THEME_WHITE = R.style.LightThemeAndroid;
public static final int THEME_WHITE_BLACK = android.R.style.Theme_Holo_Light_DarkActionBar;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// I've having issue with the "pref.getString" i've tried "Settings.getString" and get error about needing a getString method is needed in Settings.java
setTheme(Integer.parseInt( pref.getString("selectedTheme", String.valueOf(R.style.LightThemeAndroid) )));
setContentView(R.layout.dashboard_layout);
// the rest of my code
)
)
Settings (Updated)
public class Settings extends PreferenceActivity implements
OnSharedPreferenceChangeListener {
final static String[] mThemeEntries = {
"Default (Light)",
"Dark"
};
final static String[] mThemeValues = {
String.valueOf(R.style.LightThemeAndroid),
String.valueOf(R.style.DarkThemeAndroid)
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Loads the XML preferences file.
addPreferencesFromResource(R.xml.settings);
// SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getActivity());
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
ListPreference listPref = (ListPreference)findPreference("selectedTheme");
listPref.setEntries(mThemeEntries);
listPref.setEntryValues(mThemeValues);
listPref.setValue( pref.getString("selectedTheme", String.valueOf(mThemeValues[0]) ) );
XML Resources
Themes.xml
<style name="LightThemeAndroid" parent="android:style/Theme.Holo.Light">
</style>
<style name="DarkThemeAndroid" parent="android:style/Theme.Holo">
</style>
Settings.xml
<ListPreference
android:title="Themes"
android:summary="Change the UI of the application"
android:key="theme"
android:entries="#array/themesReturnValue"
android:entryValues="#array/themesDisplayWord"
android:defaultValue="Theme1" />
Arrays.xml
<string-array name="themesReturnValue">
<item>Light</item>
<item>Dark</item>
<item>LightActionBar</item>
</string-array>
<string-array name="themesDisplayWord">
<item>Theme1</item>
<item>Theme2</item>
<item>Theme3</item>
</string-array>

getTheme() is a non-static method and you are trying to call it in a static way (not from an instance variable). You need an instance of the object to call the non-static method. Also, the docs state
Note that this should be called before any views are instantiated in the Context (for example before calling setContentView(View) or inflate(int, ViewGroup)).
Docs

There exists non-static getTheme() method of the ContextThemeWrapper class that returns Theme object type. Change the name of the getTheme() method to a different one for example getThe() and you should be fine.

First like codeMagic said, you are calling getTheme in a non-static way, I would also put the theme related constants in 1 class. Try making getTheme a static method (Although i would probably create a seperate class just for theme). Apart from that, here is a solution I was working on 2 days ago as i couldn't find anywhere on google or here for an answer to a more dynamic fluid theme preference.
I start off with my preference fragment (in your case, activity). Then declare the themes and values into an array...
public class LayoutFragment extends PreferenceFragment {
final static String[] mThemeEntries = {
"Default (Light)",
"Dark"
};
final static String[] mThemeValues = {
String.valueOf(R.style.Theme_Default),
String.valueOf(R.style.Theme_Dark)
};
then in my oncreate method, i set these values (note, in the xml i do not set these values and there are no references to those values in the xml files)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_layout);
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getActivity());
ListPreference listPref = (ListPreference)findPreference("selectedTheme");
listPref.setEntries(mThemeEntries);
listPref.setEntryValues(mThemeValues);
listPref.setValue( pref.getString("selectedTheme", String.valueOf(mThemeValues[0]) ) );
}
Notice that i had converted these values to a string, dont ask why, but i was getting a whole lot of trouble trying to store the int value of those resource id's (this is also why xml values was not an option)...
now when ever i need to set a theme from the user preference... I just call
setTheme(Integer.parseInt( pref.getString("selectedTheme", String.valueOf(R.style.Theme_Default) )));
and off course you could provide a static method somewhere to do the castings etc without writing it for each activity.
EDIT: You don't need the array's to be static, I must off left them like that while I was trying different things to get it to work.

Related

Accessing methods or classes from another java file/class from Activity

I am new to android studio but I am getting better at it as I program more and more. I have a MainActivity.java and the .xml file. And a friend provided me some code that it suppose to work with the input areas. The problem is I do not know how to access that regular java file. So that I can use it the way it is intended. He was using eclipse to build everything while I use android studio. I have the buttons all good to go and areas of input good to go but I just dont know how to implement his code. Any guidance will be greatly appreciated.
See examples to understand what I am trying to do.
"In android studio" a class is created called WaterDetails.java with a .xml file called activity_water_details.xml. There are calculations that were made for the duration that I need to be able to use or access from a java file created in eclipse called DurationCalculations.java. I have tried importing. I have tried opening the folder in explorer and putting the class in the same project. But, nothing seems to work.
Code:
public class WaterDetails extends AppCompatActivity {
Button continueWaterDetailsPart2;
EditText duration;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_water_details);
duration = (EditText)findViewById(R.id.enter_duration);
duration.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String user = duration.getText().toString();
if(duration.equals(" "))// if user inputs information
//Then get calculations from other java file.
}
});
Sample Code:
Second Java fie. The file I need to access.
package ScubanauiTables;
import java.util.Arrays;
public class DurationCalculations {
private int duration;
//Constructor
DurationCalculations(int duration, int maxDepth, int avgDepth, int temp, int visibility, int pressureStart,
int pressureEnd, String[] diveConditions, String[] diveActivities) {
setDuration(duration);
setMaxDepth(maxDepth);
setAvgDepth(avgDepth);
setTemp(temp);
setVisibility(visibility);
setPressureStart(pressureStart);
setPressureEnd(pressureEnd);
setAirType(21);
setDiveConditions(diveConditions);
setDiveActivities(diveActivities);
setPressureGroup();
public int getDuration() {
int temp = duration;
return temp;
}
private void setDuration(int duration) {
this.duration = duration;
}
I hope this sample code makes sense. Thank you all for your help in advance.
You want to use methods of your DurationCalculation class, and for that, you've to create an instance of that class.
You can instantiate and use your class like this
DurationCalculations durationCalculation = new DurationCalculations(
/*enter your constructor values*/);
Now you can call all public methods of your DurationCalculations class using durationCalculation variable like this
durationCalculation.getDuration();
You cannot call any private methods from outside of the class, like your setDuration() whose scope is set to private. For it be accessed outside of DurationCalculations class. You need to set it to public

Android, why I cant call my stringarray inside a method outside of onCreate?

Outside my onCreate method my stringarray isnt able to be called , why is that ?
I tried to initialize the stringarray again in the method outside onCreate with getResources(); but I cant call that either,
im guessing there is some fundemental knowledge im lacking as im fairly new in the programming world, can u help me or explain why the string array can't be called outside onCreate and/or how to work around it ?
example inside onCreate :
final String[] list = res.getStringArray(R.array.fact);
outside onCreate :
public void facts(){
getResources();
final String[] list = res.getStringArray(R.array.fact);
}// marked res. in red saying "qualifier must be an expression" and without that I cant call "list"..
I believe your issue is variable scope. You've declared your string array in the onCreate() method, giving it "method scope" and then you're tying to access it in another method. I would suggest declaring your string array as a class level variable so that you can access it from any method within that class.
To avoid the NullPointerException issue pointed out by Ben P. in a comment, you could initialize your array variable in the onResume() method so that you have it ready to go before you need to use it anywhere else.
Make sure to take a look at the reference links below the code example. I think they'll help explain "why" better than I can in a quick answer here.
public class MainActivity extends AppCompatActivity {
private static String[] list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
// onResume() runs after onCreate() and onStart() in the Android Activity lifecycle.
// If your array has not been initialized yet, do it now.
if (list == null || list.length == 0) {
list = getResources().getStringArray(R.array.fact);
}
}
public void facts() {
// Do something with your array...
for (String arrayItem : list) {
String fact = arrayItem.toUpperCase();
}
}
}
References:
The Activity Lifecycle: https://developer.android.com/guide/components/activities/activity-lifecycle
Variable scope: https://www.java-made-easy.com/variable-scope.html
Replace
final String[] list = res.getStringArray(R.array.fact);
by
final String[] list = getResources().getStringArray(R.array.fact);

Android: Updating textviews in multiple activities

I need some pointers on doing the following:
lets say i have 10/20 (number doesn't matter) of activities.
each of these activities has a textview that should work like a counter.
each of these activities has a button to go to the next activity.
this counter starts when the app is launched, and increment itself every second.
So what i did so far is:
have in my main activity a method that instantiate a class that extends Thread.
In that class in the run() method, i increment a variable when a second passes.
Now i'm stuck on what i should do next. Any pointers would be appreciated thanks.
Edit: i need a way to communicate from inside the run method, to whichever activity is now currently on screen, to update its textview.
Just a bit of theory here for standard Object Oriented Programming : stick to the recommended principles like Loose Coupling which makes your project code less tied to each other. You can read more on that later.
Now, using Events, you can setup a system that is synonymous with the natural Publisher/Subscriber design pattern. Like this:
The activity that needs to notify the other activities is called Publisher and the other activities that need to be notified are called Subscribers.
From here:
There are already built and tested libraries to do Events in android. Like my favorite EventBus.
Step 1 Add this line to your app-level build.gradle file:
compile 'org.greenrobot:eventbus:3.0.0'
Then create a simple Plain Old Java Object aka POJO class like this:
public class UpdateTextViewEvent{
private String textToShow;
public UpdateTextViewEvent(String text){
this.textToShow = text;
}
//add your public getters and setters here
}
Step 2 Notify others:
When you want to notify anyone of the changes, you simply called this method:
EventBus.getDefault().post(new UpdateTextViewEvent("Some new Text"));
Step 3 Receive notifications
For those who want to be notified of this event, simply do this:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
NOTE: to actually handle the event:
#Subscribe
public void onEvent(UpdateTextViewEvent event){
String text = event.getTextToShow();
//now you can show by setting accordingly on the TextView;
}
This is so much easier to do, do decouple your code by eliminating static references in your different activities
I hope this helps! Good luck!
make that Textview in second class as
public static Textview text;
and call it in main activity as
SecondActivity obj=new SecondActivity();
obj.text.settext("");
You can create one another activity e.g. BaseActivity extend with Activity class and your all 10/20 activity extends with created BaseActivity Class.
You can use your textview with protected access specifiers.
What you need to do is inside the counter class, create an a method and passed in a TextView as the parameter. Then create an int variable and set the counter as the instance:
Like this
public static class Counter extends Thread{
private static int x;
#Override
public void run(){
x = counter;
}
public void setCounter(TextView tv){
tv.setText(String.valueOf(x));
}
}
Now call this method setCounter(TextView) in all the activity's onCreate() method you'll like to display the counter, and passed in your the layout TextView as the argument. Like this
...
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState):
....
TextView cTextView = (TextView)findViewById(R.id.texT1);
Counter c = new Counter();
c.setCounter(cTextView);
}

Android: Referring to a string resource when defining a log name

In my Android app, I want to use a single variable for the log name in multiple files. At the moment, I'm specifying it separately in each file, e.g.
public final String LOG_NAME = "LogName";
Log.d(LOG_NAME, "Logged output);
I've tried this:
public final String LOG_NAME = (String) getText(R.string.app_name_nospaces);
And while this works in generally most of my files, Eclipse complains about one of them:
The method getText(int) is undefined
for the type DatabaseManager
I've made sure I'm definitely importing android.content.Context in that file. If I tell it exactly where to find getText:
Multiple markers at this line
- Cannot make a static reference to the non-static method getText(int)
from the type Context
- The method getText(int) is undefined for the type DatabaseManager
I'm sure I've committed a glaringly obvious n00b error, but I just can't see it! Thanks for all help: if any other code snippets would help, let me know.
That's because getText is a method of Context. It does not matter if you import the Context class; what matters is that you invoke that method from a Context (for instance, the Activity class is a Context (it inherits Context)).
In that case, what I'd recommend, is creating a Application class that returns the context you want. Here I explain how to do it. After that you can do something like:
public final String LOG_NAME = (String) App.getContext().getText(R.string.app_name_nospaces);
Depending on what sort of 'files' you are using, you can define a TAG that is used.
For example, when I create an app, I like to create a base class for my Activity classes...
Suppose my app is called 'Wibble', and my package is com.mydomain.Wibble...I create my base Activity like so...
package com.mydomain.Wibble
public class WibbleActivity extends Activity {
final protected String TAG = this.getClass().getName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// I'll explain how this next line works later
android.util.Log.d(TAG, "Entered onCreate()...");
}
}
Now suppose I derive an activity as follows...
package com.mydomain.Wibble
public class SomeActivity extends WibbleActivity {
#Override
protexted void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Don't Log "Entered onCreate()..." - WibbleActivity does it for me
android.util.Log.d(TAG, "SomeText");
}
}
Then I derive another Activity...
package com.mydomain.Wibble
public class SomeOtherActivity extends WibbleActivity {
#Override
protexted void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Don't Log "Entered onCreate()..." - WibbleActivity does it for me
android.util.Log.d(TAG, "SomeOtherText");
}
When onCreate() is called for SomeActivity, the output will be...
com.mydomain.Wibble.SomeActivity Entered onCreate()...
com.mydomain.Wibble.SomeActivity SomeText
...when onCreate() is called for SomeOtherActivity however, the output will be...
com.mydomain.Wibble.SomeOtherActivity Entered onCreate()...
com.mydomain.Wibble.SomeOtherActivity SomeOtherText
Neither activity needs to know specifics through an explicit string and the package name is prefixed. Obviously it will only work in certain situations but I find it useful.

NullPointerException trying to access a String resource

I have the following /res/values/uris.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="bladder">blahblah</string>
</resources>
I am accessing it in code:
private String bladderUrl= getString(R.string.bladder);
But it is returning null. I'm not sure why?
My guess you placed bladderUrl smth like this:
public class YourActivity extends Activity {
private String bladderUrl = getString(R.string.bladder);
#Override
public void onCreate(Bundle savedInstanceState) {
...
However you need to do smth like this:
public class YourActivity extends Activity {
private String bladderUrl;
#Override
public void onCreate(Bundle savedInstanceState) {
bladderUrl = getString(R.string.bladder);
...
First of all to clear that up. You don't have to have all your strings in strings.xml. Period.
Emmanuels answer is partially right. You have to get the string inside your onCreate() method when the context is initialized. His answer is only partially correct since he also mentioned that you have to have the strings inside the strings.xml which is not true.
First, make sure these values are in the file strings.xml.
Also, you can't access the strings from the constructor. The application context is not yet initialized. You should do it in onCreate().
Emmanuel

Categories