programmatically scroll PreferenceActivity - android

I have PreferenceActivity with plenty of PreferenceCategories defined in it. If I have the android:key of a given category.
Is it possible programmatically to scroll the Activity to this category?

I know this is an old answered question, but I found a better way than iterating through all the categories.
PreferenceCategory myCat = (PreferenceCategory) findPreference("myKey");
int position = myCat.getOrder();
getListView().setSelection(position);

You can iterate through the preferences in the activity like this:
PreferenceScreen screen = getPreferenceScreen();
int i;
for(i = 0; i < screen.getPreferenceCount(); i++) {
String key = screen.getPreference(i).getKey();
// be careful, because key will be null if no android:key is specified
// (as is often the case for PreferenceCategory elements)
if("myKey".equals(key))
break;
}
// PreferenceActivity extends ListActivity, so the ListView is accessible...
getListView().setSelection(i);
Tested out with Android SDK 14 and it works fine.
Caution though, calling getListView().setSelection(i) inside onCreate or onResume has no effect. It has to be called after the activity is drawn.
The getPreferenceCount() method counts all PreferenceCategories and their nested preferences. Not sure what it does for PreferenceScreens, although I'm sure a little experimentation there would be revealing.

Related

How do I save all items in a custom ListView and their states on Screen Rotation?

I have a ListView which is being filled by a BaseAdapter.
The items in this ListView are checkboxes and the last item is a TextView and a Button which allow the user to add new checkboxes to this list. (Four of these are pre-defined and come from an ArrayList, when the user clicks the button in the list he can add a new item into the list through a DialogBox this new item is also a CheckBox)
I noticed that when the orientation of the screen is changed, the new CheckBoxes and the states of the previous ones are lost.
I read about the Activity life cycle and found how this is happening. I also read about the onSaveInstanceState and onRestoreInstanceState methods can be used to save information about the activity in a Bundle and later use this to restore everything.
However, I couldn't think of a way to save all the information about the states & text of the old CheckBoxes & the new CheckBoxes and also how would I add the information of my last item which is a TextView and a Button.
Thanks.
Use a ListFragment instead of a ListView.
Tell the fragment to setRetainInstance and then reattach the fragment instead of creating a new one when you switch orientation.
Sam_D is incorrect when he says that you cannot use config changes if you use this method. getView is still called normally after which you can restore your listview back to the state you require (assuming you kept hold of your adapter in a private field within the fragment)
Try overriding the onRetainCustomNonConfigurationInstance in your activity. And in your onCreate() use getLastCustomNonConfigurationInstance() for getting the saved objects..
see below code for reference...
#Override
public Object onRetainCustomNonConfigurationInstance() {
//stop = true;
// if (pd != null) pd.dismiss();
List l = new ArrayList();
if(yourColl !=null)
{
l.add(rezColl);
}
l.add(true); // save boolean
return l;
}
and in oncreate()
List al = (ArrayList) getLastCustomNonConfigurationInstance();
if(al!=null){
Iterator itr=al.iterator();
while(itr.hasNext())
{
try{
Object o=itr.next();
if(o instanceof ArrayList)
{
yourColl=(ArrayList<HashMap<String, String>>)o;
}
if(o instanceof Boolean)
{
//do something
}
}catch(Exception e){
e.printStackTrace();
}
}
}
setRetainInstance can be useful, but I try to never use it for a Fragment (or a ListFragment) which will expose a View. What this does is essentially tell your Fragment to ignore configuration changes, such as orientation change (there are many others...), which means you can't take advantage of configuration qualifiers in your resource files for this Fragment (for example, having a different layout xml for landscape vs. portrait).
Much better would be to mutate the array that you originally used to populate the adapter whenever a user interacts with the UI (ie checking a checkbox you can represent by a boolean), creating something like a wrapper class for each item. Save that array during onSaveInstanceState, and then re-populate your adapter when the Fragment restarts. Of course this is a little annoying because your class/array will have to implement the Parcelable interface - but an easy way out of having to do that would be to just serialize it into a String using the Gson library, and likewise de-serializing it back to an array, also using Gson.

Android: Is there a way to show DialogPreference from code?

I want to open some preferences (which are made by extending DialogPreference) on first app startup. Also, these preferences are used as usual preferences.
Is there a way of accomplishing this?
EDIT:
I have my custom preference, made like this:
public class CitySelectPreference extends DialogPreference {
// Some code here
}
And as the solution I want it to be shown from the code, without the need of user getting to preference screen.
Just do this :
CitySelectPreference preference = (CitySelectPreference) findPreference("city_pref_key")
//You have to set a key to yout PreferenceScreen
PreferenceScreen screen = (PreferenceScreen) findPreference("screen_pref_key");
//Retrieve the index of the preference in preferenceScreen
int index = preference.getOrder();
//Perform a click
screen.onItemClick(null, null, index, 0);

How to get each menu item from a list view by position?

Let's say a list has 4 items, how can I get a view from each menu item of a list by position?
Unfortunately the items that are in the ListView are generally only those that are visible. You should iterate on the ListAdapter instead.
For example, in some of my code, I have this:
SimpleCursorAdapter adapter = (SimpleCursorAdapter) this.getListAdapter();
int iNum = adapter.getCount();
for(int i=0; i<iNum; i++)
{
Cursor c = (Cursor) adapter.getItem(i);
// Now you can pull data from the cursor object,
// if that's what you used to create the adapter to start with
}
EDIT:
In response to jeffamaphone's comments, here's something else... if you are trying to work with each UI element then getChildAt is certainly more appropriate as it returns the View for the sub-item, but in general you can still only work with those that are visible at the time. If that's all you care about, then fine - just make sure you check for null when the call returns.
If you are trying to implement something like I was - a "Select All / Select None / Invert Selection" type of feature for a list that might exceed the screen, then you are much better off to make the changes in the Adapter, or have an external array (if as in my case, there was nowhere in the adapter to make the chagne), and then call notifyDataSetChanged() on the List Adapter. For example, my "Invert" feature has code like this:
case R.id.selectInvertLedgerItems:
for(int i=0; i<ItemChecked.length; i++)
{
ItemChecked[i] = !ItemChecked[i];
}
la.notifyDataSetChanged();
RecalculateTotalSelected();
break;
Note that in my case, I am also using a custom ListView sub-item, using adapter.setViewBinder(this); and a custom setViewValue(...) function.
Furthermore if I recall correctly, I don't think that the "position" in the list is necessarily the same as the "position" in the adapter... it is again based more on the position in the list. Thus, even though you are wanting the "50th" item on the list, if it is the first visible, getChildAt(50) won't return what you are expecting. I think you can use ListView.getFirstVisiblePosition() to account and adjust.
See here, this question answers the similar problem you mentioned here
In an android ListView, how can I iterate/manipulte all the child views, not just the visible ones?

Dynamically create CheckBoxPreferences

I am currently building out a list of rows with checkboxes dynamically using content from a web service. However, this ListView will need to do pretty much what a PreferenceActivity would accomplish.
I don't know the number of rows as the content is dynamic so I can't create each CheckBoxPreference in XML. How do I go about building a PreferenceActivity that will display an unknown number rows with a CheckBoxPreference dynamically?
I think you're looking for something like this:
public class MyPreferenceActivity extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.my_preference_activity);
//fetch the item where you wish to insert the CheckBoxPreference, in this case a PreferenceCategory with key "targetCategory"
PreferenceCategory targetCategory = (PreferenceCategory)findPreference("targetCategory");
//create one check box for each setting you need
CheckBoxPreference checkBoxPreference = new CheckBoxPreference(this);
//make sure each key is unique
checkBoxPreference.setKey("keyName");
checkBoxPreference.setChecked(true);
targetCategory.addPreference(checkBoxPreference);
}
}
Well #Jodes, actually both of you are right, but the correct way of doing this would be using a ListPreference.
I would use a entire programmatic approach, from my experience it's easier to be consistent; either create an entire XML layout via code, or via XML, but mixing the 2 can be weird and you cannot alter everything set via XML...
onCreate(){
this.setPreferenceScreen(createPreferenceHierarchy());
}
public PreferenceScreen createPreferenceHierarchy(){
PreferenceScreen root = getPreferenceManager().createPreferenceScreen(this);
// category 1 created programmatically
PreferenceCategory cat1 = new PreferenceCategory(this);
cat1.setTitle("title");
root.addPreference(cat1);
ListPreference list1 = new ListPreference(this);
list1.setTitle(getResources().getString(R.string.some_string_title));
list1.setSummary(getResources().getString(R.string.some_string_text));
list1.setDialogTitle(getResources().getString(R.string.some_string_pick_title));
list1.setKey("your_key");
CharSequence[] entries = calendars.getCalenders(); //or anything else that returns the right data
list1.setEntries(entries);
int length = entries.length;
CharSequence[] values = new CharSequence[length];
for (int i=0; i<length; i++){
CharSequence val = ""+i+1+"";
values[i] = val;
}
list1.setEntryValues(values);
cat1.addPreference(list1);
return root;
}//end method
However, using this approach you will run into the platform's limitations of not having a multiple select ListPreference, and you'll probably want to implement something else.
I found this solution, which works great. You'll have to read the comments to find clues about how to debug the code though...
You need a ListView for that, a PreferenceActivity. As discussed in this link, PreferenceActivity should only be used for actually saving preferences.
Instead you could either create a simple dialog with single or multiple choice options:
http://developer.android.com/guide/topics/ui/dialogs.html
Or use a ListView as in the API examples Google provides, they give a simple example:
http://hi-android.info/docs/resources/samples/ApiDemos/src/com/example/android/apis/view/List10.html
Use PreferenceFragmentCompat from Preference Compat Library
compile 'com.android.support:preference-v7:23.4.0'
Check this article for the implementation details https://medium.com/#arasthel92/dynamically-creating-preferences-on-android-ecc56e4f0789#.71ssvjses

How to set scroll position for long PreferenceScreen

Android app has some very long preference screens, which always open at the top of the preference menu. I have some idea where the user wants to be in the preference menu. How can I force the preference screen to open scrolled to a specific preference item?
I know this is an old one, so this answer is just for reference.
To auto-select a given screen, all you have to do is setPreferenceScreen() (this is for a pre-Honeycomb non-Fragment PreferenceActivity).
Once you're on the correct PreferenceScreen, you can indeed use getListView().smoothScrollToPosition(position) (but this is a Froyo+ method), or you can use getListView.setSelection(position).
But how to get the position?
First, watch out for the trap: PreferenceActivity.getListAdapter() does not return the actual ListAdapter, but a local instance variable which is disconcertingly not in sync with PreferenceActivity.getListView().getAdapter() (and usually null).
Second, trying to use Preference.getOrder() returns the order of the Preference object within its parent, which is what you want to use for the position only if you're not using PreferenceCategories since what you need is its order within the PreferenceScreen.
If you are using PreferenceCategories, you need to iterate over the items in the adapter (for (int i = 0; i < adapter.getCount(); i++)until you find the right one, and use its position.
Another corner of the Android SDK that is in dire need of some attention…
You can just use scrollToPreference :
https://developer.android.com/reference/androidx/preference/PreferenceFragmentCompat#scrollToPreference(androidx.preference.Preference)
Example:
scrollToPreference(preferenceKey)
or:
scrollToPreference(preference)
Add this function to your PreferenceFragment
public void scrollToItem(String preferenceName) {
ListView listView = ButterKnife.findById(getView(),android.R.id.list);
Preference preference = findPreference(preferenceName);
if (preference != null && listView != null) {
for (int i = 0; i < listView.getAdapter().getCount(); i++) {
Preference iPref = (Preference) listView.getAdapter().getItem(i);
if (iPref == preference) {
listView.setSelection(i);
break;
}
}
}
}
Lets say you have settings.xml with this
<Preference
android:icon="#drawable/ic_action_email"
android:key="emailSupport"
android:title="#string/email_support" />
You can call
scrollToItem("emailSupport");
Note: You may need to replace listView.setSelection(i) with listView.smoothScrollToPosition(i)
Since PreferenceActivity extends ListActivity, you can call getListView() to get the ListView containing your preferences, and then use listView.smoothScrollToPosition() to scroll to a specific row in the list. I haven't actually tried this before, but it should work.

Categories

Resources