Làm cách nào để sử dụng AutoCompleteTextView và điền vào nó với dữ liệu từ một API web?


82

Tôi muốn sử dụng một AutoCompleteTextViewtrong hoạt động của mình và điền dữ liệu khi người dùng nhập bằng cách truy vấn một API web. Làm thế nào để làm điều này?

Tôi tạo một lớp mới và ghi đè AutoCompleteTextView.performFilteringhay tôi sử dụng bộ điều hợp danh sách tùy chỉnh và cung cấp một tùy chỉnh android.widget.Filterghi đè performanceFiltering?

Hay có cách nào tốt hơn để đạt được mục tiêu cuối cùng của tôi?

Tôi đã làm điều gì đó tương tự, nhưng đó là đối với hộp Tìm kiếm nhanh và nó liên quan đến việc triển khai một dịch vụ, nhưng tôi tin rằng đó không phải là điều tôi muốn làm ở đây.


10
một liên kết tuyệt vời cho người xem trong tương lai :): makovkastar.github.io/blog/2014/04/12/…
Tina

Câu trả lời:


104

Tôi đã nghĩ ra một giải pháp, tôi không biết nó có phải là giải pháp tốt nhất hay không, nhưng nó có vẻ hoạt động rất tốt. Những gì tôi đã làm là tạo một bộ điều hợp tùy chỉnh mở rộng ArrayAdapter. Trong bộ điều hợp tùy chỉnh, tôi ghi đè getFilter và tạo lớp Bộ lọc của riêng mình để ghi đè performanceFiltering. Điều này bắt đầu một chuỗi mới để nó không làm gián đoạn giao diện người dùng. Dưới đây là một ví dụ trần trụi.

MyActivity.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);
    }
}

AutoCompleteAdapter.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;
    }
}

4
Giải pháp tốt - chính xác những gì tôi cần. Nếu tôi có thể hỏi thì ... bạn hãy trả lại một kiểu cho bố cục simple_dropdown_item_1line của bạn. Làm cách nào để lấy một giá trị thích hợp từ lớp Kiểu của bạn? Đối với tôi, đó là một lớp tôi đã tạo và tôi muốn giá trị văn bản getStyleName hiển thị trong mục danh sách của mình, nhưng nó đơn giản hiển thị tên của lớp.
bugfixr

7
@bugfixr Thêm phương thức toString công khai trên lớp của bạn. Trong ví dụ của tôi, đó là: public String toString () {return name; }
AJ.

1
Hãy cẩn thận với lệnh gọi InformDataSetInvalidated (). Điều đó có nghĩa là sau đó bộ điều hợp không thể được sử dụng.
Vikram Bodicherla

1
Nhưng điều này đang thực hiện yêu cầu mạng trong cùng một luồng, performFilteringphương thức có chạy trong luồng không phải giao diện người dùng không?
Bhargav

2
@Tohid Đây vẫn là câu trả lời tốt nhất, chỉ có thể thêm một điều vào điều này. Đó là một trình xử lý bị trì hoãn có thể được sử dụng để kích hoạt lệnh gọi API. Như được hiển thị ở đây: truiton.com/2018/06/…
KnowIT

7

Mở rộng trên AJ. Câu trả lời ở trên, bộ điều hợp tùy chỉnh sau bao gồm việc xử lý các yêu cầu máy chủ và phân tích cú pháp 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();
            }
        };
    }

và sử dụng nó theo cách tương tự:

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

3

Chu: Để tùy chỉnh giao diện của chế độ xem và kiểm soát nhiều hơn việc mở gói đối tượng, hãy làm như sau ...

    @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;
    }

2
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();
        }
    });        
}

AutoCompleteUserAdapter

/**
 * 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();
    }
}

1
bỏ phiếu cho actvName.isPerformingCompletion () đã lưu xử lý của tôi.
Rajat Mehra

1

Đây là phiên bản Kotlin của Lớp Bộ điều hợp tải dữ liệu từ cơ sở dữ liệu cục bộ thông qua Phòng:

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
        }
    }
}

Định nghĩa phương pháp DAO:

    @Query("select * from SearchResult where full_name like :name and type = 'USER_TYPE'")
fun findWithNameLike(name: String): List<SearchResult>

Cảm ơn! Bạn vừa tiết kiệm cho tôi vài giờ =)
Barakuda
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.