当搜索栏中的文本写入Recyclerview android时,调用notifyItemRangechanged
每当用户在当搜索栏中的文本写入Recyclerview android时,调用notifyItemRangechanged,android,animation,android-recyclerview,searchview,notify,Android,Animation,Android Recyclerview,Searchview,Notify,每当用户在搜索栏中键入内容时,我想在我的回收视图上添加动画。我已经实现了一个Filter方法来过滤项目,但未能在OnQuerytextChanged中调用notifyItemrangechanged,因此出现。我已经尝试过这样的方法: @Override public boolean onQueryTextChange(String newText) { istyping = true;
搜索栏中键入内容时,我想在我的回收视图
上添加动画
。我已经实现了一个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;
}
@覆盖
公共布尔onQueryTextChange(字符串newText){
istyping=true;
ArrayList templist=新的ArrayList();
mSearchQuery=newText;
//此行-->adapter.notifyItemRangeChanged(0,namelistwithnumber.size());每次创建新适配器时,这意味着没有以前的数据…这反过来意味着不能有任何动画;-)
您需要做的是在适配器中引用的活动中创建一个ArrayList
然后在发布的方法中修改ArrayList中的项,然后调用adapter.notifyItemRangeChanged(0,list.size());
即
。。。
private ArrayList list=new ArrayList();
专用MyRecycleServiceAdapter适配器;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
...
//创建适配器并引用ArrayList
适配器=新的MyRecycleServiceAdapter(MainActivity.this,列表);
...
@凌驾
公共布尔onQueryTextChange(字符串newText){
istyping=true;
ArrayList templist=新的ArrayList();
mSearchQuery=newText;
for(字符串温度:namelistwithnumber){
if(temp.toLowerCase().contains(newText.toLowerCase())){
圣堂武士。添加(临时);
}
}
//无需设置新适配器,适配器已具有对ArrayList的引用
//所以只需修改它,并告诉适配器它已更改,动画将被处理
//自动地
list.clear();
list.addAll(圣殿骑士);
adapter.notifyItemRangeChanged(0,list.size());
...
每次创建新适配器时,这意味着没有以前的数据…这反过来意味着不可能有任何动画;-)
您需要做的是在适配器中引用的活动中创建一个ArrayList
然后在发布的方法中修改ArrayList中的项,然后调用adapter.notifyItemRangeChanged(0,list.size());
即
。。。
private ArrayList list=new ArrayList();
专用MyRecycleServiceAdapter适配器;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
...
//创建适配器并引用ArrayList
适配器=新的MyRecycleServiceAdapter(MainActivity.this,列表);
...
@凌驾
公共布尔onQueryTextChange(字符串newText){
istyping=true;
ArrayList templist=新的ArrayList();
mSearchQuery=newText;
for(字符串温度:namelistwithnumber){
if(temp.toLowerCase().contains(newText.toLowerCase())){
圣堂武士。添加(临时);
}
}
//无需设置新适配器,适配器已具有对ArrayList的引用
//所以只需修改它,并告诉适配器它已更改,动画将被处理
//自动地
list.clear();
list.addAll(圣殿骑士);
adapter.notifyItemRangeChanged(0,list.size());
...
这是一个完整的工作示例。我从示例列表中查询单词,但您可以从db或web API中进行查询。我对recyclerView使用了固定的高度,以便可以看到所有动画。这里有很多概念在起作用:MVVM设计模式、LiveData、数据绑定等。要获得最佳结果,没有简单的答案。如果不熟悉的话有了这些概念,我们就可以一个接一个地研究它们
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);
});
}
}
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;
}
}
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());
}
};
}
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;
}
}
<?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>
<?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>
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'
}
MainViewModel.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);
});
}
}
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;
}
}
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());
}
};
}
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;
}
}
<?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>
<?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>
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'
}
活动\u main.xml:
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);
});
}
}
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;
}
}
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());
}
};
}
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;
}
}
<?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>
<?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>
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'
}
这是一个完整的工作示例。我从示例列表中查询单词,但您可以从db或web API中进行查询。我对recyclerView使用了固定的高度,以便可以看到所有动画。这里有很多概念在工作:MVVM设计模式、LiveData、数据绑定等。要获得最佳结果,没有简单的答案。如果不熟悉ese概念会逐一进行研究
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);
});
}
}
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;
}
}
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());
}
};
}
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;
}
}
<?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>
<?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>
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'
}
MainViewModel.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);
});
}
}
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;
}
}
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());
}
};
}
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;
}
}
<?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>
<?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>
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'
}
活动\u main.xml:
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);
});
}
}
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;
}
}
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());
}
};
}
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;
}
}
<?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>
<?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>
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'
}
新浪的回答似乎很好,它有很好的实践(除了冗余数据绑定),但既然您不接受它,我想您不想用这些附加概念重写代码。下面是一个简单的解决方案:
- 将
setHassTableId(true)
设置为适配器
- 覆盖适配器内部的getItemId(内部位置)
将DefaultItemAnimator()
添加到RecyclerView
分步指南:
1.将默认项Animator添加到回收视图中
mainlist.setItemAnimator(new DefaultItemAnimator());
2.向适配器添加更新列表的功能
您不应该每次更改项目列表时都重新初始化您的MyRecycleServiceAdapter
。从构造函数中删除列表!而是在dapter类中声明成员varialbeList nameListWithNumber=new ArrayList();
,并向适配器添加函数:
public void setItems(List<NameListwithNumber> newItems){
nameListWithNumber.clear();
nameListWithNumber.addAll(newItems);
notifydatasetChanged();
}
(我不确定为什么需要适配器tho中的活动实例)
并在onCreate()方法中初始化它
myAdapter = new MyRecyclerViewAdapter(MainActivity.this);
myAdapter.setHasStableIds(true);
mainList.setAdapter(myAdapter);
4.覆盖适配器内部的getItemId(int位置)
在适配器内部添加以下功能:
@Override
public long getItemId(int position) {
return nameListWithNumber.get(position).id;
}
如果您的模型没有唯一id,只需使用返回nameListWithNumber.get(position).hashCode();
最后
在您的公共布尔onQueryTextChange(字符串newText)
你需要改变
if (newText.isEmpty()) {
adapter.setItems(nameList)
istyping = false;
}
else {
adapter.setItems(tempList)
noresults.setVisibility(templist.size() == 0 ? View.VISIBLE : View.INVISIBLE)
}
这是完全相同的方法的结果,只是过滤方式不同
新浪的答案似乎不错,它有很好的做法(除了redund)