AutoCompleteTextView를 사용하고 웹 API의 데이터로 채우려면 어떻게합니까?


82

AutoCompleteTextView내 활동에서 를 사용 하고 웹 API를 쿼리하여 사용자가 입력 할 때 데이터를 채우고 싶습니다. 어떻게해야합니까?

새 클래스를 만들고 재정의 AutoCompleteTextView.performFiltering합니까, 아니면 사용자 지정 목록 어댑터를 사용하고 android.widget.FilterperformFiltering을 재정의 하는 사용자 지정 을 제공 합니까?

아니면 최종 목표를 달성하는 더 좋은 방법이 있습니까?

비슷한 작업을 수행했지만 빠른 검색 상자를위한 것이었고 서비스 구현과 관련이 있었지만 여기에서하고 싶은 것이 아니라고 생각합니다.


10
: : 미래의 시청자에 대한 훌륭한 링크 makovkastar.github.io/blog/2014/04/12/...
티나

답변:


104

나는 해결책을 생각해 냈고 그것이 최선의 해결책인지는 모르겠지만 매우 잘 작동하는 것 같습니다. 내가 한 일은 ArrayAdapter를 확장하는 사용자 지정 어댑터를 만든 것입니다. 사용자 지정 어댑터에서 getFilter를 재정의하고 performFiltering을 재정의하는 고유 한 Filter 클래스를 만들었습니다. 이렇게하면 새 스레드가 시작되므로 UI가 중단되지 않습니다. 아래는 베어 본 예제입니다.

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
좋은 솔루션-정확히 내가 필요한 것. 그래도 물어볼 수 있다면 ... simple_dropdown_item_1 라인 레이아웃에 스타일을 반환합니다. Style 클래스에서 적절한 값을 어떻게 검색합니까? 나를 위해 그것은 내가 만든 클래스이고 텍스트 값 getStyleName이 내 목록 항목에 표시되기를 원하지만 클래스 이름이 간단하게 표시됩니다.
bugfixr 2011

7
@bugfixr 클래스에 공용 toString 메소드를 추가하십시오. 내 예에서는 다음과 같습니다. public String toString () {return name; }
AJ.

1
notifyDataSetInvalidated () 호출에주의하십시오. 이는 나중에 어댑터를 사용할 수 없음을 의미합니다.
Vikram Bodicherla

1
그러나 이것은 동일한 스레드에서 네트워크 요청을 수행하고 있으며 performFiltering메서드가 UI가 아닌 스레드에서 실행되고 있습니까?
Bhargav

2
@Tohid 이것은 여전히 ​​최선의 대답이며, 여기에 하나만 추가 할 수 있습니다. 즉, 지연된 핸들러를 사용하여 API 호출을 트리거 할 수 있습니다. 여기에 표시된대로 : truiton.com/2018/06/…
KnowIT

7

AJ에서 확장 . 의 답변 위의 다음 사용자 정의 어댑터에는 서버 요청 처리 및 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);
    }
}

3

Chu :보기 모양을 사용자 지정하고 개체 래핑 해제에 대한 더 많은 제어 권한을 얻으려면 다음을 수행하십시오.

    @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
내 처리를 저장 한 actvName.isPerformingCompletion ()에 대한 찬성 투표.
Rajat Mehra

1

다음은 Room을 통해 로컬 데이터베이스에서 데이터를로드하는 어댑터 클래스의 Kotlin 버전입니다.

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>

감사! 당신은 저에게 몇 시간을 절약했습니다 =)
Barakuda
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.