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.android.supportv7.util; 17 18 import android.os.AsyncTask; 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.annotation.Nullable; 27 import androidx.appcompat.app.AppCompatActivity; 28 import androidx.core.util.Pair; 29 import androidx.recyclerview.widget.DiffUtil; 30 import androidx.recyclerview.widget.LinearLayoutManager; 31 import androidx.recyclerview.widget.RecyclerView; 32 33 import com.example.android.supportv7.Cheeses; 34 import com.example.android.supportv7.widget.adapter.SimpleStringAdapter; 35 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.List; 39 import java.util.Random; 40 import java.util.concurrent.atomic.AtomicBoolean; 41 42 /** 43 * A sample activity that demonstrates usage if {@link android.support.v7.util.DiffUtil} with 44 * a RecyclerView. 45 */ 46 public class DiffUtilActivity extends AppCompatActivity { 47 private Random mRandom = new Random(System.nanoTime()); 48 49 @Override onCreate(@ullable Bundle savedInstanceState)50 protected void onCreate(@Nullable Bundle savedInstanceState) { 51 super.onCreate(savedInstanceState); 52 LinearLayout ll = new LinearLayout(this); 53 RecyclerView rv = new RecyclerView(this); 54 Button shuffle = new Button(this); 55 shuffle.setText("Shuffle"); 56 ll.addView(shuffle); 57 ll.addView(rv); 58 rv.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 59 ViewGroup.LayoutParams.MATCH_PARENT)); 60 rv.setLayoutManager(new LinearLayoutManager(this)); 61 List<String> cheeseList = createRandomCheeseList(Collections.<String>emptyList(), 50); 62 final SimpleStringAdapter adapter = 63 new SimpleStringAdapter(this, cheeseList.toArray(new String[cheeseList.size()])); 64 rv.setAdapter(adapter); 65 final AtomicBoolean refreshingList = new AtomicBoolean(false); 66 shuffle.setOnClickListener(new View.OnClickListener() { 67 @Override 68 public void onClick(View view) { 69 if (refreshingList.getAndSet(true)) { 70 // already refreshing, do not allow modifications 71 return; 72 } 73 //noinspection unchecked 74 new AsyncTask<List<String>, Void, Pair<List<String>, DiffUtil.DiffResult>>() { 75 76 @Override 77 protected Pair<List<String>, DiffUtil.DiffResult> doInBackground( 78 List<String>... lists) { 79 List<String> oldList = lists[0]; 80 List<String> newList = createRandomCheeseList(oldList, 5); 81 DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff( 82 new MyCallback(oldList, newList)); 83 //noinspection unchecked 84 return new Pair(newList, diffResult); 85 } 86 87 @Override 88 protected void onPostExecute( 89 Pair<List<String>, DiffUtil.DiffResult> resultPair) { 90 refreshingList.set(false); 91 adapter.setValues(resultPair.first); 92 resultPair.second.dispatchUpdatesTo(adapter); 93 Toast.makeText(DiffUtilActivity.this, "new list size " 94 + resultPair.first.size(), Toast.LENGTH_SHORT).show(); 95 } 96 }.execute(adapter.getValues()); 97 98 } 99 }); 100 setContentView(ll); 101 } 102 103 private static class MyCallback extends DiffUtil.Callback { 104 private final List<String> mOld; 105 private final List<String> mNew; 106 MyCallback(List<String> old, List<String> aNew)107 public MyCallback(List<String> old, List<String> aNew) { 108 mOld = old; 109 mNew = aNew; 110 } 111 112 @Override getOldListSize()113 public int getOldListSize() { 114 return mOld.size(); 115 } 116 117 @Override getNewListSize()118 public int getNewListSize() { 119 return mNew.size(); 120 } 121 122 @Override areItemsTheSame(int oldItemPosition, int newItemPosition)123 public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { 124 // for strings, content equality is the same as identitiy equality since we don't have 125 // duplicates in this sample. 126 return mOld.get(oldItemPosition).equals(mNew.get(newItemPosition)); 127 } 128 129 @Override areContentsTheSame(int oldItemPosition, int newItemPosition)130 public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { 131 return mOld.get(oldItemPosition).equals(mNew.get(newItemPosition)); 132 } 133 } 134 createRandomCheeseList(List<String> seed, int iterations)135 private List<String> createRandomCheeseList(List<String> seed, int iterations) { 136 List<String> output = new ArrayList<>(); 137 output.addAll(seed); 138 for (int i = 0; i < iterations; i++) { 139 switch (mRandom.nextInt(3)) { 140 case 0: //add 141 output.add(mRandom.nextInt(1 + output.size()), getRandomCheese(output)); 142 break; 143 case 1: // remove 144 if (output.size() > 0) { 145 output.remove(mRandom.nextInt(output.size())); 146 } 147 break; 148 case 2: // move 149 if (output.size() > 0) { 150 int from = mRandom.nextInt(output.size()); 151 int to = mRandom.nextInt(output.size()); 152 output.add(to, output.remove(from)); 153 } 154 break; 155 } 156 } 157 return output; 158 } 159 getRandomCheese(List<String> excludes)160 private String getRandomCheese(List<String> excludes) { 161 String chosen = Cheeses.sCheeseStrings[mRandom.nextInt(Cheeses.sCheeseStrings.length)]; 162 while (excludes.contains(chosen)) { 163 chosen = Cheeses.sCheeseStrings[mRandom.nextInt(Cheeses.sCheeseStrings.length)]; 164 } 165 return chosen; 166 } 167 } 168