当搜索栏中的文本写入 Recyclerview 时调用 notifyItemRangechanged android

Call notifyItemRangechanged when text in searchbar is written Recyclerview android

每当用户在 SearchBar 中键入内容时,我想在我的 RecyclerView 上添加一个 animation。我已经实现了一个 Filter 方法来过滤项目,但是在 OnQuerytextChanged 中调用 notifyItemrangechanged 失败,所以出现了。我已经尝试过这样的事情:

@Override
                public boolean onQueryTextChange(String newText) {


                    istyping = true;

                    ArrayList<String> templist = new ArrayList<>();

                    mSearchQuery = newText;

 //this line -->   adapter.notifyItemRangeChanged(0, namelistwithnumber.size());    <---



                    for (String temp : namelistwithnumber) {
                        if (temp.toLowerCase().contains(newText.toLowerCase())) {
                            templist.add(temp);
                        }
                    }

                    if (newText.isEmpty()){
                        mainlist.setAdapter(null);
                        adapter = new MyRecyclerViewAdapter(MainActivity.this, namelist);
                        mainlist.setAdapter(adapter);
                        adapter.notifyDataSetChanged();
                        istyping = false;
                    }

                    if (templist.size() == 0) {
                        mainlist.setAdapter(null);
                        noresults.setVisibility(View.VISIBLE);
                    } else {
                        if (!newText.isEmpty()){
                          adapter = new MyRecyclerViewAdapter(MainActivity.this, templist);
                            mainlist.setAdapter(adapter);
                            noresults.setVisibility(View.INVISIBLE);
                            adapter.setClickListener(MainActivity.this);

                        }
                        noresults.setVisibility(View.INVISIBLE);
                    }

                    return true;
                }

这就是我想要实现的:

感谢任何帮助!!

你每次都创建一个新的适配器,这意味着没有以前的数据......这反过来意味着不能有任何动画;-)

您需要做的是在 Activity 中创建一个 ArrayList,在 Adapter

中引用它

然后在方法中 post 修改 ArrayList 中的项目然后调用 adapter.notifyItemRangeChanged(0, list.size()); 即

...
private ArrayList<String> list = new ArrayList<>();
private MyRecyclerViewAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    //create your adapter and reference the ArrayList
    adapter = new MyRecyclerViewAdapter(MainActivity.this, list);
   ...


@Override
public boolean onQueryTextChange(String newText) {

   istyping = true;
   ArrayList<String> templist = new ArrayList<>();
   mSearchQuery = newText;
   for (String temp : namelistwithnumber) {
     if (temp.toLowerCase().contains(newText.toLowerCase())) {
       templist.add(temp);
     }
   }
  //no need to set a new Adapter, the Adapter already has the ref to the ArrayList 
  //so just modify it and tell the adapter it has changed animation will be handled 
  //automatically
  list.clear();
  list.addAll(templist);
  adapter.notifyItemRangeChanged(0, list.size());
  ...

这是一个完整的工作示例。我从示例列表中查询了单词,但您可以从数据库或 Web API 中进行查询。我为 recyclerView 使用了一个固定的高度,这样所有的动画都会被看到。这里有很多概念在起作用:MVVM 设计模式、LiveData、DataBinding 等。为了获得最佳结果,没有简单的答案。如果不熟悉这些概念,请一一研究。

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;
    private SearchAdapter searchAdapter;
    private MainViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        viewModel = ViewModelProviders.of(this).get(MainViewModel.class);

        searchAdapter = new SearchAdapter();
        binding.recycler.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));
        binding.recycler.setAdapter(searchAdapter);

        observeData();

        viewModel.queryWord("");

        binding.search.setOnQueryTextListener((new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                viewModel.queryWord(query);
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                viewModel.queryWord(newText);
                return false;
            }
        }));
    }

    private void observeData() {
        viewModel.getResult().observe(this, result -> {
            searchAdapter.submitList(result);
        });
    }
}

MainViewModel.java:

public class MainViewModel extends AndroidViewModel {

    private ArrayList<String> namelistwithnumber;

    MutableLiveData<List<SearchResult>> result = new MutableLiveData<>();

    public MainViewModel(@NonNull Application application) {
        super(application);
        namelistwithnumber = new ArrayList(Arrays.asList("aa", "ab", "ac", "ad", "ba", "bb", "bc", "bd", "ca", "cb", "cc", "cd", "da", "db", "dc", "dd"));
    }

    public void queryWord(String word) {
        ArrayList<SearchResult> templist = new ArrayList<>();
        int id = 0;

        if (word.equals("")) {
            for (String temp : namelistwithnumber) {
                id++;
                SearchResult anItem = new SearchResult(id, temp);
                templist.add(anItem);
            }
        } else {
            for (String temp : namelistwithnumber) {
                if (temp.toLowerCase().contains(word.toLowerCase())) {
                    id++;
                    SearchResult anItem = new SearchResult(id, temp);
                    templist.add(anItem);
                }
            }
        }
        result.setValue(templist);
    }

    public MutableLiveData<List<SearchResult>> getResult() {
        return result;
    }
}

SearchAdapter.java:

public class SearchAdapter extends ListAdapter<SearchResult, SearchAdapter.ViewHolder> {

    private LayoutInflater mInflater;
    private Context context;

    public SearchAdapter() {
        super(DIFF_CALLBACK);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        context = parent.getContext();
        this.mInflater = LayoutInflater.from(context);
        ResultItemBinding binding = ResultItemBinding.inflate(mInflater, parent, false);
        return new ViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        SearchResult searchResult= getItem(position);
        holder.bind(searchResult);
    }


    public static class ViewHolder extends RecyclerView.ViewHolder {
        ResultItemBinding binding;

        ViewHolder(ResultItemBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
        }

        void bind(SearchResult item) {
            binding.setResult(item);
        }
    }


    public static final DiffUtil.ItemCallback<SearchResult> DIFF_CALLBACK =
            new DiffUtil.ItemCallback<SearchResult>() {
                @Override
                public boolean areItemsTheSame(
                        @NonNull SearchResult oldUser, @NonNull SearchResult newUser) {
                    // User properties may have changed if reloaded from the DB, but ID is fixed
                    return oldUser.getWord().equals(newUser.getWord());
                }

                @Override
                public boolean areContentsTheSame(
                        @NonNull SearchResult oldUser, @NonNull SearchResult newUser) {
                    // NOTE: if you use equals, your object must properly override Object#equals()
                    // Incorrectly returning false here will result in too many animations.
                    return oldUser.getWord().equals(newUser.getWord());
                }
            };
}

SearchResult.java:

public class SearchResult {
    private long id;
    private String word;

    public SearchResult(long id, String word) {
        this.id = id;
        this.word = word;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <androidx.appcompat.widget.SearchView
                android:id="@+id/search"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recycler"
                android:layout_width="match_parent"
                android:layout_height="600dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/search" />


        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.core.widget.NestedScrollView>

</layout>

result_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="result"
            type="com.example.recyclerviewtest.SearchResult" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp">

        <TextView
            android:id="@+id/word"
            android:layout_width="match_parent"
            android:layout_height="25dp"
            android:gravity="start|center_vertical"
            android:maxLines="1"
            android:text="@{result.word}"
            android:textColor="#000"
            android:textSize="16sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28

    defaultConfig {
        applicationId "com.example.recyclerviewtest"
        minSdkVersion 28
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildFeatures {
        dataBinding true
        // for view binding:
        // viewBinding true
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'org.jetbrains:annotations-java5:15.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
}

新浪的回答看起来不错,它有很好的做法(除了冗余数据绑定),但既然你不接受它,我假设你不想用那些额外的概念重写你的代码。所以这是简单的解决方案:

  • setHasStableIds(true)设置为适配器
  • 覆盖 getItemId(int position) 内部适配器
  • DefaultItemAnimator() 添加到 RecyclerView

分步指南:

1.添加默认的 itemAnimator 到你的 RecyclerView

mainlist.setItemAnimator(new DefaultItemAnimator());

2。向适配器添加功能以更新列表

您不应在每次更改项目列表时都重新初始化 MyRecyclerViewAdapter。从构造函数中删除列表!而是在你的 dapter class 中声明成员 varialbe List<NameListwithNumber> nameListWithNumber = new ArrayList<>(); 并向你的适配器添加一个函数:

public void setItems(List<NameListwithNumber> newItems){
nameListWithNumber.clear();
nameListWithNumber.addAll(newItems);
notifydatasetChanged();
}

3。初始化适配器并设置 setHasStableIds(true)

在您的 Activity 中将您的适配器声明为全局变量

private MyRecyclerViewAdapter  myAdapter;

(我不确定你为什么需要适配器内的 activity 实例)

并在 onCreate() 方法中初始化它

 myAdapter = new MyRecyclerViewAdapter(MainActivity.this);
 myAdapter.setHasStableIds(true);
 mainList.setAdapter(myAdapter);

4。覆盖 getItemId(int position) inside adapter

在您的适配器中添加此功能:

@Override
public long getItemId(int position) {
   return nameListWithNumber.get(position).id;
}

如果您的模型没有 唯一 ID,只需使用 return nameListWithNumber.get(position).hashCode();


终于 在你的 public boolean onQueryTextChange(String newText) 里面 你需要改变

  if (newText.isEmpty()) {
       adapter.setItems(nameList)
       istyping = false;
    }
  else {
    adapter.setItems(tempList)
    noresults.setVisibility(templist.size() == 0 ? View.VISIBLE : View.INVISIBLE)
}

这是完全相同方法的结果,只是过滤方式不同。