【问题标题】:Filtering custom adapter returning incorrect results过滤返回错误结果的自定义适配器
【发布时间】:2012-12-22 11:25:26
【问题描述】:

检查:

我正在尝试在我的ListView 上实现过滤器。但我面临一个非常奇怪的问题。如果我在EditText 中键入字母T,则ListView 将填充以BJ 开头的名称。请帮忙。

public class MyCustomAdapter extends BaseAdapter implements Filterable {

Context mContext;
private LayoutInflater mInflater;
SparseBooleanArray mSparseBooleanArray;
private ArrayList<Map<String,String>> mAdapData = new ArrayList<Map<String, String>>();
private ArrayList<Map<String,String>> mOriginalData = new ArrayList<Map<String, String>>();

public MyCustomAdapter(Context mContext) {
    mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mSparseBooleanArray = new SparseBooleanArray();
}

public ArrayList<String> getCheckedItems() {
    ArrayList<String> mTempArry = new ArrayList<String>();
    for (int i = 0; i < mAdapData.size(); i++) {
        if (mSparseBooleanArray.get(i)) {
            Map<String, String> map = (Map<String, String>) mAdapData.get(i);
            final String numbr = map.get("Phone").toString();
            mTempArry.add(numbr);
        }
    }
    return mTempArry;
}

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

public void addItem(String paramString1, String paramString2) {
    Map<String, String> NameNumber = new HashMap<String, String>();
    NameNumber.put("Name", paramString1);
    NameNumber.put("Phone", paramString2);
    this.mAdapData.add(NameNumber);
    this.mOriginalData.add(NameNumber);
    notifyDataSetChanged();
}

@SuppressWarnings("unchecked")
public Object getItem(int paramInt) {
    return (ArrayList<Map<String, String>>) this.mAdapData.get(paramInt);
}

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

@Override
public View getView(final int paramInt, View paramView, ViewGroup paramViewGroup) {

    ViewHolder viewHolder;

    if (paramView == null) {
        viewHolder = new ViewHolder();
        paramView = mInflater.inflate(R.layout.multiplecontactview, null);
        viewHolder.tvName = (TextView) paramView.findViewById(R.id.txtContactName);
        viewHolder.tvNumber = (TextView) paramView.findViewById(R.id.txtContactNumber);
        viewHolder.cb = (CheckBox) paramView.findViewById(R.id.checkBox1);
        viewHolder.cb.setTag(paramInt);
        viewHolder.cb.setChecked(mSparseBooleanArray.get(paramInt));
        viewHolder.cb.setOnCheckedChangeListener(mCheckedChangeListener);
        viewHolder.tvName.setTextColor(Color.BLACK);
        viewHolder.tvNumber.setTextColor(Color.BLACK);
        for (int i = 0; i < mAdapData.size(); i++) {
            Map<String, String> map = (Map<String, String>) mAdapData.get(paramInt);
            final String name = map.get("Name").toString();
            Log.e("Name", name);
            final String numbr = map.get("Phone").toString();
            Log.e("Number", numbr);
            viewHolder.tvName.setText(name);
            viewHolder.tvNumber.setText(numbr);
        }
        paramView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) paramView.getTag();
    }       
    return paramView;
}

OnCheckedChangeListener mCheckedChangeListener = new OnCheckedChangeListener() {
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        mSparseBooleanArray.put((Integer) buttonView.getTag(), isChecked);
    }
};

public static class ViewHolder {
    TextView tvName;
    TextView tvNumber;
    CheckBox cb;
}

@Override
public Filter getFilter() {
    return new MyContactFilter();
}

@SuppressLint("DefaultLocale")
private class MyContactFilter extends Filter {

    @Override
    protected FilterResults performFiltering(CharSequence constraint) {
        FilterResults results = new FilterResults();
        ArrayList<Map<String, String>> mFilteredData = new ArrayList<Map<String, String>>();

        if (!TextUtils.isEmpty(constraint)) { 
            for(int i = 0; i < mAdapData.size(); i++) {
                Map<String, String> map = (Map<String, String>) mAdapData.get(i);
                final String names = map.get("Name").toString();
                final String numbr = map.get("Phone").toString();
                if(names.toLowerCase().contains(constraint.toString().toLowerCase())) {
                    Map<String, String> FilNameNumber = new HashMap<String, String>();
                    FilNameNumber.put("Name", names);
                    FilNameNumber.put("Phone", numbr);
                    mFilteredData.add(FilNameNumber);
                }
            }
            results.values = mFilteredData;
            results.count = mFilteredData.size();

        } else {
            synchronized (mOriginalData) {
                results.values = mOriginalData;
                results.count = mOriginalData.size();
            }
        }
        return results;
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void publishResults(CharSequence cs, FilterResults fr) {
        mAdapData = (ArrayList<Map<String, String>>) fr.values;
        notifyDataSetChanged();
    }
}
}

【问题讨论】:

    标签: android filter android-listview baseadapter


    【解决方案1】:

    您的问题来自您设置getView() 方法的方式以及您如何在该SparseBooleanArray 中设置选中项。我已经编辑了你的适配器的代码,它现在应该可以工作了。主要变化在OnCheckedChangeListener 字段:

    public class MyCustomAdapter extends BaseAdapter implements Filterable {
    
        private Context mContext;
        private LayoutInflater mInflater;
        private SparseBooleanArray mSparseBooleanArray;
        // from where do you get the data?
        private ArrayList<Map<String, String>> mAdapData = new ArrayList<Map<String, String>>();
        private ArrayList<Map<String, String>> mOriginalData = new ArrayList<Map<String, String>>();
    
        public MyCustomAdapter(Context mContext) {
            mInflater = (LayoutInflater) mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            mSparseBooleanArray = new SparseBooleanArray();
        }
    
        public ArrayList<String> getCheckedItems() {
            ArrayList<String> mTempArry = new ArrayList<String>();
            for (int i = 0; i < mAdapData.size(); i++) {
                if (mSparseBooleanArray.get(i)) {
                    Map<String, String> map = (Map<String, String>) mAdapData
                            .get(i);
                    final String numbr = map.get("Phone");
                    mTempArry.add(numbr);
                }
            }
            return mTempArry;
        }
    
        @Override
        public int getCount() {
            return this.mAdapData.size();
        }
    
        public void addItem(String paramString1, String paramString2) {
            Map<String, String> NameNumber = new HashMap<String, String>();
            NameNumber.put("Name", paramString1);
            NameNumber.put("Phone", paramString2);
            mAdapData.add(NameNumber);
            mOriginalData.add(NameNumber);
            notifyDataSetChanged();
        }
    
        @SuppressWarnings("unchecked")
        public Object getItem(int paramInt) {
            return (ArrayList<Map<String, String>>) this.mAdapData.get(paramInt);
        }
    
        @Override
        public long getItemId(int paramInt) {
            return paramInt;
        }
    
        @Override
        public View getView(final int paramInt, View paramView,
                ViewGroup paramViewGroup) {
            ViewHolder viewHolder;
            if (paramView == null) {
                viewHolder = new ViewHolder();
                paramView = mInflater.inflate(R.layout.adapters_specialfilterrow,
                        paramViewGroup, false);
                viewHolder.tvName = (TextView) paramView
                        .findViewById(R.id.textView1);
                viewHolder.tvNumber = (TextView) paramView
                        .findViewById(R.id.textView2);
                viewHolder.cb = (CheckBox) paramView.findViewById(R.id.checkBox1);
                paramView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) paramView.getTag();
            }
            viewHolder.cb.setTag(paramInt);
            viewHolder.cb.setOnCheckedChangeListener(null);
            viewHolder.cb.setChecked(mSparseBooleanArray.get(paramInt));
            viewHolder.cb.setOnCheckedChangeListener(mCheckedChangeListener);
            viewHolder.tvName.setTextColor(Color.BLACK);
            viewHolder.tvNumber.setTextColor(Color.BLACK);
            Map<String, String> map = (Map<String, String>) mAdapData.get(paramInt);
            final String name = map.get("Name");
            final String numbr = map.get("Phone");
            viewHolder.tvName.setText(name);
            viewHolder.tvNumber.setText(numbr);
            return paramView;
        }
    
        OnCheckedChangeListener mCheckedChangeListener = new OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView,
                    boolean isChecked) {
                // get the item from the current mAdapData
                Map<String, String> item = mAdapData.get((Integer) buttonView
                        .getTag());
                int position = -1;
                // see where is the item placed in the mOriginalData and use that
                // position
                for (int i = 0; i < mOriginalData.size(); i++) {
                    final Map<String, String> tmp = mOriginalData.get(i);
                    if (tmp.get("Name").toLowerCase().equals(item.get("Name").toLowerCase())) {
                        position = i;
                        break;
                    }
                }
                mSparseBooleanArray.put(position, isChecked);
            }
        };
    
        public static class ViewHolder {
            TextView tvName;
            TextView tvNumber;
            CheckBox cb;
        }
    
        @Override
        public Filter getFilter() {
            return new MyContactFilter();
        }
    
        @SuppressLint("DefaultLocale")
        private class MyContactFilter extends Filter {
    
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();
                ArrayList<Map<String, String>> mFilteredData = new ArrayList<Map<String, String>>();
    
                if (!TextUtils.isEmpty(constraint)) {
                    for (int i = 0; i < mAdapData.size(); i++) {
                        Map<String, String> map = (Map<String, String>) mAdapData
                                .get(i);
                        final String names = map.get("Name");
                        final String numbr = map.get("Phone");
                        if (names.toLowerCase().contains(
                                constraint.toString().toLowerCase())) {
                            Map<String, String> FilNameNumber = new HashMap<String, String>();
                            FilNameNumber.put("Name", names);
                            FilNameNumber.put("Phone", numbr);
                            mFilteredData.add(FilNameNumber);
                        }
                    }
                    results.values = mFilteredData;
                    results.count = mFilteredData.size();
                } else {
                    synchronized (mOriginalData) {
                        results.values = mOriginalData;
                        results.count = mOriginalData.size();
                    }
                }
                return results;
            }
    
            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence cs, FilterResults fr) {
                mAdapData = (ArrayList<Map<String, String>>) fr.values;
                notifyDataSetChanged();
            }
        }
    }
    

    【讨论】:

    • 说真的,我已经发布了相同的答案,这被选中了...... :(
    • @androiddeveloper 您的回答只是解决方案的一部分(是的,我将其包含在我的回答中(我承认,在对您的回答的评论中)),但 OnCheckedChangeListener 听众的变化是也很重要,最终解决了用户的所有问题。
    • @Luksprog 嗨,你能帮我解决这个问题吗:stackoverflow.com/questions/14020646/… 就我而言,我可以在 logcat 上看到过滤后的值。但是,UI 中显示了相同的更改
    【解决方案2】:

    我还在我的应用程序中实现了过滤器列表视图。为此,我只需要一个 sn-p 代码。检查一下,如果有帮助的话。

    editText.addTextChangedListener(new TextWatcher() {
    
                public void onTextChanged(CharSequence arg0, int arg1, int arg2,
                        int arg3) {
    
                }
                public void beforeTextChanged(CharSequence arg0, int arg1,
                        int arg2, int arg3) {
    
                }
                public void afterTextChanged(Editable arg0) {
                    sendToList.this.adapter.getFilter().filter(arg0);
    
                }
            });
    

    【讨论】:

    • 这适用于我的适配器吗?我正在使用ArrayList&lt;Map&lt;String, String&gt;&gt; 填充listview
    • 不确定,但你可以试试。我将它用于单个对象。
    【解决方案3】:

    getView() 方法中,您没有更新 UI 以获取过滤结果的值。 所以它返回了旧结果的视图。

    就在 return paramView 之前,将视图的数据更新为过滤结果中的数据,并将其从 viewHolder 的准备中删除,因为它只被调用了几次。


    编辑: 这是我对代码的更正。我认为它会解决它:

    @Override
    public View getView(final int paramInt, View paramView, ViewGroup paramViewGroup) {
    
    ViewHolder viewHolder;
    
    if (paramView == null) {
        viewHolder = new ViewHolder();
        paramView = mInflater.inflate(R.layout.multiplecontactview, null);
        viewHolder.tvName = (TextView) paramView.findViewById(R.id.txtContactName);
        viewHolder.tvNumber = (TextView) paramView.findViewById(R.id.txtContactNumber);
        viewHolder.cb = (CheckBox) paramView.findViewById(R.id.checkBox1);
        viewHolder.tvName.setTextColor(Color.BLACK);
        viewHolder.tvNumber.setTextColor(Color.BLACK);
        paramView.setTag(viewHolder);
        viewHolder.cb.setOnCheckedChangeListener(mCheckedChangeListener);
    } else {
        viewHolder = (ViewHolder) paramView.getTag();
    }       
    viewHolder.cb.setTag(paramInt);
    viewHolder.cb.setChecked(mSparseBooleanArray.get(paramInt));
    
    
    Map<String, String> map = (Map<String, String>) mAdapData.get(paramInt);
    final String name = map.get("Name").toString();
    Log.e("Name", name);
    final String numbr = map.get("Phone").toString();
    Log.e("Number", numbr);
    viewHolder.tvName.setText(name);
    viewHolder.tvNumber.setText(numbr);
    
    return paramView;
    }
    

    编辑: 这是我如何处理过滤的示例:

    @Override
    public Filter getFilter()
      {
      final Filter filter=new Filter()
        {
          @Override
          protected FilterResults performFiltering(final CharSequence constraint)
            {
            _lastFilterConstraint=constraint;
            if(TextUtils.isEmpty(constraint))
              return null;
            final String constraintToCheck=constraint.toString().toLowerCase(Locale.getDefault());
            final FilterResults results=new FilterResults();
            // <- here i do the filtering itself and later put the results into "results"
            results.values=values;
            results.count=values.size();
            return results;
            }
    
          @SuppressWarnings("unchecked")
          @Override
          protected void publishResults(final CharSequence constraint,final FilterResults results)
            {
            _filteredItems==null ? null : (ArrayList<Item>)results.values;
            notifyDataSetChanged();
            }
        };
      return filter;
      }
    
    @Override
    public int getCount()
      {
      if(_filteredItems!=null)
        return _filteredItems.size();
      return _originalItems.size();
      }
    
    @Override
    public Item getItem(final int position)
      {
      if(_filteredItems!=null)
        {
        if(position<_filteredItems.size())
          return _filteredItems.get(position);
        return null;
        }
      if(position<_originalItems.size())
        return _originalItems.get(position);
      return null;
      }
    
    @Override
    public View getView(final int position,final View convertView,final ViewGroup parent)
      {
      final View inflatedView;
      final ViewHolder viewHolder;
      if(convertView==null)
        {
        //<- here i inflate the view into the inflatedView
        }
      else
        {
        inflaterView=convertView;
        viewHolder=(ViewHolder)inflaterView.getTag();
        }
      // <- here i use getItem and update the view according to its data .
      return inflaterView;
      }
    

    【讨论】:

    • 但是当我搜索字母 T 时,为什么列表视图中充满了包含字母 B 的名称?
    • @user1822826 因为您只在convertViewnull 时才设置TextViews(最初)。过滤很可能会显示未将 TextViews 设置为新数据的先前行。完成过滤后,您应该查看 mAdapData 包含的内容。
    • convertView 是回收视图供您更新。你得到旧视图,但你没有更新它们。我建议您观看 google 的“listView 世界”的精彩讲座:google.com/events/io/2010/sessions/…。我认为这会解决问题。
    • 我还更新了我的答案以包含修复。希望它现在可以工作。
    • @user1822826 使用答案中的代码,看看过滤的行为。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多