【问题标题】:Search for Keyword in ListView with Custom Adapter使用自定义适配器在 ListView 中搜索关键字
【发布时间】:2017-04-13 10:33:26
【问题描述】:

我有一个列表视图,上面有一个搜索 EditText。列表视图中的每一行都包含几个文本和图片。当用户在搜索 EditText 中输入关键字时,我希望使用包含该关键字的行来更新列表视图。我借用了this问题中的实现,如下所示。

我的自定义适配器:

public class CustomAdapter extends BaseAdapter implements Filterable  {

    private LayoutInflater inflater;
    private ArrayList<CustomObject> objects;//mOriginalValues
    private Activity activity;
    private ArrayList<Bitmap> newsImageList;
    private Context context;
    private ArrayList<CustomObject> filteredObjects;    // Values to be displayed after filtering


    private class ViewHolder {
        TextView titleTextView;
        TextView dateTextView;
        TextView bodyTextView;
        ImageView logoView;
        ImageView newsImageView;
        ProgressBar progressBar;
    }


    public CustomAdapter(Context context, ArrayList<CustomObject> objects, Activity activity) {
       // super(context, R.layout.news_item, objects);

        inflater = LayoutInflater.from(context);
        this.objects = objects;
        this.filteredObjects=objects;
        this.activity= activity;
        this.newsImageList= new ArrayList<Bitmap>();
        this.context= context;

        for(int i=0; i<objects.size(); i++)
            newsImageList.add(null);


    }

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

    @Override
    public CustomObject getItem(int position) {
        return filteredObjects.get(position);
    }

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

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

        System.out.println("IN GET VIEW");

        ViewHolder holder = null;
        if(convertView == null) {
            holder = new ViewHolder();
            convertView = inflater.inflate(R.layout.news_item, null);

            holder.titleTextView = (TextView) convertView.findViewById(R.id.txtTitle);
            holder.dateTextView = (TextView) convertView.findViewById(R.id.txtDate);
            holder.bodyTextView = (TextView) convertView.findViewById(R.id.txtBody);
            holder.logoView= (ImageView)  convertView.findViewById(R.id.source_imageView);
            holder.newsImageView= (ImageView) convertView.findViewById(R.id.news_imageView);
            holder.progressBar= (ProgressBar) convertView.findViewById(R.id.img_progress_bar);


            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.titleTextView.setText(objects.get(position).getTitle());
        holder.dateTextView.setText(objects.get(position).getDate());
        holder.bodyTextView.setText(objects.get(position).getBody());

        //for the source logo
        String uri = "@drawable/"+objects.get(position).getSource_logo()+"_logo";
        int imageResource = activity.getResources().getIdentifier(uri, null, activity.getPackageName());
        Drawable res =activity.getResources().getDrawable(imageResource);
        holder.logoView.setImageDrawable(res);



        //the news image
        String newsImageURL = objects.get(position).getNewsImageURL();

        if(newsImageURL!=null) {
            // show The Image in a ImageView

            //pre-execute
            holder.progressBar.setVisibility(View.VISIBLE);
            holder.newsImageView.setVisibility(View.VISIBLE);
            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.bodyTextView.getLayoutParams();
            params.addRule(RelativeLayout.BELOW, R.id.relative_container);

            Picasso.with(context).load(newsImageURL).into(holder.newsImageView);//EXECUTE in background

            holder.progressBar.setVisibility(View.INVISIBLE);//post-execute

        }
        else
        {
            System.out.println("NO IMAGE");
            //adjusting the view
            holder.progressBar.setVisibility(View.INVISIBLE);
            holder.newsImageView.setVisibility(View.GONE);
            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.bodyTextView.getLayoutParams();
            params.addRule(RelativeLayout.BELOW, R.id.txtDate);
        }


        return convertView;
    }



    @Override
    public Filter getFilter() {
        Filter filter = new Filter() {

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint,FilterResults results) {

                filteredObjects = (ArrayList<CustomObject>) results.values; // has the filtered values
                notifyDataSetChanged();  // notifies the data with new filtered values
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {

                System.out.println("FILTERING with the keyword:"+ constraint);

                FilterResults results = new FilterResults();        // Holds the results of a filtering operation in values
                ArrayList<Object> FilteredArrList = new ArrayList<Object>();

                if (objects == null) {
                    objects = new ArrayList<CustomObject>(filteredObjects); // saves the original data in mOriginalValues
                }

                /********
                 *
                 *  If constraint(CharSequence that is received) is null returns the mOriginalValues(Original) values
                 *  else does the Filtering and returns FilteredArrList(Filtered)
                 *
                 ********/
                if (constraint == null || constraint.length() == 0) {

                    // set the Original result to return
                    results.count = objects.size();
                    results.values = objects;
                } else {
                    constraint = constraint.toString().toLowerCase();
                    for (int i = 0; i < objects.size(); i++) {

                        System.out.println("loop "+i);

                        String title = objects.get(i).getTitle();
                        String date = objects.get(i).getDate();
                        String body = objects.get(i).getBody();
                        String source_logo = objects.get(i).getSource_logo();
                        String newsImageURL = objects.get(i).getNewsImageURL();

                        System.out.println("title of this item:"+title+", key word:"+constraint.toString());
                        if (title.contains(constraint.toString()) || date.contains(constraint.toString()) || body.contains(constraint.toString())) {
                            System.out.println("FOUND!");
                            FilteredArrList.add(new CustomObject(title,date, body, source_logo, newsImageURL));
                        }
                    }
                    // set the Filtered result to return
                    results.count = FilteredArrList.size();
                    results.values = FilteredArrList;
                }
                return results;
            }


        };
        return filter;
    }

}

class CustomObject {

    private String title;
    private String date;
    private String body;
    private String source_logo;
    private String newsImageURL;

    public CustomObject(String prop1, String prop2, String prop3, String prop4, String prop5) {
        this.title = prop1;
        this.date = prop2;
        this.body=prop3;
        this.source_logo=prop4;
        this.newsImageURL=prop5;
    }

    public String getTitle() {
        return title;
    }

    public String getDate() {
        return date;
    }

    public String getBody(){
        return body;
    }

    public String getSource_logo()
    {
        return source_logo;
    }

    public String getNewsImageURL(){ return newsImageURL; }

}

在我的活动中:

listView = (ListView) activity.findViewById(R.id.activity_main_listview);//news list view (title, date, body per item)
            searchEditText = (EditText) activity.findViewById(R.id.news_search_editText);

            // Add Text Change Listener to EditText
            searchEditText.addTextChangedListener(new TextWatcher() {

                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    // Call back the Adapter with current character to Filter
                    System.out.println("TEXT CHANGED!");
                    customAdapter.getFilter().filter(s.toString());
                    //listView.setAdapter(customAdapter);
                }

                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                }

                @Override
                public void afterTextChanged(Editable s) {

                    //customAdapter.getFilter().filter(s.toString());
                }
            });


            //preparing the news titles, dates, first part of bodies, and source logo.
            ArrayList<CustomObject> objects = new ArrayList<CustomObject>();
            for (int i = 0; i < titleList.size(); i++)
                objects.add(new CustomObject(titleList.get(i), dateList.get(i), reduceTextLetters(textList.get(i), 150) + "...", sourceList.get(i), imageURLList.get(i)));

            customAdapter = new CustomAdapter(activity, objects, activity);
            listView.setAdapter(customAdapter);

我的活动 XML 文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <RelativeLayout
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true">

        <EditText
            android:id="@+id/news_search_editText"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:hint="البحث"
            />

        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/activity_main_swipe_refresh_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/news_search_editText">

        <ListView
            android:id="@+id/activity_main_listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            >
        </ListView>


        </android.support.v4.widget.SwipeRefreshLayout>


    </RelativeLayout>

    <Button
        android:text="تحديث ↑"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/refresh_button"
        android:background="@drawable/button_refresh"
        android:layout_alignParentTop="true"
        android:layout_centerInParent="true"
        android:layout_gravity="center"
        android:layout_marginTop="50dp"
        />

</RelativeLayout>

很遗憾,过滤的行为不正确。虽然我可以在日志中看到关键字与每行内容的比较是正确的,并且只有匹配的关键字被添加到过滤列表中,但更新的列表是不正确的。当我输入列表中的关键字时,它会为我显示列表中的前几项(其中一些与关键字无关)并删除其余项。当我输入不在列表中的关键字时,它会清除列表视图。因此,该行为是奇怪和出乎意料的。有人可以提出解决方案吗?

【问题讨论】:

    标签: android listview custom-adapter android-filterable


    【解决方案1】:

    还有另一种方法——通过 Sql 的 LIKE 请求将所有可能的值保存到数据库搜索匹配值。我用它在很长的城市列表中实现搜索。 这是一个sn-p的代码(项目正在使用OrmLite):

     /**
         * Query all objects from database, which field contains given substring.
         * @param daoFetcher    Function, returning Dao of necessary type
         * @param targetColumnName Name of Table column, we're looking matches in for
         * @param matchText text within table column we're searching for.
         * @param <T>
         * @return
         */
        private <T>  List<T> getRecordsContainingText(Func0<Dao<T, Integer>> daoFetcher,
                                                      String targetColumnName,
                                                      String matchText) throws SQLException {
            // get dao of appropriate type by using user function
            Dao<T, Integer> dao = daoFetcher.call();
            QueryBuilder<T, Integer> queryBuilder = dao.queryBuilder();
            // query for 'LIKE'
            String likeQuery = "%" + matchText + "%";
            // form query
            queryBuilder.where().like(targetColumnName, likeQuery);
            PreparedQuery<T> preparedQuery = queryBuilder.prepare();
            List<T> result = dao.query(preparedQuery);
            return result;
        }
    

    【讨论】:

      【解决方案2】:

      我通过创建列表值的副本解决了我的问题。每当用户输入关键字时,此副本将被清除,然后仅使用具有关键字的行从原始列表中填充。最后,我用复制列表中的新值重置了我的适配器。像下面这样,

                  //another way (for keyword search)
              // Add Text Change Listener to EditText
              searchEditText.addTextChangedListener(new TextWatcher() {
      
                  @Override
                  public void onTextChanged(CharSequence s, int start, int before, int count) {
                      // Call back the Adapter with current character to Filter
                      System.out.println("TEXT CHANGED!");
                      //customAdapter.getFilter().filter(s.toString());
                      //listView.setAdapter(customAdapter);
      
      
                      //another way (for keyword search)
      
                      titleList2.clear();
                      dateList2.clear();
                      textList2.clear();
                      urlList2.clear();
                      sourceList2.clear();
                      timestampList2.clear();
                      imageURLList2.clear();
      
                      for(int i=0; i<textList.size(); i++)
                      {
                          if( s.toString().isEmpty() || textList.get(i).contains(s) || titleList.get(i).contains(s) || dateList.get(i).contains(s) || sourceList.get(i).contains(s))
                          {
      
                              titleList2.add(titleList.get(i));
                              dateList2.add(dateList.get(i));
                              textList2.add(textList.get(i));
                              urlList2.add(urlList.get(i));
                              sourceList2.add(sourceList.get(i));
                              timestampList2.add(timestampList.get(i));
                              imageURLList2.add(imageURLList.get(i));
      
                          }
      
                      }
      
      
                      //preparing the news titles, dates, first part of bodies, and source logo.
                      ArrayList<CustomObject> objects = new ArrayList<CustomObject>();
                      for (int i = 0; i < titleList2.size(); i++)
                          objects.add(new CustomObject(titleList2.get(i), dateList2.get(i), reduceTextLetters(textList2.get(i), 150) + "...", sourceList2.get(i), imageURLList2.get(i)));
      
                      customAdapter = new CustomAdapter(activity, objects, activity);
                      listView.setAdapter(customAdapter);
      
      
      
      
                  }
      

      这是一种简单的方法,我觉得它非常直观。

      【讨论】:

        猜你喜欢
        • 2013-12-28
        • 1970-01-01
        • 2016-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-20
        相关资源
        最近更新 更多