Search code examples
javaandroidxmlandroid-5.0-lollipopandroid-selector

Selected list item background colour unexpectedly reused after list scroll on tablets


For my list view on tablets, I'm trying to get my selected list item selection to keep its state when selected but unfortunately I'm seeing some weird behaviour. For some reason whenever I scroll through the list to the point where the selected item is not visible and then scroll back to the point where the selected item IS visible, the background colour unexpectedly gets reused. I believe something needs to go in the getView method but I'm not sure what to do with this method. What must be done in order to prevent the background colour from being reused?

Adapter class

public class VictoriaListAdapter extends BaseAdapter {

    private List<Victoria> mData;
    private LayoutInflater mInflater;

    public VictoriaListAdapter (List<Victoria> data, Context context) {
        mData = data;
        mData = new ArrayList(mData);
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public String getItem(int position) {
        return mData.get(position).getStation();
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder holder;

        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.list_item_dualline, parent, false);
            holder = new ViewHolder();

            holder.title = (TextView) convertView.findViewById(R.id.item_station);
            holder.description = (TextView) convertView.findViewById(R.id.item_zone);

            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.title.setText(mData.get(position).getStation());
        holder.description.setText(mData.get(position).getZone());

        return convertView;
    }

    /**
     * View holder
     */
    static class ViewHolder {
        private TextView title;
        private TextView description;
    }
}

Fragment class

public class FragmentVictoriaLine extends ListFragment {

    private VictoriaListAdapter mAdapter;

    public FragmentVictoriaLine() {
    }

    /**
     * Whether or not the activity is in two-pane mode, i.e. running on a tablet
     * device.
     */
    public boolean mTwoPane;

    public static FragmentVictoriaLine newInstance() {
        return new FragmentVictoriaLine();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_victoria_line, container, false);
        initialize();
        return view;
    }

    List<Victoria> list = new ArrayList<>();
    private void initialize() {
        String[] items = getActivity().getResources().getStringArray(R.array.victoria_stations);
        String[] itemDescriptions = getActivity().getResources().getStringArray(R.array.victoria_zones);
        for (int n = 0; n < items.length; n++){
            Victoria victoria = new Victoria();
            victoria.setID();
            victoria.setStation(items[n]);
            victoria.setZone(itemDescriptions[n]);
            list.add(victoria);
        }

        mAdapter = new VictoriaListAdapter(list, getActivity());
        setListAdapter(mAdapter);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        View v = getView();

        mTwoPane = getActivity().findViewById(R.id.detail_container) != null;

        assert v != null;
        ListView lv = (ListView)v.findViewById(android.R.id.list);
        lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            private Victoria selectedMain;
            private View selectedView;

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                VictoriaListAdapter adapter = (VictoriaListAdapter) parent.getAdapter();
                String station = adapter.getItem(position);

                if (mTwoPane) {
                    setItemNormal();
                    View rowView = view;
                    setItemSelected(rowView);

                    Fragment newFragment;
                    if (station.equals(view.getResources().getString(R.string.bho))) {
                        newFragment = new FragmentVictoriaBHO();
                    } else if (station.equals(view.getResources().getString(R.string.brx))) {
                        newFragment = new FragmentVictoriaBRX();
                    } else if (station.equals(view.getResources().getString(R.string.eus))) {
                        newFragment = new FragmentVictoriaEUS();
                    } else if (station.equals(view.getResources().getString(R.string.fpk))) {
                        newFragment = new FragmentVictoriaFPK();
                    } else if (station.equals(view.getResources().getString(R.string.green_park))) {
                        newFragment = new FragmentVictoriaGreenPark();
                    } else if (station.equals(view.getResources().getString(R.string.hhy))) {
                        newFragment = new FragmentVictoriaHHY();
                    } else if (station.equals(view.getResources().getString(R.string.kxsp))) {
                        newFragment = new FragmentVictoriaKXSP();
                    } else {
                        newFragment = new FragmentVictoriaBHO();
                    }
                    VictoriaLineActivity activity = (VictoriaLineActivity) view.getContext();
                    FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
                    transaction.setCustomAnimations(R.anim.fade_out, R.anim.fade_in);
                    transaction.replace(R.id.detail_container, newFragment);
                    transaction.commit();
                } else {
                    Intent intent;
                    if (station.equals(view.getResources().getString(R.string.bho))) {
                        intent = new Intent(getActivity(), VictoriaBHOActivity.class);
                    } else if (station.equals(view.getResources().getString(R.string.brx))) {
                        intent = new Intent(getActivity(), VictoriaBRXActivity.class);
                    } else if (station.equals(view.getResources().getString(R.string.eus))) {
                        intent = new Intent(getActivity(), VictoriaEUSActivity.class);
                    } else if (station.equals(view.getResources().getString(R.string.fpk))) {
                        intent = new Intent(getActivity(), VictoriaFPKActivity.class);
                    } else if (station.equals(view.getResources().getString(R.string.green_park))) {
                        intent = new Intent(getActivity(), VictoriaGreenParkActivity.class);
                    } else if (station.equals(view.getResources().getString(R.string.hhy))) {
                        intent = new Intent(getActivity(), VictoriaHHYActivity.class);
                    } else if (station.equals(view.getResources().getString(R.string.kxsp))) {
                        intent = new Intent(getActivity(), VictoriaKXSPActivity.class);
                    } else {
                        intent = new Intent(getActivity(), VictoriaBHOActivity.class);
                    }
                    startActivity(intent);
                }
            }

            public void setItemSelected(View view) {
                View rowView = view;
                view.setBackgroundColor(Color.parseColor("#868F98"));

                TextView tv0 = (TextView) rowView.findViewById(R.id.item_station);
                tv0.setTextColor(Color.WHITE);

                TextView tv1 = (TextView) rowView.findViewById(R.id.item_zone);
                tv1.setTextColor(Color.WHITE);
            }

            public void setItemNormal() {
                for (int i = 0; i < getListView().getChildCount(); i++) {
                    View v = getListView().getChildAt(i);
                    v.setBackgroundColor(Color.TRANSPARENT);

                    TextView tv0 = ((TextView) v.findViewById(R.id.item_station));
                    tv0.setTextColor(Color.WHITE);

                    TextView tv1 = ((TextView) v.findViewById(R.id.item_zone));
                    tv1.setTextColor(Color.parseColor("#B5B5B5"));
                }
            }
        });

        super.onActivityCreated(savedInstanceState);
    }
}

data class

public class Victoria {
    public Victoria(){}

    private String station;
    private String zone;
    private boolean selected;

    public String getStation(){
        return station;
    }

    public void setStation(String item){
        this.station = item;
    }

    public String getZone(){
        return zone;
    }

    public void setZone(String zone){
        this.zone = zone;
    }

    private int _id;
    public void getID(int _id){
        this._id = _id;
    }

    public int setID(){
        return _id;
    }

    public boolean isSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }
}

Solution

  • When you scroll through the list, the items/views in the ListView will get reused to optimize the memory.So when you set a selected state to a list item, you will see that selected state in multiple list items as you scroll through. The best way to prevent this is to preserve the state in your Data Model and set the state in getView function of the Adapter.

    Here is what you can do -

     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position,long id) {
       VictoriaListAdapter adapter = (VictoriaListAdapter) parent.getAdapter();
    
       //reverse the selected state in data model
       for (int i = 0; i < adapter.getCount(); i++) {
         Victoria victoria = (Victoria)adapter.getItem(i);
         victoria.setSelected(i == position ? true : false);
       }
       Victoria victoria = (Victoria)adapter.getItem(position);
    
       ---
       ---
    

    And in adapter -

    @Override
    public Object getItem(int position) {
        //Return full object, coz we need to access other 
        //member variables too
        return mData.get(position);
    }
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    
        ViewHolder holder;
    
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.list_item_dualline, parent, false);
            holder = new ViewHolder();
    
            holder.title = (TextView) convertView.findViewById(R.id.item_station);
            holder.description = (TextView) convertView.findViewById(R.id.item_zone);
    
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
    
        Victoria victoria = (Victoria)getItem(position);
        holder.title.setText(victoria.getStation());
        holder.description.setText(victoria.getZone());
        if (victoria.isSelected()) {
            setItemSelected(convertView);
        } else {
            setItemNormal(convertView);
        }
    
        return convertView;
    }
    
     public void setItemSelected(View view) {
        View rowView = view;
        view.setBackgroundColor(Color.parseColor("#868F98"));
    
        TextView tv0 = (TextView) rowView.findViewById(R.id.item_station);
        tv0.setTextColor(Color.WHITE);
    
        TextView tv1 = (TextView) rowView.findViewById(R.id.item_zone);
        tv1.setTextColor(Color.WHITE);
     }
    
    public void setItemNormal(View v) {
    
        v.setBackgroundColor(Color.TRANSPARENT);
    
        TextView tv0 = ((TextView) v.findViewById(R.id.item_station));
        tv0.setTextColor(Color.WHITE);
    
        TextView tv1 = ((TextView) v.findViewById(R.id.item_zone));
        tv1.setTextColor(Color.parseColor("#B5B5B5"));
    
     }
    

    Hope it helps!