Using arbitrary XML files that can be read at runtime - android

This is the Activity where I'm using the setContentView method. In this app I'm using a xml folder in the res folder and put a prefs.xml file in that.
public class SetWallpaperActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(android.R.xml.);
//setContentView(R.xml.prefs.xml);
setContentView(android.R.xml.); // This line GENERATE ERROR...
}
This is my prefs.xml file:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="schemas.android.com/apk/res/android"; >
<CheckBoxPreference android:key="touch" android:title="Enable Touch">/CheckBoxPreference>
<EditTextPreference android:key="numberOfCircles" android:title="Number of Circles"></EditTextPreference>
</PreferenceScreen>

Judging by the xml file you posted as a comment(which has preferences in it) your probably looking to make a settings screen from which the user can set various preferences for your app. If this is the case then you need to extend the PreferenceActivity class instead of the normal Activity:
public class SetWallpaperActivity extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.the_name_of_the_xml_file); // R.xml.prefs.xml from your code
}
}
Also have a look at the official guide regarding this on the android developers site.
You can't set the content view as a xml file from the res/xml folder, because the setContentView needs the id of a layout file(in the form of R.layout.the_layout_file).

Related

How to use PreferenceFragmentCompat in the preference header

I'm trying to learn ways to build preference pages in the Xamarin Android application.
I found a lot of examples with PreferenceFragment but it was marked as deprecated and it is difficult for me to rewrite them on the current stage.
I've created activity to represent headers. I added IntentFilter so I can access this activity from apps list in the settings menu. Also it has internal class to group some preferences together:
namespace droid.examples.Preferences
{
[Activity(Label = "Settings activity", Theme = "#style/AppTheme", Name = "droid.examples.Preferences.SettingsActivity")]
[IntentFilter(new string[] { "android.intent.action.APPLICATION_PREFERENCES" })]
public class SettingsActivity : PreferenceActivity
{
public override void OnBuildHeaders(IList<Header> target)
{
base.OnBuildHeaders(target);
LoadHeadersFromResource(Resource.Xml.preference_headers, target);
}
public class SettingsFragment : PreferenceFragmentCompat
{
public override void OnCreatePreferences(Bundle savedInstanceState, string rootKey)
{
// Load the Preferences from the XML file
SetPreferencesFromResource(Resource.Xml.app_preferences, rootKey);
}
}
}
}
My app_preferences.xml which I can't open by selecting "Prefs 1" header from preference_headers.xml:
<?xml version="1.0" encoding="utf-8" ?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="Category">
<CheckBoxPreference
android:key="checkbox_preference"
android:title="Developer mode"
android:summary="Allow user to see detailed messages" />
</PreferenceCategory>
</PreferenceScreen>
I have preference_headers.xml. It opens when I click on gear wheel near application name. It looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header android:fragment="droid.examples.Preferences.SettingsActivity.SettingsFragment"
android:title="Prefs 1"
android:summary="An example of some preferences." />
</preference-headers>
My package name: droid.examples
I think that one problem related to the android:fragment attribute value.
What is the rules to build that value?
I suppose that it must start from 'package name'. Should it contain namespace between class name and package name?
What does $ mean in the attribute value? Is it used to mark internal class? I saw in the several places next code:
android:fragment="com.example.android.apis.preference.PreferenceWithHeaders$Prefs1Fragment"
I hope you can help me find where I made a mistakes.
Source code from GitHub
I spend a lot of time to investigate that issue and I want to make a summary.
We have to override IsValidFragment method in the SettingsActivity:
protected override bool IsValidFragment(string fragmentName)
{
return fragmentName == "droid.examples.preferences.SettingsActivity.SettingsFragment";
}
My SettingsActivity extends PreferenceActivity. Thanks to #Jeremy advice about implementation of IOnPreferenceStartFragmentCallback I find out that base class already extends it.
public abstract class PreferenceActivity ...
{
...
public virtual bool OnPreferenceStartFragment(PreferenceFragment caller, Preference pref);
...
}
So, I probably need to use PreferenceFragment instead of PreferenceFragmentCompat to make code consistent:
public class SettingsFragment : PreferenceFragment
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
AddPreferencesFromResource(Resource.Xml.app_preferences_for_header);
}
}
Also we have to add Register attribute to our fragment:
[Register("droid.examples.preferences.SettingsActivity.SettingsFragment")]
public class SettingsFragment : PreferenceFragment
{
}
Finally I updated preference_headers.xml
<?xml version="1.0" encoding="utf-8" ?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header android:fragment="droid.examples.preferences.SettingsActivity.SettingsFragment"
android:title="Prefs 1"
android:summary="An example of some preferences." />
</preference-headers>
android:fragment attribute value can contains '$' but '+' won't work because Register doesn't support it and we will get compilation error.
Thanks everyone who tried to help me
Looks like the string you provide is just a message to send to your parent activity. Your parent activity is responsible for instantiating the right Fragment, and performing the ceremony to render it.
The platform docs seem to indicate as such:
When a user taps a Preference with an associated Fragment, the interface method PreferenceFragmentCompat.OnPreferenceStartFragmentCallback.onPreferenceStartFragment() is called
At time of writing, there's a code snippet on that page, which I've translated for my own project more-or-less as follows:
// This has to go in the activity which hosts the PreferenceFragment
// which owns the Preference that has the `android:fragment` attribute.
using Android.Support.V7.Preferences;
using Android.Support.V4.App;
partial class MyActivity :
PreferenceFragmentCompat.IOnPreferenceStartFragmentCallback
{
Fragment GetFragmentForPrefString(string prefFragment)
{
// you implement this
}
const int fragmentContainerId = Resource.Id.whatever;
public bool OnPreferenceStartFragment(
PreferenceFragmentCompat caller, Preference pref)
{
string prefString = pref.Fragment;
var transaction = SupportFragmentManager.BeginTransaction();
transaction.Replace(fragmentContainerId,
GetFragmentForPrefString(prefString));
// you'll probably also want to add it to the back stack,
// but it's not strictly necessary I guess.
transaction.Commit();
return true;
}
}
Their sample involves the Java API method getSupportFragmentManager().getFragmentFactory() which doesn't appear to be part of the V28 Xamarin support NuGet packages. But honestly I'm not sure why that level of indirection is necessary; I'd suggest you simply implement something like
switch (prefFragmentName)
{
case "Fragment 1":
return new Fragment1();
// etc

InstantiationException: Unable to instantiate fragment make sure class name exists, is public, and has an empty constructor that is public

I have a project that I originally developed using Android Studio. I decided to convert it to Xamarin (Visual Studio 2015).
After hours of porting all the code over, everything works except for my Settings activity (PreferenceActivity). I have a few PreferenceFragments that make up the settings, but all of them give me "Unable to instantiate fragment". Here is the exception I am getting:
Java.Lang.RuntimeException: Unable to start activity ComponentInfo{test.mypackagename/md50d00e677e41fc49f8b3c16e79df2b77f.SettingsActivity}: android.app.Fragment$InstantiationException: Unable to instantiate fragment test.mypackagename.GeneralPreferenceFragment: make sure class name exists, is public, and has an empty constructor that is public
I have been looking online for a solution but I just cant seem to find one. Everywhere I look they say make sure there is an empty public constructor, if its an inner class it has to be static. But I have the empty constructor and its not an inner class, its in its own file.
Here is my SettingsActivity.cs:
namespace test.mypackagename
{
public class SettingsActivity : PreferenceActivity
{
protected override void OnPostCreate(Bundle savedInstanceState)
{
base.OnPostCreate(savedInstanceState);
}
public override void OnBuildHeaders(IList<Header> target)
{
LoadHeadersFromResource(Resource.Xml.pref_headers, target);
}
}
}
Here is my GeneralPreferenceFragment.cs:
namespace test.mypackagename
{
public class GeneralPreferenceFragment : PreferenceFragment
{
public GeneralPreferenceFragment() { }
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
AddPreferencesFromResource(Resource.Xml.pref_general);
}
}
}
And here is my pref_headers.xml:
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header android:fragment="test.mypackagename.GeneralPreferenceFragment"
android:title="#string/pref_header_general" />
<header android:fragment="test.mypackagename.OtherPreferenceFragment1"
android:title="#string/pref_header_other1" />
<header android:fragment="test.mypackagename.OtherPreferenceFragment2"
android:title="#string/pref_header_other2" />
<header android:fragment="test.mypackagename.OtherPreferenceFragment3"
android:title="#string/pref_header_other3" />
<header android:fragment="test.mypackagename.OtherPreferenceFragment4"
android:title="#string/pref_header_other4" />
</preference-headers>
This was working fine before so Im not sure what the issue could be. Any help would be much appreciated.
I think you are running into this problem because when not using the [Register] attribute on your PreferenceFragment then its name appended with a MD5 sum by Xamarin.
So in order to actually have the namespace you expect it to in the pref_headers.xml you need to attribute your class:
[Register("test.mypackagename.GeneralPreferenceFragment")]
public class GeneralPreferenceFragment: PreferenceFragment
{
// code here
}
EDIT:
I've just tested the code and it works fine on my machine. I am not using any support packages or anything.
pref_general.xml
<?xml version="1.0" encoding="utf-8" ?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="durr">
<CheckBoxPreference
android:key="checkbox_preference"
android:title="herp"
android:summary="derp" />
</PreferenceCategory>
</PreferenceScreen>
pref_headers.xml
<?xml version="1.0" encoding="utf-8" ?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header android:fragment="test.mypackagename.GeneralPreferenceFragment"
android:title="general" />
</preference-headers>
SettingsActivity.cs
[Activity(Label = "SettingsActivity")]
public class SettingsActivity : PreferenceActivity
{
public override void OnBuildHeaders(IList<Header> target)
{
LoadHeadersFromResource(Resource.Xml.pref_headers, target);
}
}
GeneralPreferenceFragment.cs
[Register("test.mypackagename.GeneralPreferenceFragment")]
public class GeneralPreferenceFragment : PreferenceFragment
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
AddPreferencesFromResource(Resource.Xml.pref_general);
}
}
This works fine and the app shows up the SettingsActivity with first a general option, after clicking on that it shows up a Title and CheckBox.
This worked even without providing any ctor to the GeneralPreferenceFragment. However, you could try add these:
public GeneralPreferenceFragment()
{
}
public GeneralPreferenceFragment(IntPtr javaRef, JniHandleOwnership transfer)
: base(javaRef, transfer)
{
}
The latter ctor is often needed when the app is coming back from background or when the Java world is invoking the class somehow.

How do you create Preference Activity and Preference Fragment on Android?

As I was following an old tutorial (Créez des applications pour Android -> openclassroom) I got stuck on this deprecated method addPreferencesFromResource(int id) from the PreferenceActivity class.
So my question is :
What is the new way of creating Preferences in Android ?
I found this post (What to use instead of “addPreferencesFromResource” in a PreferenceActivity?) that help me understand that you have to go through a PreferenceFragment in order to do it.
In the following explanation I use your.package. just to show that you have to put the package name. Everybody has its own package so please replace it with your package.
lets begin :
1. Preference Fragment
Create your PreferenceFragment class
MyPreferenceFragment
public class MyPreferenceFragment extends PreferenceFragment
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.fragment_preference);
}
}
Then the associated xml resource
fragment_preference.xml (in the folder res/xml of your project)
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="FOO">
<CheckBoxPreference
android:key="checkBoxPref"
android:title="check it out"
android:summary="click this little box"/>
</PreferenceCategory>
</PreferenceScreen>
That's all for the Fragment part.
2. Preference Activity
Create the PreferenceActivity class
MyPreferenceActivity
public class MyPreferenceActivity extends PreferenceActivity
{
#Override
public void onBuildHeaders(List<Header> target)
{
loadHeadersFromResource(R.xml.headers_preference, target);
}
#Override
protected boolean isValidFragment(String fragmentName)
{
return MyPreferenceFragment.class.getName().equals(fragmentName);
}
}
Do not forget to override isValidFragment(String fragmentName) method as you will get punched in the face by your application ! ;) More seriously I have no idea why you need to do this but it is needed. If someone has an explanation about this I'd gladly read it :)
EDIT :
Thanks to kirtan403 I now know why it is needed : it has to be set because of an (android framework fragment injection).
As you can see in the onBuildHeaders(List<Header> target) we load another xml file that contain the headers of the preference. In short, headers are the left part of the preference and the fragment are the right part (for tablet). For a phone you will first have the headers and when you click on an item the corresponding fragment will be put on top of the headers list.
Read this article (Multi-pane development in Android with Fragments - Tutorial) the images explain themselves.
Then the associated xml resource
headers_preference.xml (in the folder res/xml of your project)
<?xml version="1.0" encoding="utf-8"?>
<preference-headers
xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment="your.package.MyPreferenceFragment"
android:title="Goto: Preference fragment"
android:summary="An example of some preferences." />
</preference-headers>
As you may have noticed in the header section you have :
android:fragment="your.package.MyPreferenceFragment"
This will act as a Link to the fragment you want to show. On Tablet it will load on the right part and on the phone it will load on top of the current view.
3. Android Manifest
Now what you should do is to add your Activity to the AndroidManifest.xml file.
Inside the application section add these lines :
<activity
android:name="your.package.MyPreferenceActivity"
android:label="Preferences">
</activity>
You will probably tell me :
"Oh darling you forgot to put android:launchMode="singleTask" in your actvity"
But DO NOT PUT THIS as you will never load your fragment on phone. This error was solved by a great man ! This is the link to his blog (Android header preferences on small screen/phone).
4. Start the Preferences from Menu
Finally you need to add the ability to show this Preference !! To do so you will need 3 things :
The Menu
menu.xml (in the folder res/menu of your project)
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/preferences"
android:title="Preferences" />
</menu>
Loading this Menu in your Main activity (not the PreferenceActivity) under the method onCreateOptionsMenu(Menu menu)
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
Starting the MyPreferenceActivity Activity when you click on that button.
For that you will need to override the onOptionsItemSelected(MenuItem item) method in your Main activity.
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId())
{
case R.id.preferences:
{
Intent intent = new Intent();
intent.setClassName(this, "your.package.MyPreferenceActivity");
startActivity(intent);
return true;
}
}
return super.onOptionsItemSelected(item);
}
Et voila les amis !
I haven't tested this code. I took it and modified it from my own code so I may have not well copy pasted things. If you encounter errors tell me, I'll try to figure out the problem and fix this.
I hope this post will help some people out there :D
Cheers !
I liked the solution from this post: http://alvinalexander.com/android/android-tutorial-preferencescreen-preferenceactivity-preferencefragment
.. because it seems the most compact for someone that just needs something very basic up and running quickly. It has only one .java file and two small xml files.
Activity Config REMINDERS
After adding the 3 files to your project, Don't forget to
A) Add the Prefs Activity to Manifest file
B) Add some way to launch the Prefs Activity .. e.g., a Button or Menu item
Add the following files to your project. Use the order they are listed in to avoid compile errors.
Add /res/values/array.xml
<resources>
<string-array name="listArray">
<item>Ace</item>
<item>Club</item>
</string-array>
<string-array name="listValues">
<item>Ace</item>
<item>Club</item>
</string-array>
</resources>
Add /res/xml/preferences.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference android:title="Your Name"
android:key="username"
android:summary="Please provide your username"></EditTextPreference>
<CheckBoxPreference android:title="Application Updates"
android:defaultValue="false"
android:summary="This option if selected will allow the application to check for latest versions."
android:key="applicationUpdates" />
<ListPreference android:title="Download Details"
android:summary="Select the kind of data that you would like to download"
android:key="downloadType"
android:defaultValue="Ace"
android:entries="#array/listArray"
android:entryValues="#array/listValues" />
</PreferenceScreen>
Add the Activity code
public class AppPreferenceActivity extends PreferenceActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
checkValues();
}
public static class MyPreferenceFragment extends PreferenceFragment
{
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
private void checkValues()
{
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
String strUserName = sharedPrefs.getString("username", "NA");
boolean bAppUpdates = sharedPrefs.getBoolean("applicationUpdates",false);
String downloadType = sharedPrefs.getString("downloadType","1");
String msg = "Cur Values: ";
msg += "\n userName = " + strUserName;
msg += "\n bAppUpdates = " + bAppUpdates;
msg += "\n downloadType = " + downloadType;
Toaster.shortDebug(msg);
}
}

ACRA always disabled?

Here is the code I use to have ACRA but its not working,. in the LogCat I keep getting "ACRA is disabled":
Checkbox xml:
<CheckBoxPreference android:key="acra.enable"
android:title="#string/pref_disable_acra"
android:summaryOn="#string/pref_acra_enabled"
android:summaryOff="#string/pref_acra_disabled"
android:defaultValue="true"/>
Preference class:
public class Preferences extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.layout.preferences);
}
}
Here is a picture while enabling/disabling the checkbox:
Check You have added your Application in Manifest.xml file...
Only if you add it, it will be enabled....
Shanmugam

Get to default shared preferences without invoking PreferenceActivity

I have the following PreferenceActivity defined:
public class HiddenPreferences extends PreferenceActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.hidden_prefs);
}
}
where the hidden_prefs.xml looks something like this:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference android:key="testRestUrl"
android:title="REST service URI"
android:defaultValue="http://service/url">
</EditTextPreference>
</PreferenceScreen>
now I set the values to default and would like to read this preference in some other activity, like this:
PreferenceManager.setDefaultValues(this, R.xml.hidden_prefs, false);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
but when calling:
prefs.getString("testRestUrl", "");
I need to give the default value as second parameter, and the call always returns "" (as given in the call) but not the default value as given in the XML android:defaultValue attribute.
How should the preference be access in order to get the default value?
I don't believe you can do this if you don't instantiate the PreferenceActivity.
Your best bet is to define a String in strings.xml or config.xml and use R.strings.testRestUrl in your code:
prefs.getString("testRestUrl", getString(R.string.testRestUrl));
and XML:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference android:key="testRestUrl"
android:title="REST service URI"
android:defaultValue="R.string.testRestUrl">
</EditTextPreference>
</PreferenceScreen>

Categories

Resources