RecyclerViewActivity.java revision 90e0922d7d57ad748b919dfe21c63f03987227e7
1/* 2 * Copyright (C) 2013 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 18package com.example.android.supportv7.widget; 19 20import android.R; 21import android.app.Activity; 22import android.content.Context; 23import android.graphics.Rect; 24import android.os.Bundle; 25import android.support.v4.view.MenuItemCompat; 26import android.support.v7.widget.RecyclerView; 27import android.util.DisplayMetrics; 28import android.util.TypedValue; 29import android.view.Menu; 30import android.view.MenuItem; 31import android.view.View; 32import android.view.ViewGroup; 33import android.view.View.MeasureSpec; 34import android.widget.TextView; 35import com.example.android.supportv7.Cheeses; 36 37import java.util.ArrayList; 38import java.util.Collections; 39 40public class RecyclerViewActivity extends Activity { 41 private RecyclerView mRecyclerView; 42 43 @Override 44 protected void onCreate(Bundle savedInstanceState) { 45 super.onCreate(savedInstanceState); 46 47 final RecyclerView rv = new RecyclerView(this); 48 rv.setLayoutManager(new MyLayoutManager(this)); 49 rv.setHasFixedSize(true); 50 rv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 51 ViewGroup.LayoutParams.MATCH_PARENT)); 52 rv.setAdapter(new MyAdapter(Cheeses.sCheeseStrings)); 53 setContentView(rv); 54 55 mRecyclerView = rv; 56 } 57 58 @Override 59 public boolean onCreateOptionsMenu(Menu menu) { 60 super.onCreateOptionsMenu(menu); 61 MenuItemCompat.setShowAsAction(menu.add("Layout"), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); 62 return true; 63 } 64 65 @Override 66 public boolean onOptionsItemSelected(MenuItem item) { 67 mRecyclerView.requestLayout(); 68 return super.onOptionsItemSelected(item); 69 } 70 71 private static final int SCROLL_DISTANCE = 80; // dp 72 73 /** 74 * A basic ListView-style LayoutManager. 75 */ 76 class MyLayoutManager extends RecyclerView.LayoutManager { 77 private static final String TAG = "MyLayoutManager"; 78 private int mFirstPosition; 79 private final int mScrollDistance; 80 81 public MyLayoutManager(Context c) { 82 final DisplayMetrics dm = c.getResources().getDisplayMetrics(); 83 mScrollDistance = (int) (SCROLL_DISTANCE * dm.density + 0.5f); 84 } 85 86 @Override 87 public void layoutChildren(RecyclerView.Adapter adapter, RecyclerView.Recycler recycler) { 88 final int parentBottom = getHeight() - getPaddingBottom(); 89 90 final View oldTopView = getChildCount() > 0 ? getChildAt(0) : null; 91 int oldTop = getPaddingTop(); 92 if (oldTopView != null) { 93 oldTop = oldTopView.getTop(); 94 } 95 96 recycler.scrapAllViewsAttached(); 97 98 int top = oldTop; 99 int bottom; 100 final int left = getPaddingLeft(); 101 final int right = getWidth() - getPaddingRight(); 102 103 final int count = adapter.getItemCount(); 104 for (int i = 0; mFirstPosition + i < count && top < parentBottom; i++, top = bottom) { 105 View v = recycler.getViewForPosition(adapter, mFirstPosition + i); 106 addView(v, i); 107 v.measure(MeasureSpec.makeMeasureSpec(right - left, MeasureSpec.EXACTLY), 108 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 109 bottom = top + v.getMeasuredHeight(); 110 v.layout(left, top, right, bottom); 111 } 112 113 recycler.detachDirtyScrapViews(); 114 } 115 116 @Override 117 public RecyclerView.LayoutParams generateDefaultLayoutParams() { 118 return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 119 ViewGroup.LayoutParams.WRAP_CONTENT); 120 } 121 122 @Override 123 public boolean canScrollVertically() { 124 return true; 125 } 126 127 @Override 128 public int scrollVerticallyBy(int dy, RecyclerView.Adapter adapter, 129 RecyclerView.Recycler recycler) { 130 if (getChildCount() == 0) { 131 return 0; 132 } 133 134 int scrolled = 0; 135 final int left = getPaddingLeft(); 136 final int right = getWidth() - getPaddingRight(); 137 if (dy < 0) { 138 while (scrolled > dy) { 139 final View topView = getChildAt(0); 140 final int hangingTop = Math.max(-topView.getTop(), 0); 141 final int scrollBy = Math.min(scrolled - dy, hangingTop); 142 scrolled -= scrollBy; 143 offsetChildrenVertical(scrollBy); 144 if (mFirstPosition > 0 && scrolled > dy) { 145 mFirstPosition--; 146 View v = recycler.getViewForPosition(adapter, mFirstPosition); 147 addView(v, 0); 148 v.measure(MeasureSpec.makeMeasureSpec(right - left, MeasureSpec.EXACTLY), 149 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 150 final int bottom = topView.getTop(); // TODO decorated top? 151 final int top = bottom - v.getMeasuredHeight(); 152 v.layout(left, top, right, bottom); 153 } else { 154 break; 155 } 156 } 157 } else if (dy > 0) { 158 final int parentHeight = getHeight(); 159 while (scrolled < dy) { 160 final View bottomView = getChildAt(getChildCount() - 1); 161 final int hangingBottom = Math.max(bottomView.getBottom() - parentHeight, 0); 162 final int scrollBy = -Math.min(dy - scrolled, hangingBottom); 163 scrolled -= scrollBy; 164 offsetChildrenVertical(scrollBy); 165 if (scrolled < dy && getItemCount() > mFirstPosition + getChildCount()) { 166 View v = recycler.getViewForPosition(adapter, 167 mFirstPosition + getChildCount()); 168 final int top = getChildAt(getChildCount() - 1).getBottom(); 169 addView(v); 170 v.measure(MeasureSpec.makeMeasureSpec(right - left, MeasureSpec.EXACTLY), 171 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 172 final int bottom = top + v.getMeasuredHeight(); 173 v.layout(left, top, right, bottom); 174 } else { 175 break; 176 } 177 } 178 } 179 detachAndScrapViewsOutOfBounds(recycler); 180 return scrolled; 181 } 182 183 @Override 184 public View onFocusSearchFailed(View focused, int direction, 185 RecyclerView.Adapter adapter, RecyclerView.Recycler recycler) { 186 final int oldCount = getChildCount(); 187 188 if (oldCount == 0) { 189 return null; 190 } 191 192 final int left = getPaddingLeft(); 193 final int right = getWidth() - getPaddingRight(); 194 195 View toFocus = null; 196 int newViewsHeight = 0; 197 if (direction == View.FOCUS_UP || direction == View.FOCUS_BACKWARD) { 198 while (mFirstPosition > 0 && newViewsHeight < mScrollDistance) { 199 mFirstPosition--; 200 View v = recycler.getViewForPosition(adapter, mFirstPosition); 201 final int bottom = getChildAt(0).getTop(); // TODO decorated top? 202 addView(v, 0); 203 v.measure(MeasureSpec.makeMeasureSpec(right - left, MeasureSpec.EXACTLY), 204 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 205 final int top = bottom - v.getMeasuredHeight(); 206 v.layout(left, top, right, bottom); 207 if (v.isFocusable()) { 208 toFocus = v; 209 break; 210 } 211 } 212 } 213 if (direction == View.FOCUS_DOWN || direction == View.FOCUS_FORWARD) { 214 while (mFirstPosition + getChildCount() < getItemCount() && 215 newViewsHeight < mScrollDistance) { 216 View v = recycler.getViewForPosition(adapter, mFirstPosition + getChildCount()); 217 final int top = getChildAt(getChildCount() - 1).getBottom(); 218 addView(v); 219 v.measure(MeasureSpec.makeMeasureSpec(right - left, MeasureSpec.EXACTLY), 220 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 221 final int bottom = top + v.getMeasuredHeight(); 222 v.layout(left, top, right, bottom); 223 if (v.isFocusable()) { 224 toFocus = v; 225 break; 226 } 227 } 228 } 229 230 return toFocus; 231 } 232 233 public void detachAndScrapViewsOutOfBounds(RecyclerView.Recycler recycler) { 234 final int childCount = getChildCount(); 235 final int parentWidth = getWidth(); 236 final int parentHeight = getHeight(); 237 boolean foundFirst = false; 238 int first = 0; 239 int last = 0; 240 for (int i = 0; i < childCount; i++) { 241 final View v = getChildAt(i); 242 if (v.hasFocus() || (v.getRight() >= 0 && v.getLeft() <= parentWidth && 243 v.getBottom() >= 0 && v.getTop() <= parentHeight)) { 244 if (!foundFirst) { 245 first = i; 246 foundFirst = true; 247 } 248 last = i; 249 } 250 } 251 for (int i = childCount - 1; i > last; i--) { 252 recycler.detachAndScrapView(getChildAt(i)); 253 } 254 for (int i = first - 1; i >= 0; i--) { 255 recycler.detachAndScrapView(getChildAt(i)); 256 } 257 if (getChildCount() == 0) { 258 mFirstPosition = 0; 259 } else { 260 mFirstPosition += first; 261 } 262 } 263 } 264 265 class MyAdapter extends RecyclerView.Adapter { 266 private int mBackground; 267 private ArrayList<String> mValues; 268 269 public MyAdapter(String[] strings) { 270 TypedValue val = new TypedValue(); 271 RecyclerViewActivity.this.getTheme().resolveAttribute( 272 R.attr.selectableItemBackground, val, true); 273 mBackground = val.resourceId; 274 mValues = new ArrayList<String>(); 275 Collections.addAll(mValues, strings); 276 } 277 278 @Override 279 public RecyclerView.ViewHolder createViewHolder(ViewGroup parent, int viewType) { 280 final ViewHolder h = new ViewHolder(new TextView(RecyclerViewActivity.this)); 281 h.textView.setMinimumHeight(128); 282 h.textView.setFocusable(true); 283 h.textView.setBackgroundResource(mBackground); 284 h.textView.setOnClickListener(new View.OnClickListener() { 285 @Override 286 public void onClick(View v) { 287 final int pos = h.getPosition(); 288 if (mValues.size() > pos + 1) { 289 final String t = mValues.get(pos); 290 mValues.set(pos, mValues.get(pos + 1)); 291 mValues.set(pos + 1, t); 292 notifyDataSetChanged(); 293 } 294 } 295 }); 296 return h; 297 } 298 299 @Override 300 public void bindViewHolder(RecyclerView.ViewHolder holder, int position) { 301 ((ViewHolder) holder).textView.setText(mValues.get(position)); 302 } 303 304 @Override 305 public int getItemCount() { 306 return mValues.size(); 307 } 308 } 309 310 static class ViewHolder extends RecyclerView.ViewHolder { 311 public TextView textView; 312 313 public ViewHolder(TextView v) { 314 super(v); 315 textView = v; 316 } 317 } 318} 319