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 17 package com.example.androidx.widget; 18 19 import android.graphics.Color; 20 import android.os.Parcelable; 21 import android.view.LayoutInflater; 22 import android.view.ViewGroup; 23 import android.widget.TextView; 24 25 import androidx.core.view.ViewCompat; 26 import androidx.recyclerview.widget.DividerItemDecoration; 27 import androidx.recyclerview.widget.LinearLayoutManager; 28 import androidx.recyclerview.widget.RecyclerView; 29 30 import com.example.androidx.Cheeses; 31 import com.example.androidx.R; 32 import com.example.androidx.widget.util.ConfigToggle; 33 34 import org.jspecify.annotations.NonNull; 35 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 39 /** 40 * A sample nested RecyclerView activity. 41 */ 42 public class NestedRecyclerViewActivity extends BaseLayoutManagerActivity<LinearLayoutManager> { 43 44 @Override createLayoutManager()45 protected LinearLayoutManager createLayoutManager() { 46 return new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); 47 } 48 49 @Override onRecyclerViewInit(RecyclerView recyclerView)50 protected void onRecyclerViewInit(RecyclerView recyclerView) { 51 recyclerView.addItemDecoration( 52 new DividerItemDecoration(this, mLayoutManager.getOrientation())); 53 } 54 55 @Override createConfigToggles()56 protected ConfigToggle[] createConfigToggles() { 57 return new ConfigToggle[]{ 58 new ConfigToggle(this, R.string.checkbox_accessibility_custom_actions) { 59 @Override 60 public boolean isChecked() { 61 return ((OuterAdapter) mRecyclerView.getAdapter()) 62 .mShouldAddAccessibilityCustomAction; 63 } 64 65 @Override 66 public void onChange(boolean newValue) { 67 mRecyclerView.setAdapter( 68 new OuterAdapter(Cheeses.sCheeseStrings, newValue)); 69 } 70 } 71 }; 72 } 73 74 static class InnerAdapter extends RecyclerView.Adapter<InnerAdapter.ViewHolder> { 75 public static class ViewHolder extends RecyclerView.ViewHolder { 76 TextView mTextView; 77 78 public ViewHolder(TextView itemView) { 79 super(itemView); 80 mTextView = itemView; 81 } 82 } 83 84 private String[] mData; 85 86 InnerAdapter(String[] data) { 87 mData = data; 88 } 89 90 @Override 91 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 92 LayoutInflater inflater = LayoutInflater.from(parent.getContext()); 93 TextView textView = (TextView) inflater.inflate(R.layout.nested_item, parent, false); 94 textView.setMinimumWidth(300); 95 textView.setMinimumHeight(300); 96 return new ViewHolder(textView); 97 } 98 99 @Override 100 public void onBindViewHolder(ViewHolder holder, int position) { 101 holder.mTextView.setText(mData[position]); 102 boolean even = position % 2 == 0; 103 holder.mTextView.setBackgroundColor(even ? Color.TRANSPARENT : 0x2fffffff); 104 } 105 106 @Override 107 public int getItemCount() { 108 return mData.length; 109 } 110 } 111 112 static class OuterAdapter extends RecyclerView.Adapter<OuterAdapter.ViewHolder> { 113 public static class ViewHolder extends RecyclerView.ViewHolder { 114 private final RecyclerView mRecyclerView; 115 public ViewHolder(RecyclerView itemView) { 116 super(itemView); 117 mRecyclerView = itemView; 118 } 119 } 120 121 ArrayList<InnerAdapter> mAdapters = new ArrayList<>(); 122 ArrayList<Parcelable> mSavedStates = new ArrayList<>(); 123 RecyclerView.RecycledViewPool mSharedPool = new RecyclerView.RecycledViewPool(); 124 boolean mShouldAddAccessibilityCustomAction; 125 126 127 OuterAdapter(String[] data, boolean shouldAddAccessibilityCustomAction) { 128 mShouldAddAccessibilityCustomAction = shouldAddAccessibilityCustomAction; 129 int currentCharIndex = 0; 130 char currentChar = data[0].charAt(0); 131 for (int i = 1; i <= data.length; i++) { 132 if (i == data.length || data[i].charAt(0) != currentChar) { 133 mAdapters.add(new InnerAdapter( 134 Arrays.copyOfRange(data, currentCharIndex, i - 1))); 135 mSavedStates.add(null); 136 if (i < data.length) { 137 currentChar = data[i].charAt(0); 138 currentCharIndex = i; 139 } 140 } 141 } 142 } 143 144 @Override 145 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 146 RecyclerView rv = new RecyclerView(parent.getContext()); 147 if (mShouldAddAccessibilityCustomAction) { 148 ViewCompat.addAccessibilityAction(rv, 149 rv.getContext().getString(R.string.arbitrary_action_label), 150 (v, a) -> { 151 return true; 152 }); 153 } 154 rv.setLayoutManager(new LinearLayoutManager(parent.getContext(), 155 LinearLayoutManager.HORIZONTAL, false)); 156 rv.setRecycledViewPool(mSharedPool); 157 return new ViewHolder(rv); 158 } 159 160 @Override 161 public void onBindViewHolder(ViewHolder holder, int position) { 162 // Note: would be equally valid to replace adapter content instead of swapping adapter 163 holder.mRecyclerView.swapAdapter(mAdapters.get(position), true); 164 165 Parcelable savedState = mSavedStates.get(position); 166 if (savedState != null) { 167 holder.mRecyclerView.getLayoutManager().onRestoreInstanceState(savedState); 168 mSavedStates.set(position, null); 169 } 170 } 171 172 @Override 173 public void onViewRecycled(@NonNull ViewHolder holder) { 174 mSavedStates.set(holder.getBindingAdapterPosition(), 175 holder.mRecyclerView.getLayoutManager().onSaveInstanceState()); 176 } 177 178 @Override 179 public int getItemCount() { 180 return mAdapters.size(); 181 } 182 } 183 184 @Override 185 protected RecyclerView.Adapter createAdapter() { 186 return new OuterAdapter(Cheeses.sCheeseStrings, false); 187 } 188 } 189