1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.example.androidx.util; 17 18 import android.annotation.SuppressLint; 19 import android.os.Bundle; 20 import android.view.View; 21 import android.view.ViewGroup; 22 import android.widget.Button; 23 import android.widget.LinearLayout; 24 import android.widget.Toast; 25 26 import androidx.appcompat.app.AppCompatActivity; 27 import androidx.core.util.Pair; 28 import androidx.recyclerview.widget.DiffUtil; 29 import androidx.recyclerview.widget.LinearLayoutManager; 30 import androidx.recyclerview.widget.RecyclerView; 31 32 import com.example.androidx.Cheeses; 33 import com.example.androidx.widget.adapter.SimpleStringAdapter; 34 35 import org.jspecify.annotations.Nullable; 36 37 import java.util.ArrayList; 38 import java.util.Collections; 39 import java.util.List; 40 import java.util.Random; 41 import java.util.concurrent.atomic.AtomicBoolean; 42 43 /** 44 * A sample activity that demonstrates usage if {@link androidx.recyclerview.widget.DiffUtil} with 45 * a RecyclerView. 46 */ 47 public class DiffUtilActivity extends AppCompatActivity { 48 private Random mRandom = new Random(System.nanoTime()); 49 50 @Override 51 @SuppressLint("WrongThread") 52 @SuppressWarnings("deprecation") /* AsyncTask */ onCreate(@ullable Bundle savedInstanceState)53 protected void onCreate(@Nullable Bundle savedInstanceState) { 54 super.onCreate(savedInstanceState); 55 LinearLayout ll = new LinearLayout(this); 56 RecyclerView rv = new RecyclerView(this); 57 Button shuffle = new Button(this); 58 shuffle.setText("Shuffle"); 59 ll.addView(shuffle); 60 ll.addView(rv); 61 rv.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 62 ViewGroup.LayoutParams.MATCH_PARENT)); 63 rv.setLayoutManager(new LinearLayoutManager(this)); 64 List<String> cheeseList = createRandomCheeseList(Collections.<String>emptyList(), 50); 65 final SimpleStringAdapter adapter = 66 new SimpleStringAdapter(this, cheeseList.toArray(new String[cheeseList.size()])); 67 rv.setAdapter(adapter); 68 final AtomicBoolean refreshingList = new AtomicBoolean(false); 69 shuffle.setOnClickListener(new View.OnClickListener() { 70 @Override 71 public void onClick(View view) { 72 if (refreshingList.getAndSet(true)) { 73 // already refreshing, do not allow modifications 74 return; 75 } 76 new android.os.AsyncTask<List<String>, Void, Pair<List<String>, 77 DiffUtil.DiffResult>>() { 78 @Override 79 protected Pair<List<String>, DiffUtil.DiffResult> doInBackground( 80 List<String>... lists) { 81 List<String> oldList = lists[0]; 82 List<String> newList = createRandomCheeseList(oldList, 5); 83 DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff( 84 new MyCallback(oldList, newList)); 85 //noinspection unchecked 86 return new Pair(newList, diffResult); 87 } 88 89 @Override 90 protected void onPostExecute( 91 Pair<List<String>, DiffUtil.DiffResult> resultPair) { 92 refreshingList.set(false); 93 adapter.setValues(resultPair.first); 94 resultPair.second.dispatchUpdatesTo(adapter); 95 Toast.makeText(DiffUtilActivity.this, "new list size " 96 + resultPair.first.size(), Toast.LENGTH_SHORT).show(); 97 } 98 }.execute(adapter.getValues()); 99 100 } 101 }); 102 setContentView(ll); 103 } 104 105 private static class MyCallback extends DiffUtil.Callback { 106 private final List<String> mOld; 107 private final List<String> mNew; 108 MyCallback(List<String> old, List<String> aNew)109 public MyCallback(List<String> old, List<String> aNew) { 110 mOld = old; 111 mNew = aNew; 112 } 113 114 @Override getOldListSize()115 public int getOldListSize() { 116 return mOld.size(); 117 } 118 119 @Override getNewListSize()120 public int getNewListSize() { 121 return mNew.size(); 122 } 123 124 @Override areItemsTheSame(int oldItemPosition, int newItemPosition)125 public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { 126 // for strings, content equality is the same as identitiy equality since we don't have 127 // duplicates in this sample. 128 return mOld.get(oldItemPosition).equals(mNew.get(newItemPosition)); 129 } 130 131 @Override areContentsTheSame(int oldItemPosition, int newItemPosition)132 public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { 133 return mOld.get(oldItemPosition).equals(mNew.get(newItemPosition)); 134 } 135 } 136 createRandomCheeseList(List<String> seed, int iterations)137 private List<String> createRandomCheeseList(List<String> seed, int iterations) { 138 List<String> output = new ArrayList<>(); 139 output.addAll(seed); 140 for (int i = 0; i < iterations; i++) { 141 switch (mRandom.nextInt(3)) { 142 case 0: //add 143 output.add(mRandom.nextInt(1 + output.size()), getRandomCheese(output)); 144 break; 145 case 1: // remove 146 if (output.size() > 0) { 147 output.remove(mRandom.nextInt(output.size())); 148 } 149 break; 150 case 2: // move 151 if (output.size() > 0) { 152 int from = mRandom.nextInt(output.size()); 153 int to = mRandom.nextInt(output.size()); 154 output.add(to, output.remove(from)); 155 } 156 break; 157 } 158 } 159 return output; 160 } 161 getRandomCheese(List<String> excludes)162 private String getRandomCheese(List<String> excludes) { 163 String chosen = Cheeses.sCheeseStrings[mRandom.nextInt(Cheeses.sCheeseStrings.length)]; 164 while (excludes.contains(chosen)) { 165 chosen = Cheeses.sCheeseStrings[mRandom.nextInt(Cheeses.sCheeseStrings.length)]; 166 } 167 return chosen; 168 } 169 } 170