2

私のアプリでは、ユーザーの電話帳から連絡先を読んでいて、ユーザーが何を望んでいるかに基づいて何らかのアクションを実行します。連絡先がユーザーの電話帳からインポートされるアクティビティには、ユーザーが連絡先をフィルタリングして検索するために使用できるeditTextフィールドも含まれています。コードは次のとおりです。

public class InviteFriendsFromContactsActivity extends Activity implements
    TextWatcher {

private EditText friendSearch;

private ArrayList<ContactInfo> contacts;

private ListView contactsListView;

private ContactsListviewAdapter clvAdapter;


public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.invite_from_contacts);

    friendSearch = (EditText) findViewById(R.id.friendsearch);
    friendSearch.addTextChangedListener(this);



    contactsListView = (ListView) findViewById(R.id.ContactsListView);

    ReadContacts();

}

void ReadContacts() {

    contacts = new ArrayList<ContactInfo>();

    Cursor cursor = getContentResolver().query(
            ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
    while (cursor.moveToNext()) {
        long contactId = cursor.getLong(cursor
                .getColumnIndex(ContactsContract.Contacts._ID));
        String name = cursor.getString(cursor
                .getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
        Cursor emails = getContentResolver().query(
                ContactsContract.CommonDataKinds.Email.CONTENT_URI,
                null,
                ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = "
                        + contactId, null, null);
        while (emails.moveToNext()) {
            // This would allow to get several email addresses, so if 1
            // contact has 2 emails, will be listed as 2 rows
            String emailAddress = emails
                    .getString(emails
                            .getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
            if (emailAddress != null)
                contacts.add(new ContactInfo(contactId, name, emailAddress));
        }
        emails.close();
    }
    cursor.close();

    clvAdapter = new ContactsListviewAdapter(getApplicationContext(),
            contacts);

    contactsListView.setAdapter(clvAdapter);

}

public void onTextChanged(CharSequence s, int start, int before, int count) {
    if (clvAdapter != null) {
        clvAdapter.getFilter().filter(s);
        contactsListView.setAdapter(clvAdapter);
    }
    if (clvAdapter.getCount() == 0) {
        ListView list = (ListView) findViewById(R.id.ContactsListView);
        list.setClickable(false);

        final List<ContactInfo> listItems = new ArrayList<ContactInfo>();
        listItems.add(new ContactInfo("No results", ""));

        ContactsListviewAdapter adapter = new ContactsListviewAdapter(
                InviteFriendsFromContactsActivity.this, listItems) {

            @Override
            public View getView(int position, View convertView,
                    ViewGroup viewGroup) {
                ContactInfo entry = listItems.get(position);

                CheckBox checkBox;
                TextView nameText;
                TextView emailText;

                LayoutInflater inflater = (LayoutInflater) InviteFriendsFromContactsActivity.this
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.row, null);

                nameText = (TextView) convertView
                        .findViewById(R.id.toptext);

                emailText = (TextView) convertView
                        .findViewById(R.id.bottomtext);

                checkBox = (CheckBox) convertView
                        .findViewById(R.id.checkBox_invited);

                checkBox.setVisibility(View.GONE);
                nameText.setText(entry.getName());
                emailText.setVisibility(View.GONE);

                convertView.setBackgroundColor(Color.WHITE);

                return convertView;
            }
        };
        list.setAdapter(adapter);
    }

}
}

また、ContactsListViewAdapterクラスは次のとおりです。これはかなり大きなクラスですが、この質問に最も関連があると思う部分はgetFilterメソッドです。

class ContactsViewHolder {

public ContactsViewHolder(TextView nameTextView, TextView emailTextView,
        CheckBox checkBox, ImageView iv) {
    super();
    this.nameTextView = nameTextView;
    this.emailTextView = emailTextView;
    this.checkBox = checkBox;
    this.iv = iv;
}

public TextView getNameTextView() {
    return nameTextView;
}

public void setNameTextView(TextView nameTextView) {
    this.nameTextView = nameTextView;
}

public TextView getEmailTextView() {
    return emailTextView;
}

public void setEmailTextView(TextView emailTextView) {
    this.emailTextView = emailTextView;
}

public CheckBox getCheckBox() {
    return checkBox;
}

public void setCheckBox(CheckBox checkBox) {
    this.checkBox = checkBox;
}

public ImageView getIv() {
    return iv;
}

public void setIv(ImageView iv) {
    this.iv = iv;
}

private TextView nameTextView;
private TextView emailTextView;
private CheckBox checkBox;
private ImageView iv;

}

public class ContactsListviewAdapter extends BaseAdapter implements Filterable {
private Context context;

private List<ContactInfo> contacts;

private List<ContactInfo> originalContacts;

private List<String> selectedEmails = new ArrayList<String>();

public ContactsListviewAdapter(Context context, List<ContactInfo> contacts) {
    this.context = context;
    this.contacts = contacts;
}

public int getCount() {
    return contacts.size();
}

public Object getItem(int position) {
    return contacts.get(position);
}

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

public View getView(int position, View convertView, ViewGroup viewGroup) {
    ContactInfo entry = contacts.get(position);

    CheckBox checkBox;
    TextView nameText;
    TextView emailText;
    ImageView contactPic;

    if (convertView == null) {
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.row, null);

        nameText = (TextView) convertView.findViewById(R.id.toptext);

        emailText = (TextView) convertView.findViewById(R.id.bottomtext);

        contactPic = (ImageView) convertView.findViewById(R.id.pic);

        checkBox = (CheckBox) convertView
                .findViewById(R.id.checkBox_invited);

        // Optimization: Tag the row with it's child views, so we don't have
        // to
        // call findViewById() later when we reuse the row.
        convertView.setTag(new ContactsViewHolder(nameText, emailText,
                checkBox, contactPic));

        // If CheckBox is toggled, update the planet it is tagged with.
        checkBox.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                CheckBox cb = (CheckBox) v;
                ContactInfo contact = (ContactInfo) cb.getTag();
                contact.setChecked(cb.isChecked());

                if(contact.isChecked())
                {
                    selectedEmails.add(contact.getEmail());
                }
            }
        });
    }
    // Reuse existing row view
    else {
        // Because we use a ViewHolder, we avoid having to call
        // findViewById().
        ContactsViewHolder viewHolder = (ContactsViewHolder) convertView
                .getTag();
        checkBox = viewHolder.getCheckBox();
        nameText = viewHolder.getNameTextView();
        emailText = viewHolder.getEmailTextView();
        contactPic = viewHolder.getIv();
    }

    // Tag the CheckBox with the Contact it is displaying, so that we can
    // access the Contact in onClick() when the CheckBox is toggled.
    checkBox.setTag(entry);

    // Display contact data
    checkBox.setChecked(entry.isChecked());
    nameText.setText(entry.getName());
    emailText.setText(entry.getEmail());

    Bitmap bitmap = People.loadContactPhoto(context,
            ContentUris.withAppendedId(People.CONTENT_URI, entry.getId()),
            R.drawable.contacts, null);
    contactPic.setImageBitmap(bitmap);

    return convertView;
}

@Override
public Filter getFilter() {
    Filter filter = new Filter() {

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint,
                FilterResults results) {

            contacts = (List<ContactInfo>) results.values; // has the filtered values
            notifyDataSetChanged(); // notifies the data with new filtered values
        }

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults results = new FilterResults(); // Holds the
                                                            // results of a
                                                            // filtering
                                                            // operation in
                                                            // values
            List<ContactInfo> FilteredArrList = new ArrayList<ContactInfo>();

            if (originalContacts == null) {
                originalContacts = new ArrayList<ContactInfo>(contacts); 
            }

            /********
             * 
             * 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 = originalContacts.size();
                results.values = originalContacts;
            } else {
                constraint = constraint.toString().toLowerCase();
                for (int i = 0; i < originalContacts.size(); i++) {
                    String name = originalContacts.get(i).getName();
                    if (name.toLowerCase()
                            .startsWith(constraint.toString())) {
                        FilteredArrList.add(originalContacts.get(i));
                    }
                }
                // set the Filtered result to return
                results.count = FilteredArrList.size();
                results.values = FilteredArrList;
            }
            return results;
        }
    };
    return filter;
}


public List<String> getSelectedEmails() {
    return selectedEmails;
}
}

連絡先はアクティビティに正常にインポートされます。連絡先を検索しようとすると問題が発生します。検索フィールドに「x」と入力すると(電話帳にxで始まる連絡先がありません)、0の連絡先が表示されますが、メッセージNo results foundは表示されません。'x'の後に別の文字を入力した後にのみ表示されます。通常、メッセージNo results foundは実際に表示された後、常に1文字で表示されます。

何が問題で、メッセージはどのように遅れますか?

4

2 に答える 2

0

コードをあまり見なくても、その遅延の理由は、メソッドでフィルタリングを設定する方法にあると言えますonTextChanged。アダプタでのフィルタリングはバックグラウンドスレッド(そのスレッドで実行)で実行され、メソッド(メインUIスレッドで実行)が呼び出されるperformFilteringまで、アダプタは新しい値、つまりフィルタリングの結果を認識しません。publishResults

ここで、制約を指定してメソッドをonTextchanged呼び出し、filterすぐに同じアダプターを再設定しListViewます(なぜこのようなことをするのですか?!)。テストに到達するまでにif (clvAdapter.getCount() == 0)、フィルタリングが終了しておらず、アダプターに古い値が残っている可能性が高いため、ifテストは失敗し、設定したアダプターに「結果が見つかりません」というテキストが表示されません。別の文字を入力すると、onTextChangedメソッドが再度呼び出され、今回はそのif状態でアダプタが実際に空になりますtrue

メソッドをクリーンアップしonTextchanged(呼び出すだけfilter)、その特別な行をアダプターに直接実装してみます。

于 2012-11-04T16:34:43.727 に答える
0

まったく同じ問題が発生したため、フッターをリストに追加し、フィルタリングが完了したら削除する必要がありました。TextWatcherは、1文字の遅延で結果を表示します。そこで、少し違う角度からアプローチしました。まず、メソッドを作成し、GLobalLayoutListenerそのメソッド内のリストビューにを追加しました。

public void DoGlobalListener(){
        mTextList.getViewTreeObserver().addOnGlobalLayoutListener(
                new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                       if(mListAdapter.isEmpty() && !mFooterAdded){
                        mTextList.addFooterView(mFooter);
                        mFooterAdded = true;
                       }else{
                        mTextList.removeFooterView(mFooter);
                        mFooterAdded = false;
                       }
                       mTextList.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    }
                });
    }

ここでは、のadapterセットListViewが空であるかどうかを確認booleanしてから、フッターが追加されているかどうかを確認するために設定したかどうかをfalseに設定します。両方の条件が満たされている場合は、フッターを追加し、booleanをtrueに設定します。条件が満たされない場合は、逆のことが行われます。最後に、これを停止しGlobalLayoutListenerて、これが1回だけ行われるようにします。このメソッドは、textWatcherのメソッド内で呼び出すことができ、のテキストが変更さafterTextChangedれるたびに実行されます。EditText

    @Override
    public void afterTextChanged(Editable s) {
    DoGlobalListener();
}

これは、物事を行うための最も適切で最適化された方法ではないかもしれませんが、それは仕事をします。この質問が投稿されてからしばらく経ちましたが、この問題に直面している人の助けになることを願っています。乾杯

于 2014-01-12T11:14:19.413 に答える