android - 使用新适配器更新 recyclerview

标签 android android-recyclerview

我在顶部有一个带有 Searchview 的 fragment ,在此下方我显示了包含 las 10 次调用的调用日志。为了显示通话记录,我将 recyclerview 与卡片一起使用。这种行为很好,但现在我想实现其他东西。

如果我在 Searchview 中搜索一个名字,我想做一些事情,比如当结果列表更新时显示与联系人列表的重合。也就是说,我需要重用 recyclerview,但此时,我将显示与联系人列表的巧合,而不是通话记录。

我使用了找到的代码 here ,但不工作。我正在做一些测试,看看有什么问题,我发现我无法重用 recyclerview。

首先,我这样做是为了显示通话记录:

mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this.getActivity()));
mLogAdapter = new LogAdapter(DisplayCallLog());
mRecyclerView.setAdapter(mLogAdapter);

当搜索 View 更新时,我尝试只显示联系人列表以验证它是否有效:

public boolean onQueryTextChange(String query) {
    mContactAdapter = new ContactAdapter(ContactsList());
    mRecyclerView.swapAdapter(mContactAdapter, true);
    //final List<ContactInfo> filteredModelList = filter(ContactsList(), query);
    //mContactAdapter.animateTo(filteredModelList);
    //mRecyclerView.scrollToPosition(0);
    return true;
}

但我无法在同一个 recyclerview 上显示联系人列表。


编辑 1 -> 评论

我试过在启动时加载联系人列表而不是日志列表,但它也没有加载。也许问题是联系人列表太长了?


EDIT 2 -> 添加了大量代码

1) 设置 SearchView

我没有在操作栏中添加搜索 View ,而是使用卡片 View 来包含它。这是 phone_layout.xml

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="60dp">
    <android.support.v7.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardCornerRadius="4dp"
        card_view:cardElevation="4dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginLeft="80dp"
        android:layout_marginRight="80dp">
        <android.support.v7.widget.SearchView
            android:id="@+id/dialpad_searchview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/transparent"
            android:textSize="18sp"
            app:queryHint="@string/enter_phone_number"
            app:iconifiedByDefault="false"
            android:imeOptions="flagNoFullscreen"/>
    </android.support.v7.widget.CardView>
</LinearLayout>
<android.support.v7.widget.RecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/recyclerview"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="5dp"
    android:layout_marginLeft="80dp"
    android:layout_marginRight="80dp"/>

2) 设置适配器

首先我定义了模型类 ContactInfo。这对于展位使用通话记录和联系巧合是相同的。

public class ContactInfo {

public int id;
public String name;
public String number;
public String type;
public int logType;

public static final String ID_PREFIX = "ID_";
public static final String NUMBER_PREFIX = "Name_";
public static final String NAME_PREFIX = "Number_";
public static final String TYPE_PREFIX = "Type_";

public String getContactName() {
    return name;
}
public String getContactNumber() {
    return number;
}

展台的ContactViewHolder也是一样的。

public class ContactViewHolder extends RecyclerView.ViewHolder {
protected TextView vName;
protected TextView vType;
protected ImageView vPic;

public ContactViewHolder(View v) {
    super(v);
    vName =  (TextView) v.findViewById(R.id.contactname);
    vType = (TextView)  v.findViewById(R.id.contacttype);
    vPic = (ImageView)  v.findViewById(R.id.contactpic);
}

现在,每种用途的不同之处在于布局和适配器。从通话记录开始,这里是 phone_calllog_card.xml

<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardCornerRadius="4dp"
card_view:cardElevation="4dp"
android:layout_marginBottom="5dp">
<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:selectableItemBackground">
    <ImageView
        android:id="@+id/contactpic"
        android:tag="image_tag"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="20dp"/>
    <TextView
        android:id="@+id/contactname"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/contactpic"
        android:layout_marginLeft="40dp"
        android:text="Name"
        android:textAppearance="?android:attr/textAppearanceLarge"/>
    <TextView
        android:id="@+id/contacttype"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/contactname"
        android:layout_toRightOf="@+id/contactpic"
        android:layout_marginLeft="40dp"
        android:text="Type"
        android:textAppearance="?android:attr/textAppearanceMedium"/>
</RelativeLayout>

还有 LogAdapter 类。

public class LogAdapter extends RecyclerView.Adapter<ContactViewHolder> {

private List<ContactInfo> logList;

public LogAdapter(List<ContactInfo> logList) {
    this.logList = logList;
}

@Override
public int getItemCount() {
    return logList.size();
}

@Override
public void onBindViewHolder(ContactViewHolder contactViewHolder, int i) {
    final String number;
    String name;
    ContactInfo ci = logList.get(i);
    name = ci.name;
    if (name.equals("-2")) {
        name = "Private";
    }
    contactViewHolder.vName.setText(name);
    contactViewHolder.vType.setText(ci.type);
    number = ci.number;
    }

    contactViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(Intent.ACTION_CALL);
            intent.setData(Uri.parse("tel:" + number.trim()));
            if (ActivityCompat.checkSelfPermission(v.getContext(), Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
                v.getContext().startActivity(intent);
            }
        }
    });
}

@Override
public ContactViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    View itemView = LayoutInflater.
            from(viewGroup.getContext()).
            inflate(R.layout.phone_calllog_card, viewGroup, false);

    return new ContactViewHolder(itemView);
}

phone_contact_card.xmlContactAdapter 与之前的这些几乎完全不同,但几乎没有变化。

<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardCornerRadius="4dp"
card_view:cardElevation="4dp"
android:layout_marginBottom="5dp">
<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:selectableItemBackground">
    <ImageView
        android:id="@+id/contactpic"
        android:tag="image_tag"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="20dp"/>
    <TextView
        android:id="@+id/contactname"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/contactpic"
        android:layout_marginLeft="40dp"
        android:layout_centerVertical="true"
        android:text="Name"
        android:textAppearance="?android:attr/textAppearanceLarge"/>
</RelativeLayout>

通话记录适配器与联系人巧合之间的区别在于,通话记录在输入 fragment 时显示,并且显示来自通话记录的 10 个静态结果。但是对于联系巧合,每次在serachview中输入一个字母来查找巧合,它必须有某种动画来刷新列表,所以这里有一些额外的方法。

public class ContactAdapter extends RecyclerView.Adapter<ContactViewHolder> {

private List<ContactInfo> contactList;

public ContactAdapter(List<ContactInfo> contactList) {
    this.contactList = contactList;
}

@Override
public int getItemCount() {
    return contactList.size();
}

@Override
public void onBindViewHolder(ContactViewHolder contactViewHolder, int i) {
    final String number;
    ContactInfo ci = contactList.get(i);
    contactViewHolder.vName.setText(ci.name);
    contactViewHolder.vPic.setImageResource(R.drawable.contact_icon_blue);
    number = ci.number;

    contactViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(Intent.ACTION_CALL);
            intent.setData(Uri.parse("tel:" + number.trim()));
            if (ActivityCompat.checkSelfPermission(v.getContext(), Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
                v.getContext().startActivity(intent);
            }
        }
    });
}

@Override
public ContactViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    View itemView = LayoutInflater.
            from(viewGroup.getContext()).
            inflate(R.layout.phone_contact_card, viewGroup, false);

    return new ContactViewHolder(itemView);
}

public void animateTo(List<ContactInfo> contacts) {
    applyAndAnimateRemovals(contacts);
    applyAndAnimateAdditions(contacts);
    applyAndAnimateMovedItems(contacts);
}

private void applyAndAnimateRemovals(List<ContactInfo> newContacts) {
    for (int i = contactList.size() - 1; i >= 0; i--) {
        final ContactInfo model = contactList.get(i);
        if (!newContacts.contains(model)) {
            removeItem(i);
        }
    }
}

private void applyAndAnimateAdditions(List<ContactInfo> newContacts) {
    for (int i = 0, count = contactList.size(); i < count; i++) {
        final ContactInfo model = newContacts.get(i);
        if (!contactList.contains(model)) {
            addItem(i, model);
        }
    }
}

private void applyAndAnimateMovedItems(List<ContactInfo> newContacts) {
    for (int toPosition = newContacts.size() - 1; toPosition >= 0; toPosition--) {
        final ContactInfo model = newContacts.get(toPosition);
        final int fromPosition = contactList.indexOf(model);
        if (fromPosition >= 0 && fromPosition != toPosition) {
            moveItem(fromPosition, toPosition);
        }
    }
}

public ContactInfo removeItem(int position) {
    final ContactInfo model = contactList.remove(position);
    notifyItemRemoved(position);
    return model;
}

public void addItem(int position, ContactInfo model) {
    contactList.add(position, model);
    notifyItemInserted(position);
}

public void moveItem(int fromPosition, int toPosition) {
    final ContactInfo model = contactList.remove(fromPosition);
    contactList.add(toPosition, model);
    notifyItemMoved(fromPosition, toPosition);
}

3)实现逻辑

最后,在 PhoneFragment 中,这是实现所有这些工作的实现。

public class PhoneFragment extends Fragment implements SearchView.OnQueryTextListener {

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.phone_layout, container, false);

    //query Searchview
    svContact.setOnQueryTextListener(this);

    //Recyclerview
    mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this.getActivity()));
    //Loads the calllog
    mLogAdapter = new LogAdapter(DisplayCallLog());
    mRecyclerView.setAdapter(mLogAdapter);

    //RecyclerView animation
    RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator();
    itemAnimator.setAddDuration(1000);
    itemAnimator.setRemoveDuration(1000);
    mRecyclerView.setItemAnimator(itemAnimator);

    return view;
}

注意:由于通话记录显示正确,我将避免在此处复制太长的 DisplayCallLog() 方法。

当在搜索 View 中输入文本时,我们定义新的适配器以实现在回收 View 中显示巧合的功能。

    @Override
public boolean onQueryTextChange(String query) {
    final List<ContactInfo> filteredModelList = filter(ContactsList(), query);
    mContactAdapter.animateTo(filteredModelList);
    mRecyclerView.scrollToPosition(0);
    return true;
}

private List<ContactInfo> filter(List<ContactInfo> contacts, String query) {
    query = query.toLowerCase();

    final List<ContactInfo> filteredModelList = new ArrayList<>();
    for (ContactInfo contact : contacts) {
        final String name = contact.getContactName().toLowerCase();
        final String number = contact.getContactNumber().toLowerCase();
        Log.i("FILTERED_QUERY", "name " + name + "/ number " + number);
        if (name.contains(query) || number.contains(query)) {
            filteredModelList.add(contact);
        }
    }
    return filteredModelList;
}

    private ArrayList<ContactInfo> ContactsList() {

    ArrayList<ContactInfo> contactsList = new ArrayList<ContactInfo>();
    int contactID = 0;
    String contactNumber = null;
    String contactName = null;
    ContactInfo cI;
    int resultLimit = 0;

    Cursor cursorContacts = getActivity().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            null, null, null, null);
    while (cursorContacts.moveToNext() && resultLimit<10) {
        contactID = cursorContacts.getInt(cursorContacts.getColumnIndexOrThrow(
                ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
        contactNumber = cursorContacts.getString(cursorContacts.getColumnIndexOrThrow(
                ContactsContract.CommonDataKinds.Phone.NUMBER));
        contactName = cursorContacts.getString(cursorContacts.getColumnIndexOrThrow(
                ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));

        cI = new ContactInfo();
        cI.id = contactID;
        cI.name = contactName;
        cI.number = contactNumber;
        Log.i("CONTACT_INFO", cI.toString());
        resultLimit++;
    }
    cursorContacts.close();
    return contactsList;
}

编辑 3 -> 新信息

我试过在启动时只显示联系人。所以,我应该看到一个联系人列表,而不是可视化日志。所以在 fragment 的 onCreateView 中我只是这样做:

mContactAdapter = new ContactAdapter(ContactsList());
mRecyclerView.setAdapter(mContactAdapter);

而不是我之前做的:

mLogAdapter = new LogAdapter(DisplayCallLog());
mRecyclerView.setAdapter(mLogAdapter);

这样,我只使用一个适配器,如果这是问题所在,它应该可以工作。但是没有用。所以问题一定与我获得联系人的方式有关(我说的是从联系人列表中获取联系人的原始列表,而不过滤它们),或者是 ContactAdapter 中的错误。但是这个适配器和 LogAdapter 几乎是一样的,所以我不知道......

最佳答案

我想我明白了。这是因为您使用了多个适配器来显示日志和调用数据的示例。所以首先,您需要做的是,它只使用单个适配器和不同的项目类型 View 。 RecyclerView 有针对不同 ViewHolder 的实现。

首先,尝试使用上面的示例,仅使用单个适配器和不同的项目类型。如果问题仍然存在,我稍后会使用您上面的资源提供示例。

更新!

因此,我希望您正确理解,使用多个适配器可能会在下次更新 View 时产生问题(事件这不是您问题的关键,导致 RecyclerView 缓存 ViewHolders)。并且没有必要在您的示例中使用多个 adaptes。另一件事,我不理解上面的所有代码。有关更多信息,需要调试您的所有项目...

但我还更新了可搜索示例(上面链接)的工作,以使用 Item ViewHolder 类型的几个示例和不同的搜索,下面是结果。无论如何,这是比使用多个适配器更好的解决方案。稍后我也会在 Github 中分享代码。

UPDATE GITHUB LINKS!

enter image description here enter image description here

关于android - 使用新适配器更新 recyclerview,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38142297/

相关文章:

android - 将 RecyclerView 与来自 LoaderManager 的数据一起使用

android - 在需要括号的方法之后,Kotlin 中尖括号的目的是什么?

android - 将图像从 drawable 设置为 recyclerview

使用 AudioRecord API 的 android 录音机

android - 元素使用权限#android.permission.CAMERA 与在 AndroidManifest.xml 中声明的元素重复

java - 将数据添加到我的 ArrayAdapter,我正在使用 RecyclerView 来显示此信息

java - 从 Arraylist 动态添加 View

java - 从自定义 DialogPreference 保存到 SharedPreferences

java - Android/Java 网络IO慢

android - Kotlin使用Gson反序列化本地json文件