使用自定义适配器在ListView中搜索关键字

时间:2017-04-13 10:33:26

标签: android listview custom-adapter android-filterable

我有一个列表视图,上面有一个搜索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>

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

2 个答案:

答案 0 :(得分:0)

还有另一种方法 - 通过Sql的LIKE请求将所有可能的值保存到数据库搜索匹配值。我用它在很长的城市列表中实现搜索。 这是一段代码(prject正在使用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;
    }

答案 1 :(得分:0)

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

            //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);




            }

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