Проблема с переключением на пользовательский BaseAdapter

У меня есть фрагмент, который содержит список всех профилей для моих приложений. В этом списке используется пользовательский BaseAdapter. Вот характеристики этого адаптера:

  • Пользователь может включить только один профиль за раз.
  • Если пользователь попытается отключить все профили, он будет предупрежден, и последний включенный профиль будет снова включен.
  • Если пользователь попытается включить профиль, который не настроен, пользователь будет предупрежден, и последний включенный профиль будет снова включен.
  • Пользователь может настроить каждый профиль.

Все работает, за исключением одной проблемы с моей текущей реализацией, которая существует при первом создании списка:

  • Если пользователь включает (пользователь проверяет переключатель) первый профиль в списке при первом создании списка, а затем пытается включить другой профиль, первый все равно будет выбран (переключатель по-прежнему установлен). Также обратите внимание, что даже если первый профиль проверен, фактически включенным будет тот, который будет проверен позже.
  • Этого не происходит, если выбраны другие профили. Всякий раз, когда другие профили включены, все другие профили отключаются.
  • Но после того, как действие было приостановлено и возобновлено, все работает, как и ожидалось. Это означает, что проблема с проверкой первого переключателя исчезнет.

Вот графическое представление этой проблемы: введите здесь описание изображения

Вот код этого фрагмента, в котором размещается BaseAdapter (AdapterProfiles). PreferenceData — это класс, который обрабатывает все, что связано с общими настройками:

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);
        }
    }
}

}

У меня есть собственный BaseAdapter, и каждая его строка содержит TextView, ImageView и Switch, которые выглядят следующим образом: введите здесь описание изображения

Вот моя реализация BaseAdapter, которая создает строки в списке:

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;
}

}

наконец, вот класс CustomOnClickListener, если кому-то интересно, что это такое. Реализация этого класса заключается в сопоставлении обратного вызова с конкретным компонентом в списке. Этот класс был первоначально создан кем-то (я не нашел его в своей закладке, чтобы указать автора), я использовал его / ее образец, чтобы соответствовать моей собственной реализации. Вот код для этого:

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);
}

person Milan    schedule 26.02.2013    source источник


Ответы (1)


Теперь это кажется глупым здесь, реализующим мой собственный метод для отмены выбора всех других Switch-ов в адаптере, когда я знал, что метод notifyDataSetChanged() обновит изменения в представлении. Теперь я, кажется, решил эту проблему, удалив эти коды:

if (sw_profiles.isChecked() == true) {
                unselectProfileState(position);
            } 

private void unselectProfileState(int position) {
for (int i = 0; i < profileList.size(); i++) {
    if (position != i) {
        AdapterProfiles.getMapHolder().get(i).sw_profiles
                .setChecked(false);
    }
}

}

И добавив следующее в конце случая OnCustomClick R.id.sw_profiles. Как было сказано ранее, вот краткое описание этого метода из документации Android: "Уведомляет подключенные наблюдатели о том, что базовые данные были изменены, и любое представление, отражающее набор данных, должно обновиться" :

adapter.notifyDataSetChanged();
person Milan    schedule 26.02.2013