Home | History | Annotate | Download | only in widget
      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 
     18 package com.example.android.supportv7.widget;
     19 
     20 import android.app.Activity;
     21 import android.content.Context;
     22 import android.os.Bundle;
     23 import android.util.DisplayMetrics;
     24 import android.view.Menu;
     25 import android.view.MenuItem;
     26 import android.view.View;
     27 import android.view.ViewGroup;
     28 
     29 import androidx.recyclerview.widget.DividerItemDecoration;
     30 import androidx.recyclerview.widget.RecyclerView;
     31 
     32 import com.example.android.supportv7.Cheeses;
     33 import com.example.android.supportv7.widget.adapter.SimpleStringAdapter;
     34 
     35 public 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         menu.add("Layout").setShowAsAction(MenuItem.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