Страница 1 из 1

Автодополнение SearchView с разделителем.

Добавлено: 31 янв 2015, 23:38
CullyCross
[SOLVED] Будьте любезны, подскажите, как можно сделать элемент SearchView(android.support.v7.widget) в ActionBar(android.support.v7.app) сродне MultiAutoCompleteTextView, чтоб после ввода слова(не всего выражения) отображалось дополнение конкретно для него. Я не расширял MultiAutoCompleteTextView, потому что не хотел терять стандартное решение для иконки поиска и кнопки стирания текста. Я задал этот же вопрос тут: http://stackoverflow.com/questions/2825 ... -delimiter.

К третьему посту прикрепил код решения.

Re: Автодополнение SearchView с разделителем.

Добавлено: 01 фев 2015, 04:13
CullyCross
Обновил код на stackoverflow. Копирую сюда:

Расширение класса SearchView:

Код: Выделить всё

public class MultiAutoCompleteSearchView extends android.support.v7.widget.SearchView {

    private MultiAutoCompleteSearchView.SearchAutoComplete mSearchAutoComplete;

    public static class SearchAutoComplete extends android.support.v7.widget.SearchView.SearchAutoComplete {

        private String mSeparator = "+";

        public SearchAutoComplete(Context context) {
            super(context);
        }
        public SearchAutoComplete(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
        public SearchAutoComplete(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

        @Override
        protected void replaceText(CharSequence text) {
            String newText = getText().toString();
            if (newText.contains(mSeparator)) {
                int lastIndex = newText.lastIndexOf(mSeparator);
                newText = newText.substring(0, lastIndex + 1) + text.toString();
            } else {
                newText = text.toString();
            }
            super.replaceText(newText);
        }

        @Override
        protected void performFiltering(CharSequence text, int keyCode) {
            String newText = text.toString();
            if (newText.indexOf(mSeparator) != -1) {
                int lastIndex = newText.lastIndexOf(mSeparator);
                if (lastIndex != newText.length() - 1) {
                    newText = newText.substring(lastIndex + 1).trim();
                    if (newText.length() >= getThreshold()) {
                        text = newText;
                    }
                }
            }
            super.performFiltering(text, keyCode);
        }
    }

    public void initialize() {
        mSearchAutoComplete = (MultiAutoCompleteSearchView.SearchAutoComplete)
                findViewById(android.support.v7.appcompat.R.id.search_src_text);
        this.setAdapter(null);
        this.setOnItemClickListener(null);
    }

    public MultiAutoCompleteSearchView(Context context) {
        super(context);
        initialize();
    }

    public MultiAutoCompleteSearchView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public MultiAutoCompleteSearchView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize();
    }

    public void setOnItemClickListener(AdapterView.OnItemClickListener listener) {
        mSearchAutoComplete.setOnItemClickListener(listener);
    }

    public void setAdapter(ArrayAdapter<?> adapter) {
        mSearchAutoComplete.setAdapter(adapter);
    }
}
xml файл с меню:

Код: Выделить всё

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      tools:context=".SearchActivity">
    <item android:id="@+id/action_search"
          android:title="@string/action_search"
          android:icon="@drawable/ic_action_search"
          app:showAsAction="ifRoom|collapseActionView"
          app:actionViewClass="cullycross.com.searchview.MultiAutoCompleteSearchView" />
</menu>
Метод onCreateOptionsMenu

Код: Выделить всё

public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_search, menu);

    final MenuItem searchItem = menu.findItem(R.id.action_search);
    final MultiAutoCompleteSearchView searchView = (MultiAutoCompleteSearchView)
            MenuItemCompat.getActionView(searchItem);
    searchView.setQueryHint("Type any word");
    MultiAutoCompleteSearchView.SearchAutoComplete searchAutoComplete =
            (MultiAutoCompleteSearchView.SearchAutoComplete)searchView
                    .findViewById(R.id.search_src_text);
    searchAutoComplete.setAdapter(new ArrayAdapter<String>(
            this,
            android.R.layout.simple_dropdown_item_1line,
            options
    ));
    return true;
}
getActionView возвращает null.

Re: Автодополнение SearchView с разделителем.

Добавлено: 01 фев 2015, 07:34
CullyCross
Часов 6-7 позади - я написал решение. Я долго гуглил, прежде изобретать велосипед, надеюсь, что решения действительно нет в интернете, иначе обидно :) . Если кому будет интересно:

Кастомный адаптер, который фильтрует значения, сохраняет старую строку:

Код: Выделить всё

package cullycross.com.searchview;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by cullycross on 2/1/15.
 */
public class DelimiterAdapter extends ArrayAdapter<String> implements Filterable {
    private final static String [] options = {
            "Apple","Mango","Peach","Banana","Orange","Grapes","Watermelon","Tomato"
    };
    private final LayoutInflater mInflater;
    private List<String> mSubStrings;

    private String mMainString;
    public String getMainString() { return mMainString; }

    private FuckingAmazingFilter mFilter;

    public DelimiterAdapter(Context context, int resource) {
        super(context, -1);
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final TextView tv;
        if (convertView != null) {
            tv = (TextView) convertView;
        } else {
            tv = (TextView) mInflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
        }
        tv.setText(getItem(position));
        return tv;
    }

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

    @Override
    public String getItem(int position) {
        return mSubStrings.get(position);
    }

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


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

    private class FuckingAmazingFilter extends Filter {

        private final static String DELIMITER = "+";

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            final FilterResults filterResults = new FilterResults();
            String request;
            mSubStrings = new ArrayList<String>();
            if(constraint != null) {
                request = constraint.toString();

                //cuts the string with delimiter
                if (request.contains(DELIMITER) &&
                        request.lastIndexOf(DELIMITER) != request.length() - 1) {
                    final String[] splitted = request.split("\\" + DELIMITER);
                    request = splitted[splitted.length - 1].trim();

                    //save string before delimiter
                    int index = constraint.toString().lastIndexOf(request);
                    mMainString = constraint.toString().substring(0, index);
                } else {
                    request = request.trim();
                    mMainString = "";
                }


                //checks for substring of any word in the dictionary
                for(String s : options) {
                    if(s.contains(request)) {
                        mSubStrings.add(s);
                    }
                }
            }
            filterResults.values = mSubStrings;
            filterResults.count = mSubStrings.size();
            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            clear();
            for (String request : (ArrayList<String>)results.values) {
                add(request);
            }
            if (results.count > 0) {
                notifyDataSetChanged();
            } else {
                notifyDataSetInvalidated();
            }
        }
    }
}
Расширенный SearchView :

Код: Выделить всё

package cullycross.com.searchview;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;

/**
 * Created by cullycross on 1/31/15.
 */
public class MultiAutoCompleteSearchView extends android.support.v7.widget.SearchView {

    private SearchAutoComplete mSearchAutoComplete;

    public void initialize() {
        mSearchAutoComplete = (SearchAutoComplete)
                findViewById(android.support.v7.appcompat.R.id.search_src_text);
        this.setAdapter(null);
        this.setOnItemClickListener(null);
    }

    public MultiAutoCompleteSearchView(Context context) {
        super(context);
        initialize();
    }

    public MultiAutoCompleteSearchView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public MultiAutoCompleteSearchView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize();
    }

    public void setOnItemClickListener(AdapterView.OnItemClickListener listener) {
        mSearchAutoComplete.setOnItemClickListener(listener);
    }

    public void setAdapter(ArrayAdapter<?> adapter) {
        mSearchAutoComplete.setAdapter(adapter);
    }
}
onCreateOptionsMenu в Activity:

Код: Выделить всё

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_search, menu);

        final MenuItem searchItem = menu.findItem(R.id.action_search);

        final MultiAutoCompleteSearchView searchView = (MultiAutoCompleteSearchView)
                MenuItemCompat.getActionView(searchItem);

        searchView.setQueryHint("Type any word");

        MultiAutoCompleteSearchView.SearchAutoComplete searchAutoComplete =
                (MultiAutoCompleteSearchView.SearchAutoComplete)searchView
                        .findViewById(R.id.search_src_text);
        //since words are very short
        searchAutoComplete.setThreshold(1);

        searchAutoComplete.setAdapter(new DelimiterAdapter(
                this,
                android.R.layout.simple_dropdown_item_1line
        ));

        searchView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                String stringBefore, newString;
                stringBefore = ((DelimiterAdapter)parent.getAdapter()).getMainString();
                newString = parent.getAdapter().getItem(position).toString();

                searchView.setQuery(stringBefore+newString, false);

            }
        });


        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) {
                if(s.length() > 0) {
                    mFindString = s;

                    mGetAndParse.getAndParse("lalka");
                    return true;
                }
                return false;
            }

            @Override
            public boolean onQueryTextChange(String s) {
                /*StringBuilder filteredStringBuilder = new StringBuilder();
                for (char ch : s.toCharArray()) {
                    if (Character.isLetter(ch) || ch == '+') {
                        filteredStringBuilder.append(ch);
                    }
                }
                searchView.setQuery(filteredStringBuilder.toString(), false);*/
                return false;
            }
        });

        return true;
    }
XML файл menu:

Код: Выделить всё

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      tools:context=".SearchActivity">
    <item android:id="@+id/action_search"
          android:title="@string/action_search"
          android:icon="@drawable/ic_action_search"
          app:showAsAction="ifRoom|collapseActionView"
          app:actionViewClass="cullycross.com.searchview.MultiAutoCompleteSearchView" />
</menu>