简体   繁体   中英

Why the original array items are also get deleted in an ArrayAdapter?

I add and remove values from adapter objects through adapter's add() and clear() and they are removing and adding elements to a list, which is not referenced from any place inside the adapter. I have orig list, which sits outside the adapter and has a reference from inside the adapter to the not changing outside reference and inside the adapter I only have reference to a new list, which I filled using the old original list with the same objects. How come that the new list affects the old list?

class LanguageItemArrayAdapter extends ArrayAdapter<com.anysoftkeyboard.ui.settings.LanguageItem>{
    private Context mContext;
    private final ArrayList<com.anysoftkeyboard.ui.settings.LanguageItem> origList = new ArrayList<com.anysoftkeyboard.ui.settings.LanguageItem>();
    private final ArrayList<com.anysoftkeyboard.ui.settings.LanguageItem> filteredList = new ArrayList<com.anysoftkeyboard.ui.settings.LanguageItem>();

    LanguageItemArrayAdapter(@NonNull Context context, @LayoutRes ArrayList<com.anysoftkeyboard.ui.settings.LanguageItem> list, ArrayList<com.anysoftkeyboard.ui.settings.LanguageItem> filteredList){
        super(context, 0, filteredList);
        mContext = context;
        origList.addAll(list); //THIS WORKS WELL. 
        //THIS BREAKS EVERYTHING -> 
        //origlist = list;
    }

    public void fillData(){
        filteredList.addAll(origList);
        notifyDataSetChanged();

    }

    @Override
    public View getView(int position,  View convertView, @NonNull ViewGroup parent) {
        View listItem = convertView;
        if (listItem == null)
            listItem = LayoutInflater.from(mContext).inflate(R.layout.languages_list_row, parent, false);
        com.anysoftkeyboard.ui.settings.LanguageItem currentItem = filteredList.get(position);

        TextView title = listItem.findViewById(R.id.title);
        title.setText(currentItem.getTitle());

        return listItem;
    }

    private Filter myFilter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults filterResults = new FilterResults();
            ArrayList<com.anysoftkeyboard.ui.settings.LanguageItem> tempList=new ArrayList<com.anysoftkeyboard.ui.settings.LanguageItem>();
            //constraint is the result from text you want to filter against.
            //objects is your data set you will filter from
            if(constraint != null) {
                int length= origList.size();
                int i=0;
                while(i<length){
                    LanguageItem item= origList.get(i);
                    if(item.toString().toUpperCase().contains(constraint.toString().toUpperCase())) {
                        tempList.add(item);
                    }
                    i++;
                }
                //following two lines is very important
                //as publish result can only take FilterResults objects
                filterResults.values = tempList;
                filterResults.count = tempList.size();
            }

            return filterResults;
        }

        //HERE ARE THE AFFECTING METHODS:
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            filteredList.clear();

            clear(); //******THIS METHOD REMOVES THE VALUES from origList as well******
            filteredList.addAll((Collection<? extends LanguageItem>) results.values);
            notifyDataSetChanged();

            //*****THIS ADDS VALUES TO ORIG LIST AS WELL. WHY?*****
            for(LanguageItem item: filteredList)
                add(item); 

        }
    };

    @Override
    public Filter getFilter() {
        return myFilter;
    }
}

Why does the original list get edited while I change only the filtered list?

EDIT:

ArrayAdapter<com.anysoftkeyboard.ui.settings.LanguageItem> adapter = new LanguageItemArrayAdapter(getContext(), simpleLanguageItems, simpleLanguageItems);
    ((LanguageItemArrayAdapter) adapter).fillData();;

(I know the fix, I don't understand the reason - fix is in code below if you're struggling the problem with Custom Adapter Filter like I did.)

This code passes the same instance of ArrayList to the LanguageItemArrayAdapter

new LanguageItemArrayAdapter(getContext(), simpleLanguageItems, simpleLanguageItems)

To understand why add(…) and clear() in ArrayAdapter also modifies the same object you need to look at their implementations from source .

public void clear() {
    synchronized (mLock) {
        if (mOriginalValues != null) {
            mOriginalValues.clear();
        } else {
            mObjects.clear();
        }
        …
    }
    …
}

clear() will modify mObjects and mOriginalValues (you will notice that other functions in ArrayAdapter do the same). You will need to read the code inside ArrayFilter to understand how they are modified.

private class ArrayFilter extends Filter {
  /* 
  mObjects will contain only items fulfilling the filter conditions. 
  Original items are copied into mOriginalValues 
  */
}

Looking at the constructor (and constructor chain) you will find that your class constructor has

super(context, 0, filteredList);

that will end up calling

private ArrayAdapter(@NonNull Context context, 
        @LayoutRes int resource,
        @IdRes int textViewResourceId, 
        @NonNull List<T> objects, 
        boolean objsFromResources) {
    …
    mObjects = objects;
    …
}

This allows ArrayAdapter to modify your simpleLanguageItems instance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM