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.app.Activity; 21import android.content.Context; 22import android.os.Bundle; 23import android.support.v4.view.MenuItemCompat; 24import android.support.v7.widget.DividerItemDecoration; 25import android.support.v7.widget.RecyclerView; 26import android.util.DisplayMetrics; 27import android.view.Menu; 28import android.view.MenuItem; 29import android.view.View; 30import android.view.ViewGroup; 31 32import com.example.android.supportv7.Cheeses; 33import com.example.android.supportv7.widget.adapter.SimpleStringAdapter; 34 35public class RecyclerViewActivity extends Activity { 36 37 private static final String TAG = "RecyclerViewActivity"; 38 39 private RecyclerView mRecyclerView; 40 41 @Override 42 protected void onCreate(Bundle savedInstanceState) { 43 super.onCreate(savedInstanceState); 44 45 final RecyclerView rv = new RecyclerView(this); 46 rv.setLayoutManager(new MyLayoutManager(this)); 47 rv.setHasFixedSize(true); 48 rv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 49 ViewGroup.LayoutParams.MATCH_PARENT)); 50 rv.setAdapter(new SimpleStringAdapter(this, Cheeses.sCheeseStrings) { 51 @Override 52 public SimpleStringAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, 53 int viewType) { 54 final SimpleStringAdapter.ViewHolder vh = super 55 .onCreateViewHolder(parent, viewType); 56 vh.itemView.setOnClickListener(new View.OnClickListener() { 57 @Override 58 public void onClick(View v) { 59 final int pos = vh.getAdapterPosition(); 60 if (pos == RecyclerView.NO_POSITION) { 61 return; 62 } 63 if (pos + 1 < getItemCount()) { 64 swap(pos, pos + 1); 65 } 66 } 67 }); 68 return vh; 69 } 70 }); 71 rv.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); 72 setContentView(rv); 73 mRecyclerView = rv; 74 } 75 76 @Override 77 public boolean onCreateOptionsMenu(Menu menu) { 78 super.onCreateOptionsMenu(menu); 79 MenuItemCompat.setShowAsAction(menu.add("Layout"), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); 80 return true; 81 } 82 83 @Override 84 public boolean onOptionsItemSelected(MenuItem item) { 85 mRecyclerView.requestLayout(); 86 return super.onOptionsItemSelected(item); 87 } 88 89 private static final int SCROLL_DISTANCE = 80; // dp 90 91 /** 92 * A basic ListView-style LayoutManager. 93 */ 94 class MyLayoutManager extends RecyclerView.LayoutManager { 95 96 private static final String TAG = "MyLayoutManager"; 97 98 private int mFirstPosition; 99 100 private final int mScrollDistance; 101 102 public MyLayoutManager(Context c) { 103 final DisplayMetrics dm = c.getResources().getDisplayMetrics(); 104 mScrollDistance = (int) (SCROLL_DISTANCE * dm.density + 0.5f); 105 } 106 107 @Override 108 public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 109 final int parentBottom = getHeight() - getPaddingBottom(); 110 final View oldTopView = getChildCount() > 0 ? getChildAt(0) : null; 111 int oldTop = getPaddingTop(); 112 if (oldTopView != null) { 113 oldTop = oldTopView.getTop(); 114 } 115 116 detachAndScrapAttachedViews(recycler); 117 118 int top = oldTop; 119 int bottom; 120 final int left = getPaddingLeft(); 121 final int right = getWidth() - getPaddingRight(); 122 123 final int count = state.getItemCount(); 124 for (int i = 0; mFirstPosition + i < count && top < parentBottom; i++, top = bottom) { 125 View v = recycler.getViewForPosition(mFirstPosition + i); 126 addView(v, i); 127 measureChildWithMargins(v, 0, 0); 128 bottom = top + getDecoratedMeasuredHeight(v); 129 layoutDecorated(v, left, top, right, bottom); 130 } 131 } 132 133 @Override 134 public RecyclerView.LayoutParams generateDefaultLayoutParams() { 135 return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 136 ViewGroup.LayoutParams.WRAP_CONTENT); 137 } 138 139 @Override 140 public boolean canScrollVertically() { 141 return true; 142 } 143 144 @Override 145 public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, 146 RecyclerView.State state) { 147 if (getChildCount() == 0) { 148 return 0; 149 } 150 151 int scrolled = 0; 152 final int left = getPaddingLeft(); 153 final int right = getWidth() - getPaddingRight(); 154 if (dy < 0) { 155 while (scrolled > dy) { 156 final View topView = getChildAt(0); 157 final int hangingTop = Math.max(-getDecoratedTop(topView), 0); 158 final int scrollBy = Math.min(scrolled - dy, hangingTop); 159 scrolled -= scrollBy; 160 offsetChildrenVertical(scrollBy); 161 if (mFirstPosition > 0 && scrolled > dy) { 162 mFirstPosition--; 163 View v = recycler.getViewForPosition(mFirstPosition); 164 addView(v, 0); 165 measureChildWithMargins(v, 0, 0); 166 final int bottom = getDecoratedTop(topView); 167 final int top = bottom - getDecoratedMeasuredHeight(v); 168 layoutDecorated(v, left, top, right, bottom); 169 } else { 170 break; 171 } 172 } 173 } else if (dy > 0) { 174 final int parentHeight = getHeight(); 175 while (scrolled < dy) { 176 final View bottomView = getChildAt(getChildCount() - 1); 177 final int hangingBottom = 178 Math.max(getDecoratedBottom(bottomView) - parentHeight, 0); 179 final int scrollBy = -Math.min(dy - scrolled, hangingBottom); 180 scrolled -= scrollBy; 181 offsetChildrenVertical(scrollBy); 182 if (scrolled < dy && state.getItemCount() > mFirstPosition + getChildCount()) { 183 View v = recycler.getViewForPosition(mFirstPosition + getChildCount()); 184 final int top = getDecoratedBottom(getChildAt(getChildCount() - 1)); 185 addView(v); 186 measureChildWithMargins(v, 0, 0); 187 final int bottom = top + getDecoratedMeasuredHeight(v); 188 layoutDecorated(v, left, top, right, bottom); 189 } else { 190 break; 191 } 192 } 193 } 194 recycleViewsOutOfBounds(recycler); 195 return scrolled; 196 } 197 198 @Override 199 public View onFocusSearchFailed(View focused, int direction, 200 RecyclerView.Recycler recycler, RecyclerView.State state) { 201 final int oldCount = getChildCount(); 202 203 if (oldCount == 0) { 204 return null; 205 } 206 207 final int left = getPaddingLeft(); 208 final int right = getWidth() - getPaddingRight(); 209 210 View toFocus = null; 211 int newViewsHeight = 0; 212 if (direction == View.FOCUS_UP || direction == View.FOCUS_BACKWARD) { 213 while (mFirstPosition > 0 && newViewsHeight < mScrollDistance) { 214 mFirstPosition--; 215 View v = recycler.getViewForPosition(mFirstPosition); 216 final int bottom = getDecoratedTop(getChildAt(0)); 217 addView(v, 0); 218 measureChildWithMargins(v, 0, 0); 219 final int top = bottom - getDecoratedMeasuredHeight(v); 220 layoutDecorated(v, left, top, right, bottom); 221 if (v.isFocusable()) { 222 toFocus = v; 223 break; 224 } 225 } 226 } 227 if (direction == View.FOCUS_DOWN || direction == View.FOCUS_FORWARD) { 228 while (mFirstPosition + getChildCount() < state.getItemCount() && 229 newViewsHeight < mScrollDistance) { 230 View v = recycler.getViewForPosition(mFirstPosition + getChildCount()); 231 final int top = getDecoratedBottom(getChildAt(getChildCount() - 1)); 232 addView(v); 233 measureChildWithMargins(v, 0, 0); 234 final int bottom = top + getDecoratedMeasuredHeight(v); 235 layoutDecorated(v, left, top, right, bottom); 236 if (v.isFocusable()) { 237 toFocus = v; 238 break; 239 } 240 } 241 } 242 243 return toFocus; 244 } 245 246 public void recycleViewsOutOfBounds(RecyclerView.Recycler recycler) { 247 final int childCount = getChildCount(); 248 final int parentWidth = getWidth(); 249 final int parentHeight = getHeight(); 250 boolean foundFirst = false; 251 int first = 0; 252 int last = 0; 253 for (int i = 0; i < childCount; i++) { 254 final View v = getChildAt(i); 255 if (v.hasFocus() || (getDecoratedRight(v) >= 0 && 256 getDecoratedLeft(v) <= parentWidth && 257 getDecoratedBottom(v) >= 0 && 258 getDecoratedTop(v) <= parentHeight)) { 259 if (!foundFirst) { 260 first = i; 261 foundFirst = true; 262 } 263 last = i; 264 } 265 } 266 for (int i = childCount - 1; i > last; i--) { 267 removeAndRecycleViewAt(i, recycler); 268 } 269 for (int i = first - 1; i >= 0; i--) { 270 removeAndRecycleViewAt(i, recycler); 271 } 272 if (getChildCount() == 0) { 273 mFirstPosition = 0; 274 } else { 275 mFirstPosition += first; 276 } 277 } 278 } 279} 280