Как использовать AutoCompleteTextView и заполнить его данными из веб-API?
-
14-11-2019 - |
Вопрос
Я хочу использовать AutoCompleteTextView
в моей деятельности и заполнять данные по мере ввода пользователем, запрашивая веб-API.Как мне это сделать?
Могу ли я создать новый класс и переопределить AutoCompleteTextView.performFiltering
, или мне использовать собственный адаптер списка и предоставить собственный android.widget.Filter
это переопределяет выполнитьFiltering?
Или есть лучший способ достичь моей конечной цели?
Я сделал что-то похожее, но это было для поля быстрого поиска и включало реализацию службы, но я считаю, что это не то, что я хочу делать здесь.
Решение
Я нашел решение, не знаю, лучшее ли это решение, но, похоже, оно работает очень хорошо.Я создал собственный адаптер, расширяющий ArrayAdapter.В пользовательском адаптере я переопределил getFilter и создал свой собственный класс Filter, который переопределяет PerformFiltering.При этом запускается новый поток, поэтому он не прерывает работу пользовательского интерфейса.Ниже приведен простой пример.
Моя активность.java
public class MyActivity extends Activity {
private AutoCompleteTextView style;
@Override
public void onCreate(Bundle savedInstanceState) {
...
style = (AutoCompleteTextView) findViewById(R.id.style);
adapter = new AutoCompleteAdapter(this, android.R.layout.simple_dropdown_item_1line);
style.setAdapter(adapter);
}
}
АвтозаполнениеAdapter.java
public class AutoCompleteAdapter extends ArrayAdapter<Style> implements Filterable {
private ArrayList<Style> mData;
public AutoCompleteAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
mData = new ArrayList<Style>();
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Style getItem(int index) {
return mData.get(index);
}
@Override
public Filter getFilter() {
Filter myFilter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if(constraint != null) {
// A class that queries a web API, parses the data and returns an ArrayList<Style>
StyleFetcher fetcher = new StyleFetcher();
try {
mData = fetcher.retrieveResults(constraint.toString());
}
catch(Exception e) {
Log.e("myException", e.getMessage());
}
// Now assign the values and count to the FilterResults object
filterResults.values = mData;
filterResults.count = mData.size();
}
return filterResults;
}
@Override
protected void publishResults(CharSequence contraint, FilterResults results) {
if(results != null && results.count > 0) {
notifyDataSetChanged();
}
else {
notifyDataSetInvalidated();
}
}
};
return myFilter;
}
}
Другие советы
Расширение Эй Джей.ответ выше, следующий специальный адаптер включает в себя обработку запросов сервера, а также анализ json:
class AutoCompleteAdapter extends ArrayAdapter<String> implements Filterable
{
private ArrayList<String> data;
private final String server = "http://myserver/script.php?query=";
AutoCompleteAdapter (@NonNull Context context, @LayoutRes int resource)
{
super (context, resource);
this.data = new ArrayList<>();
}
@Override
public int getCount()
{
return data.size();
}
@Nullable
@Override
public String getItem (int position)
{
return data.get (position);
}
@NonNull
@Override
public Filter getFilter()
{
return new Filter()
{
@Override
protected FilterResults performFiltering (CharSequence constraint)
{
FilterResults results = new FilterResults();
if (constraint != null)
{
HttpURLConnection conn = null;
InputStream input = null;
try
{
URL url = new URL (server + constraint.toString());
conn = (HttpURLConnection) url.openConnection();
input = conn.getInputStream();
InputStreamReader reader = new InputStreamReader (input, "UTF-8");
BufferedReader buffer = new BufferedReader (reader, 8192);
StringBuilder builder = new StringBuilder();
String line;
while ((line = buffer.readLine()) != null)
{
builder.append (line);
}
JSONArray terms = new JSONArray (builder.toString());
ArrayList<String> suggestions = new ArrayList<>();
for (int ind = 0; ind < terms.length(); ind++)
{
String term = terms.getString (ind);
suggestions.add (term);
}
results.values = suggestions;
results.count = suggestions.size();
data = suggestions;
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
if (input != null)
{
try
{
input.close();
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
if (conn != null) conn.disconnect();
}
}
return results;
}
@Override
protected void publishResults (CharSequence constraint, FilterResults results)
{
if (results != null && results.count > 0)
{
notifyDataSetChanged();
}
else notifyDataSetInvalidated();
}
};
}
и используйте его таким же образом:
public class MyActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState) {
...
AutoCompleteTextView textView = (AutoCompleteTextView) findViewById (R.id.style);
int layout = android.R.layout.simple_list_item_1;
AutoCompleteAdapter adapter = new AutoCompleteAdapter (this, layout);
textView.setAdapter (adapter);
}
}
Чу:Чтобы настроить внешний вид представления и получить больше контроля над развертыванием объекта, выполните следующие действия...
@Override
public View getView (int position, View convertView, ViewGroup parent) {
TextView originalView = (TextView) super.getView(position, convertView, parent); // Get the original view
final LayoutInflater inflater = LayoutInflater.from(getContext());
final TextView view = (TextView) inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
// Start tweaking
view.setText(originalView.getText());
view.setTextColor(R.color.black); // also useful if you have a color scheme that makes the text show up white
view.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); // override the text size
return view;
}
private AutoCompleteUserAdapter userAdapter;
private AutoCompleteTextView actvName;
private ArrayList<SearchUserItem> arrayList;
actvName = findViewById(R.id.actvName);
actvName.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
actvName.setText(userAdapter.getItemNameAtPosition(position));
actvName.setSelection(actvName.getText().toString().trim().length());
}
});
actvName.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(final CharSequence s, int start, int before, int count) {
if (actvName.isPerformingCompletion()) {
// An item has been selected from the list. Ignore.
} else {
if (s.toString().toLowerCase().trim().length() >= 2) {
getUserList(s.toString().toLowerCase().trim());
}
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
private void getUserList(String searchText) {
//Add data to your list after success of API call
arrayList = new ArrayList<>();
arrayList.addAll(YOUR_LIST);
userAdapter = new AutoCompleteUserAdapter(context, R.layout.row_user, arrayList);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
actvName.setAdapter(userAdapter);
userAdapter.notifyDataSetChanged();
actvName.showDropDown();
}
});
}
АвтозаполнениеПользовательскийАдаптер
/**
* Created by Ketan Ramani on 11/07/2019.
*/
public class AutoCompleteUserAdapter extends ArrayAdapter<SearchUserItem> {
private Context context;
private int layoutResourceId;
private ArrayList<SearchUserItem> arrayList;
public AutoCompleteUserAdapter(Context context, int layoutResourceId, ArrayList<SearchUserItem> arrayList) {
super(context, layoutResourceId, arrayList);
this.context = context;
this.layoutResourceId = layoutResourceId;
this.arrayList = arrayList;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
try {
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate(layoutResourceId, parent, false);
}
SearchUserItem model = arrayList.get(position);
AppCompatTextView tvUserName = convertView.findViewById(R.id.tvUserName);
tvUserName.setText(model.getFullname());
} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return convertView;
}
public String getItemNameAtPosition(int position) {
return arrayList.get(position).getName();
}
public String getItemIDAtPosition(int position) {
return arrayList.get(position).getId();
}
}
Вот версия класса адаптера Kotlin, которая загружает данные из локальной базы данных через Room:
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 ...MyFinderDatabase
import ...R
import ...model.SearchResult
class SearchCompleteAdapter(context: Context, val resourceId: Int): ArrayAdapter<SearchResult>(context, resourceId), Filterable {
private val results = mutableListOf<SearchResult>()
override fun getCount() = results.size
override fun getItem(position: Int) = results[position]
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context).inflate(resourceId, parent, false)
val textView = view.findViewById<TextView>(R.id.autocomplete_name)
textView.text = getItem(position).fullName
return view
}
override fun getFilter() = object : Filter(){
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filterResults = FilterResults()
val db = MyRoomDatabase.getDatabase(context.applicationContext)
val dbResults = db.resultDao().findWithNameLike(String.format("%%%s%%", constraint.toString()))
filterResults.values = dbResults
filterResults.count = dbResults.size
results.clear()
results.addAll(dbResults)
return filterResults
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
if((results != null) && (results.count > 0)){
notifyDataSetChanged()
}
else{
notifyDataSetInvalidated()
}
}
override fun convertResultToString(resultValue: Any?): CharSequence {
val searchResult = resultValue as SearchResult
return searchResult.fullName
}
}
}
Определение метода DAO:
@Query("select * from SearchResult where full_name like :name and type = 'USER_TYPE'")
fun findWithNameLike(name: String): List<SearchResult>