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