List item won't stay selected despite setting necessary properties
Asked Answered
G

1

0

I'm trying to get the selected item within my list view to stay highlighted when I select it but for some reason it won't do that the moment the selected item is not in view. E.g. When I select Item 4, that list item does get highlighted but when I scroll through the list it doesn't stay highlighted and then other items randomly become highlighted. Even when I return to Item 4, that item is no longer highlighted. I really don't understand why this is happening. What can be done to resolve this issue? All relevant help would be appreciated.

fragment_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/fragmentmain">

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="false"
        android:layout_centerHorizontal="true"
        android:choiceMode="singleChoice"/>
</LinearLayout>

MainListAdapter.java

public class MainListAdapter extends BaseAdapter implements Filterable {

    private List<Main> mData;
    private List<Main> mFilteredData;
    private LayoutInflater mInflater;
    private ItemFilter mFilter;

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

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

    @Override
    public Main getItem(int position) {
        return mFilteredData.get(position);
    }

    @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, parent, false);
            holder = new ViewHolder();

            holder.title = (TextView) convertView.findViewById(R.id.item);
            holder.description = (TextView) convertView.findViewById(R.id.item_description);

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

        Main main = getItem(position);
        holder.title.setText(main.getContinent());
        holder.description.setText(main.getDescription());
        if (main.isSelected()) {
            convertView.setBackgroundColor(Color.parseColor("#1C3F96"));
            holder.title.setTextColor(Color.parseColor("#FFFFFF"));
            holder.description.setTextColor(Color.parseColor("#FFFFFF"));
        } else {
            convertView.setBackgroundColor(Color.TRANSPARENT);
            holder.title.setTextColor(Color.parseColor("#FFFFFF"));
            holder.description.setTextColor(Color.parseColor("#B5B5B5"));
        }

        holder.title.setText(mFilteredData.get(position).getContinent());
        holder.description.setText(mFilteredData.get(position).getDescription());

        return convertView;
    }

    @Override
    public Filter getFilter() {
        if (mFilter == null) {
            mFilter = new ItemFilter();
        }
        return mFilter;
    }

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

    /**
     * Filter for filtering list items
     */
    /**
     * <p>An array filter constrains the content of the array adapter with
     * a prefix. Each item that does not start with the supplied prefix
     * is removed from the list.</p>
     */
    private class ItemFilter extends Filter {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults results = new FilterResults();

            if (TextUtils.isEmpty(constraint)) {
                results.count = mData.size();
                results.values = new ArrayList(mData);
            } else {
                //Create a new list to filter on
                List<Main> resultList = new ArrayList<Main>();
                for (Main str : mData) {
                    if (str.getContinent().toLowerCase().contains(constraint.toString().toLowerCase())) {
                        resultList.add(str);
                    }
                }
                results.count = resultList.size();
                results.values = resultList;
            }
            return results;
        }

        /**
         * Runs on ui thread
         * @param constraint the constraint used for the result
         * @param results the results to display
         */
        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            if (results.count == 0) {
                mFilteredData.clear();
                notifyDataSetInvalidated();
            } else {
                mFilteredData = (ArrayList<Main>)results.values;
                notifyDataSetChanged();
            }
        }
    }
}

Main.java (in the "data" package)

public class Main {
    public Main(){}

    private String continent;
    private String description;
    private boolean selected;

    public String getContinent(){
        return continent;
    }

    public void setContinent(String continent){
        this.continent = continent;
    }

    public String getDescription(){
        return description;
    }

    public void setDescription(String description){
        this.description = description;
    }

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

MainActivity.java

public class MainActivity extends ActionBarActivity {

    private boolean mTwoPane;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        FragmentMain newFragment = new FragmentMain();
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.replace(R.id.master_container, newFragment);
        transaction.commit();

        if (findViewById(R.id.detail_container) != null) {
            mTwoPane = true;

        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        handleIntent(intent);
    }

    private void handleIntent(Intent intent) {

        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            String query = intent.getStringExtra(SearchManager.QUERY);
            //use the query to search your data somehow
        }
    }
}

FragmentMain.java

public class FragmentMain extends ListFragment implements SearchView.OnQueryTextListener {
    private MainListAdapter mAdapter;

    public FragmentMain() {
        // Required empty constructor
    }

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

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

        //Tell the system to call onCreateOptionsMenu
        setHasOptionsMenu(true);
        initialize(view);
        return view;
    }

    List<Main> list = new ArrayList<>();
    private void initialize(View view) {
        String[] items = getActivity().getResources().getStringArray(R.array.items);
        String[] itemDescriptions = getActivity().getResources().getStringArray(R.array.item_descriptions);
        for (int n = 0; n < items.length; n++){
            Main world = new Main();
            world.setID();
            world.setContinent(items[n]);
            world.setDescription(itemDescriptions[n]);
            list.add(world);
        }

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

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

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


        ListView lv = (ListView)v.findViewById(android.R.id.list);
        lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // get the adapter, then get the name from the adapter at that position
                MainListAdapter adapter = (MainListAdapter) parent.getAdapter();
                Main main = adapter.getItem(position);

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


                Fragment newFragment;
                switch (position) {
                    case 0:
                        newFragment = new Fragment0();
                        break;
                    case 1:
                        newFragment = new Fragment1();
                        break;
                    case 2:
                        newFragment = new Fragment2();
                        break;
                    case 3:
                        newFragment = new Fragment3();
                        break;
                    case 4:
                        newFragment = new Fragment4();
                        break;
                    case 5:
                        newFragment = new Fragment5();
                        break;
                    case 6:
                        newFragment = new Fragment6();
                        break;
                    case 7:
                        newFragment = new Fragment7();
                        break;
                    case 8:
                        newFragment = new Fragment8();
                        break;
                    case 9:
                        newFragment = new Fragment9();
                        break;
                    case 10:
                        newFragment = new Fragment10();
                        break;
                    case 11:
                        newFragment = new Fragment11();
                        break;
                    case 12:
                        newFragment = new Fragment12();
                        break;
                    case 13:
                        newFragment = new Fragment13();
                        break;
                    case 14:
                        newFragment = new Fragment14();
                        break;
                    case 15:
                        newFragment = new Fragment15();
                        break;
                    case 16:
                        newFragment = new Fragment16();
                        break;
                    case 17:
                        newFragment = new Fragment17();
                        break;
                    case 18:
                        newFragment = new Fragment18();
                        break;
                    case 19:
                        newFragment = new Fragment19();
                        break;
                    case 20:
                        newFragment = new Fragment20();
                        break;
                    default:
                        newFragment = new Fragment0();
                }
                MainActivity activity = (MainActivity) 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 (main.equals(view.getResources().getString(R.string.item0))) {
                        intent = new Intent(getActivity(), Activity0.class);
                    } else if (main.equals(view.getResources().getString(R.string.item1))) {
                        intent = new Intent(getActivity(), Activity1.class);
                    } else if (main.equals(view.getResources().getString(R.string.item2))) {
                        intent = new Intent(getActivity(), Activity2.class);
                    } else if (main.equals(view.getResources().getString(R.string.item3))) {
                        intent = new Intent(getActivity(), Activity3.class);
                    } else if (main.equals(view.getResources().getString(R.string.item4))) {
                        intent = new Intent(getActivity(), Activity4.class);
                    } else if (main.equals(view.getResources().getString(R.string.item5))) {
                        intent = new Intent(getActivity(), Activity5.class);
                    } else if (main.equals(view.getResources().getString(R.string.item6))) {
                        intent = new Intent(getActivity(), Activity6.class);
                    } else if (main.equals(view.getResources().getString(R.string.item7))) {
                        intent = new Intent(getActivity(), Activity7.class);
                    } else if (main.equals(view.getResources().getString(R.string.item8))) {
                        intent = new Intent(getActivity(), Activity8.class);
                    } else if (main.equals(view.getResources().getString(R.string.item9))) {
                        intent = new Intent(getActivity(), Activity9.class);
                    } else if (main.equals(view.getResources().getString(R.string.item10))) {
                        intent = new Intent(getActivity(), Activity10.class);
                    } else if (main.equals(view.getResources().getString(R.string.item11))) {
                        intent = new Intent(getActivity(), Activity11.class);
                    } else if (main.equals(view.getResources().getString(R.string.item12))) {
                        intent = new Intent(getActivity(), Activity12.class);
                    } else if (main.equals(view.getResources().getString(R.string.item13))) {
                        intent = new Intent(getActivity(), Activity13.class);
                    } else if (main.equals(view.getResources().getString(R.string.item14))) {
                        intent = new Intent(getActivity(), Activity14.class);
                    } else if (main.equals(view.getResources().getString(R.string.item15))) {
                        intent = new Intent(getActivity(), Activity15.class);
                    } else if (main.equals(view.getResources().getString(R.string.item16))) {
                        intent = new Intent(getActivity(), Activity16.class);
                    } else if (main.equals(view.getResources().getString(R.string.item17))) {
                        intent = new Intent(getActivity(), Activity17.class);
                    } else if (main.equals(view.getResources().getString(R.string.item18))) {
                        intent = new Intent(getActivity(), Activity18.class);
                    } else if (main.equals(view.getResources().getString(R.string.item19))) {
                        intent = new Intent(getActivity(), Activity19.class);
                    } else if (main.equals(view.getResources().getString(R.string.item20))) {
                        intent = new Intent(getActivity(), Activity20.class);
                    } else {
                        intent = new Intent(getActivity(), Activity0.class);
                    }
                    startActivity(intent);
                }
            }

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

                TextView tv0 = (TextView) rowView.findViewById(R.id.item);
                tv0.setTextColor(Color.parseColor("#FFFFFF"));

                TextView tv1 = (TextView) rowView.findViewById(R.id.item_description);
                tv1.setTextColor(Color.parseColor("#FFFFFF"));
            }

            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));
                    tv0.setTextColor(Color.WHITE);

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

        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        MenuItem searchViewMenuItem = menu.findItem(R.id.action_search);
        SearchView mSearchView = (SearchView) MenuItemCompat.getActionView(searchViewMenuItem);

        int searchImgId0 = android.support.v7.appcompat.R.id.search_button;
        ImageView v0 = (ImageView) mSearchView.findViewById(searchImgId0);
        v0.setImageResource(R.drawable.ic_action_search_light);

        int searchImgId1 = android.support.v7.appcompat.R.id.search_close_btn;
        ImageView v1 = (ImageView) mSearchView.findViewById(searchImgId1);
        v1.setImageResource(R.drawable.ic_action_content_clear_light);

        super.onPrepareOptionsMenu(menu);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        // Set up search view
        inflater.inflate(R.menu.menu_main, menu);
        MenuItem item = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
        searchView.setIconifiedByDefault(true);
        searchView.clearAnimation();
        searchView.setOnQueryTextListener(this);
    }

    @Override
    public boolean onQueryTextSubmit(String newText) {
        return false;
    }

    @Override
    public boolean onQueryTextChange(String newText) {
        mAdapter.getFilter().filter(newText);
        return false;
    }
}

list_item.xml

<RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    android:baselineAligned="false"
    android:paddingStart="12dp"
    android:paddingEnd="12dp"
    xmlns:android="http://schemas.android.com/apk/res/android">

        <TextView android:id="@+id/item"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal"
            android:textColor="?android:attr/textColorPrimary" />

        <TextView android:id="@+id/item_description"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/item"
            android:layout_alignStart="@id/item"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textColor="?android:attr/textColorSecondary" />
</RelativeLayout>

Updated screenshot 31/07/2015 enter image description here

Guan answered 15/7, 2015 at 22:10 Comment(0)
I
2

Android ListView reuses its views while you scroll it. You need to store information about a selected item in an item object and check if it is selected inside getView.

Call setItemSelected if it is selected and setItemNormal otherwise. This way you will update the selected state of each item while you scrolling the list.

EDIT

@Override
public Main getItem(int position) {
    return mFilteredData.get(position);
}

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

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

        holder.title = (TextView) convertView.findViewById(R.id.item);
        holder.description = (TextView) convertView.findViewById(R.id.item_description);

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

    Main main = getItem(position);
    holder.title.setText(main.getContinent());
    holder.description.setText(main.getDescription());
    if (main.isSelected()) {
        convertView.setBackgroundColor(Color.parseColor("#1C3F96"));
        holder.title.setTextColor(Color.parseColor("#FFFFFF"));
        holder.description.setTextColor(Color.parseColor("#FFFFFF"));
    } else {
        convertView.setBackgroundColor(Color.TRANSPARENT);
        holder.title.setTextColor(Color.parseColor("#FFFFFF"));
        holder.description.setTextColor(Color.parseColor("#B5B5B5"));
    }

    return convertView;
}

Inside your onItemClick:

...
switch (position) {
    case 0:
        newFragment = new Fragment0();
        break;
    case 1:
        newFragment = new Fragment1();
        break;
    // etc..
    default:
        newFragment = new Fragment0();
}
Integrant answered 15/7, 2015 at 22:18 Comment(21)
Take a look at Master/Detail Flow project in Android Studio, it does exactly what you need. AS -> File -> New Project -> Master/Detail FlowIntegrant
you need to call setActivatedPosition when you click on an item. if you don't call then your activated position is never updated. Highlight the item depending on mActivatedPosition valueIntegrant
I don't think this is a good practice to have so many fragments. Are they really different? Can you create one fragment instead of 20 and replace its content when you select an item in the list? This is how it should be implemented in most cases. Use different fragments only if they are completely different.Integrant
Anyway, if you still want to use different fragment for each item, you need to store related fragment tag or id in the item object and then use it when you click on the item.Integrant
take a look at this article, it has many examples: vogella.com/tutorials/AndroidListView/article.htmlIntegrant
example #14 shows how to store multiple selection. In your case it's even easier, you just need to store one selection. Take a look at this example and simplify it for your needsIntegrant
there is absolutely no difference. Instead of onCheckedChanged you have onItemClick. you just update the state when this event is fired like this: element.setSelected(!element.isSelected())Integrant
use list item view itselfIntegrant
what kind of adapter are you using?Integrant
no, this is not seen in your code. All I know is you use MainListAdapter. I have no idea whether it is ArrayAdapter or BaseAdapter. It doesn't even matter because your question is absolutely unclear.. You have an example of the list view with selection mode which is compatible with any type of adapter. Every adapter has a viewHolder and getView callback - this is all you need. Just read something about how ListView and adapter interact in Android and you will be able to solve the issue on your own.Integrant
I think you were talking about the class containing my getters and setters (which is now above). My question is not unclear + if it was then you wouldn't have been able to give an answer. You said yourself "Call setItemSelected if it is selected and setItemNormal otherwise.", which is what I HAVE done & it does highlight the selected item but obviously the problem relates to my list view reusing its view whilst I scroll. All that needs to be done here is to prevent this from happening. Please have a look at my code again and suggest some helpful code that can be used to solve this problem.Guan
Please add your adapter's getView implementation and your view holder. Thanks.Integrant
I don't think mine has those things. My app runs as normal with the code you see above hence I don't know what code is necessary to add in order to fix this.Guan
wait, you don't own MainListAdapter? Is it a part of some library?Integrant
Just double checked. MainListAdapter can be seen in FragmentMain.javaGuan
That adapter code is on the right but when I select an item, "Item 0" always appears. Please check my latest screenshot as well as MainListAdapter.java, MainActivity.java and FragmentMain.java to see if you can figure out where I've gone wrong and/or what I haven't done.Guan
you are comparing your main object with a String, which is always false: main.equals(view.getResources().getString(R.string.item0)). You have to fix all those 20 conditionsIntegrant
I still don't understand what to change them to when when I have clearly used else if statements.Guan
Updated my post. Please tryIntegrant
OK cool, that's been fixed. All that's left is the item highlight issue. Please have a look at Main.java (in the "data" package) and FragmentMain.java then you'll notice that the item highlighted related code has been used in both these classes. Do you know what I need to do to the one in ****? I was thinking of getting rid of it but I wanna double check with you first.Guan
Let us continue this discussion in chat.Integrant

© 2022 - 2024 — McMap. All rights reserved.