I have a fragment which holds a list of all profile for my applications. This list uses a custom BaseAdapter. Here is the specs of this adapter:
- User can enable only one profile at a time.
- If user tries to disable all profiles then user will be warned and last enabled profile will be set back to enabled.
- If user tried to enable profile that is not configured user will be warned and last enabled profile will be set back to enabled.
- User can configure each profile.
Everything is working find except, here is a single problem with my current implementation that exists the first time the list is created:
- If user enables (user checks Switch) the first profile in the list the first time the list is created and then tries to enable another profile, the first one still gets selected (Switch still checked). Also note that even if the first profile is checked, the actual enabled profile will be the one that is checked lateron.
- This doesn't happen if other profiles are selected. Whenever other profiles are enabled all other profile gets disabled.
- But, after the activity is paused and resumed back, everything works like expected. Meaning that the problem with first switch still getting checked will be gone.
Here is the graphic representation of this problem:
Here is the code for this fragment that hosts the BaseAdapter (AdapterProfiles). PreferenceData is a class that handles everything related to shared preferences.:
public class Profiles extends Fragment implements OnCustomClickListener {
private View view_profiles;
private ListView lv_profiles;
private PreferenceData dataPreferenceObj;
private AdapterProfiles adapter;
private ArrayList<String> profileList = new ArrayList<String>();
private final ArrayList<HashMap<String, String>> profilesList = new ArrayList<HashMap<String, String>>();
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// ...
lv_profiles = (ListView) view_profiles.findViewById(R.id.lv_profiles);
// ...
}
@Override
public void onResume() {
createProfileList();
// Registers broadcast receiver
super.onResume();
}
private void createProfileList() {
if (profilesList.isEmpty() == true) {
profileList = new ProfileList().getProfileList();
for (int i = 0; i < profileList.size(); i++) {
HashMap<String, String> map = new HashMap<String, String>();
map.put(Keys.KEY_PROFILE.toString(), profileList.get(i));
profilesList.add(map);
}
}
adapter = new AdapterProfiles(getActivity(), profilesList, this);
lv_profiles.setAdapter(adapter);
}
/*
* This is a custom onclick listener that gets handle from of list from
* LazyAdapterProfiles
*/
@Override
public void OnCustomClick(View view, int position) {
switch (view.getId()) {
case R.id.iv_profiles_settings:
// Opens setting for settings of profile clicked on
break;
case R.id.sw_profiles:
Switch sw_profiles = (Switch) view.findViewById(R.id.sw_profiles);
// Stops user from enabling profile that is not configured.
if (dataPreferenceObj.getTemporaryRingtoneUri().equals("") == true
|| dataPreferenceObj.getTemporaryLocation().equals("") == true) {
sw_profiles.setChecked(false);
showWarningDialog(
"Profile Disabled",
"Profile location and ringtone must be selected in order to enable this profile.");
} else if (dataPreferenceObj.getTemporaryRingtoneUri().equals("") != true
&& dataPreferenceObj.getTemporaryLocation().equals("") != true) {
if (sw_profiles.isChecked() == true) {
unselectProfileState(position);
} else if (sw_profiles.isChecked() == false) {
// Stops user from disabling profile as atleast one profile
// must be enabled
showWarningDialog("Profile Cannot Disable",
"Atleast one profile must be enabled");
sw_profiles.setChecked(true);
}
// Rest of the code that writes the preference and broadcast it
}
break;
}
}
// Gets switch map of all profiles and uncheck profile other than one that
// is selected.
private void unselectProfileState(int position) {
for (int i = 0; i < profileList.size(); i++) {
if (position != i) {
AdapterProfiles.getMapHolder().get(i).sw_profiles
.setChecked(false);
}
}
}
}
I have a custom BaseAdapter and each of its row holds TextView, ImageView and Switch which looks like following:
Here is my implementation of the BaseAdapter that creates rows in the list:
public class AdapterProfiles extends BaseAdapter {
private ArrayList<HashMap<String, String>> data;
private static LayoutInflater inflater = null;
private String profile_name;
private String retrived_profile_name;
private ViewHolder holder;
private Activity activity;
private OnCustomClickListener callback;
private static SparseArray<ViewHolder> mapHolder = new SparseArray<ViewHolder>();
public AdapterProfiles(Activity activity,
ArrayList<HashMap<String, String>> data,
OnCustomClickListener callback) {
this.activity = activity;
this.data = data;
this.callback = callback;
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public AdapterProfiles(Activity activity,
ArrayList<HashMap<String, String>> data) {
this.data = data;
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.ui_profiles_row, null);
holder = new ViewHolder();
holder.tv_profiles_name = (TextView) convertView
.findViewById(R.id.tv_profiles_name);
holder.tv_profiles_location_name = (TextView) convertView
.findViewById(R.id.tv_profiles_location_name);
holder.iv_profiles_settings = (ImageView) convertView
.findViewById(R.id.iv_profiles_settings);
holder.sw_profiles = (Switch) convertView
.findViewById(R.id.sw_profiles);
holder.iv_profiles_settings
.setOnClickListener(new CustomOnClickListener(callback,
position));
holder.sw_profiles.setOnClickListener(new CustomOnClickListener(
callback, position));
convertView.setTag(holder);
mapHolder.put(position, holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
getPrefs(position);
setUIData();
return convertView;
}
private void getPrefs(int position) {
HashMap<String, String> word = new HashMap<String, String>();
word = data.get(position);
profile_name = word.get(Keys.KEY_PROFILE.toString());
SharedPreferences settings = activity.getSharedPreferences(
Keys.PREFS_APP.toString(), 0);
// This allows us to retrive unique data based on the profile names
String PROFILE_LOCATION = Keys.KEY_LOCATION.toString() + "_"
+ profile_name;
retrived_profile_name = settings.getString(Keys.KEY_PROFILE.toString(),
"");
}
// Sets UI data of item in the list
private void setUIData() {
if (profile_name.equals(retrived_profile_name) == true) {
holder.sw_profiles.setChecked(true);
} else if (profile_name.equals(retrived_profile_name) == false) {
holder.sw_profiles.setChecked(false);
}
}
public static class ViewHolder {
public ImageView iv_profiles_settings;
public TextView tv_profiles_location_name;
public TextView tv_profiles_name;
public Switch sw_profiles;
}
public static SparseArray<ViewHolder> getMapHolder() {
return mapHolder;
}
}
finally here is the CustomOnClickListener class if anybody is wondering what it is. The implementation of this class is to map callback to specific component in the list. This class was originally created by someone (I didn't find it in my bookmark to credit the author), I used his/her sample to fit my own implementation. Here is the code for that:
public class CustomOnClickListener implements OnClickListener {
private int position;
private OnCustomClickListener callback;
public CustomOnClickListener(OnCustomClickListener callback, int position) {
this.position = position;
this.callback = callback;
}
@Override
public void onClick(View v) {
callback.OnCustomClick(v, position);
}
}
public interface OnCustomClickListener {
public void OnCustomClick(View view, int position);
}