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