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