├── .gitignore ├── LICENSE.txt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── example │ │ └── android │ │ └── recyclerplayground │ │ ├── DividerDecoration.java │ │ ├── GridDividerDecoration.java │ │ ├── InsetDecoration.java │ │ ├── MainActivity.java │ │ ├── NumberPickerDialog.java │ │ ├── adapters │ │ ├── GameItemView.java │ │ ├── SimpleAdapter.java │ │ └── SimpleStaggeredAdapter.java │ │ ├── fragments │ │ ├── FixedTwoWayFragment.java │ │ ├── HorizontalFragment.java │ │ ├── NavigationDrawerFragment.java │ │ ├── RecyclerFragment.java │ │ ├── VerticalFragment.java │ │ ├── VerticalGridFragment.java │ │ └── VerticalStaggeredGridFragment.java │ │ └── layout │ │ └── FixedGridLayoutManager.java │ └── res │ ├── drawable-hdpi │ └── drawer_shadow.9.png │ ├── drawable-mdpi │ └── drawer_shadow.9.png │ ├── drawable-xhdpi │ └── drawer_shadow.9.png │ ├── drawable-xxhdpi │ └── drawer_shadow.9.png │ ├── drawable │ └── background_game.xml │ ├── layout │ ├── activity_main.xml │ ├── fragment_navigation_drawer.xml │ ├── fragment_recycler.xml │ └── view_match_item.xml │ ├── menu │ └── grid_options.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/ 4 | *.iml 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Wireless Designs, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Android RecyclerView Examples 2 | ----------------------------- 3 | 4 | This repository contains examples for using the RecyclerView widget found in the Android Support Library. 5 | 6 | Disclaimer 7 | ---------- 8 | This repository contains sample code intended to demonstrate the capabilities of the RecyclerView layout manager APIs. It is not intended to be used as-is in applications as a library dependency, and will not be maintained as such. Bug fix contributions are welcome, but issues and feature requests will not be addressed. 9 | 10 | Example Contents 11 | ---------------- 12 | The following bits can be found in the main sample application: 13 | - Implementation of `LinearLayoutManager` and `GridLayoutManager` for vertical and horizontal scrolling. 14 | - Custom ItemDecorations 15 | - `InsetDecoration` - Create an inset margin on all child views. 16 | - `DividerDecoration` - Create an inset margin and draw dividers below vertical child views. 17 | - `GridDividerDecoration` - Create an inset margin an draw dividers along grid lines 18 | - Custom LayoutManager 19 | - `FixedGridLayoutManager` - Similar to `StaticGridLayoutManager`, but with a controllable column count. 20 | 21 | The following examples are incubating on the `experimental` branch (these mostly work, if you feel like living dangerously): 22 | - Custom LayoutManagers 23 | - `StaticGridLayoutManager` - 2D scrolling grid with variable column count based on data set. Window of visible (non-recycled) views is determined statically. 24 | - `DynamicGridLayoutManager` - 2D scrolling grid where window of visible views is determined dynamically. Results in fewer views in memory, but scrolling performance is questionable. 25 | 26 | License 27 | ------- 28 | 29 | The code supplied here is covered under the MIT Open Source License: 30 | 31 | Copyright (c) 2015 Wireless Designs, LLC 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 34 | 35 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.example.android.recyclerplayground" 9 | minSdkVersion 15 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:23.1.0' 25 | compile 'com.android.support:recyclerview-v7:23.1.0' 26 | } 27 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/davesmith/android-sdk-mac_86/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/DividerDecoration.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Rect; 7 | import android.graphics.drawable.Drawable; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.view.View; 10 | 11 | /** 12 | * ItemDecoration implementation that applies and inset margin 13 | * around each child of the RecyclerView. It also draws item dividers 14 | * that are expected from a vertical list implementation, such as 15 | * ListView. 16 | */ 17 | public class DividerDecoration extends RecyclerView.ItemDecoration { 18 | 19 | private static final int[] ATTRS = { android.R.attr.listDivider }; 20 | 21 | private Drawable mDivider; 22 | private int mInsets; 23 | 24 | public DividerDecoration(Context context) { 25 | TypedArray a = context.obtainStyledAttributes(ATTRS); 26 | mDivider = a.getDrawable(0); 27 | a.recycle(); 28 | 29 | mInsets = context.getResources().getDimensionPixelSize(R.dimen.card_insets); 30 | } 31 | 32 | @Override 33 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 34 | drawVertical(c, parent); 35 | } 36 | 37 | /** Draw dividers underneath each child view */ 38 | public void drawVertical(Canvas c, RecyclerView parent) { 39 | final int left = parent.getPaddingLeft(); 40 | final int right = parent.getWidth() - parent.getPaddingRight(); 41 | 42 | final int childCount = parent.getChildCount(); 43 | for (int i = 0; i < childCount; i++) { 44 | final View child = parent.getChildAt(i); 45 | final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 46 | .getLayoutParams(); 47 | final int top = child.getBottom() + params.bottomMargin + mInsets; 48 | final int bottom = top + mDivider.getIntrinsicHeight(); 49 | mDivider.setBounds(left, top, right, bottom); 50 | mDivider.draw(c); 51 | } 52 | } 53 | 54 | @Override 55 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 56 | //We can supply forced insets for each item view here in the Rect 57 | outRect.set(mInsets, mInsets, mInsets, mInsets); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/GridDividerDecoration.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Canvas; 6 | import android.graphics.Rect; 7 | import android.graphics.drawable.Drawable; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.view.View; 10 | 11 | /** 12 | * ItemDecoration implementation that applies and inset margin 13 | * around each child of the RecyclerView. It also draws item dividers 14 | * that are expected from a vertical list implementation, such as 15 | * ListView. 16 | */ 17 | public class GridDividerDecoration extends RecyclerView.ItemDecoration { 18 | 19 | private static final int[] ATTRS = { android.R.attr.listDivider }; 20 | 21 | private Drawable mDivider; 22 | private int mInsets; 23 | 24 | public GridDividerDecoration(Context context) { 25 | TypedArray a = context.obtainStyledAttributes(ATTRS); 26 | mDivider = a.getDrawable(0); 27 | a.recycle(); 28 | 29 | mInsets = context.getResources().getDimensionPixelSize(R.dimen.card_insets); 30 | } 31 | 32 | @Override 33 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 34 | drawVertical(c, parent); 35 | drawHorizontal(c, parent); 36 | } 37 | 38 | /** Draw dividers at each expected grid interval */ 39 | public void drawVertical(Canvas c, RecyclerView parent) { 40 | if (parent.getChildCount() == 0) return; 41 | 42 | final int childCount = parent.getChildCount(); 43 | 44 | for (int i = 0; i < childCount; i++) { 45 | final View child = parent.getChildAt(i); 46 | final RecyclerView.LayoutParams params = 47 | (RecyclerView.LayoutParams) child.getLayoutParams(); 48 | 49 | final int left = child.getLeft() - params.leftMargin - mInsets; 50 | final int right = child.getRight() + params.rightMargin + mInsets; 51 | final int top = child.getBottom() + params.bottomMargin + mInsets; 52 | final int bottom = top + mDivider.getIntrinsicHeight(); 53 | mDivider.setBounds(left, top, right, bottom); 54 | mDivider.draw(c); 55 | } 56 | } 57 | 58 | /** Draw dividers to the right of each child view */ 59 | public void drawHorizontal(Canvas c, RecyclerView parent) { 60 | final int childCount = parent.getChildCount(); 61 | 62 | for (int i = 0; i < childCount; i++) { 63 | final View child = parent.getChildAt(i); 64 | final RecyclerView.LayoutParams params = 65 | (RecyclerView.LayoutParams) child.getLayoutParams(); 66 | 67 | final int left = child.getRight() + params.rightMargin + mInsets; 68 | final int right = left + mDivider.getIntrinsicWidth(); 69 | final int top = child.getTop() - params.topMargin - mInsets; 70 | final int bottom = child.getBottom() + params.bottomMargin + mInsets; 71 | mDivider.setBounds(left, top, right, bottom); 72 | mDivider.draw(c); 73 | } 74 | } 75 | 76 | @Override 77 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 78 | //We can supply forced insets for each item view here in the Rect 79 | outRect.set(mInsets, mInsets, mInsets, mInsets); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/InsetDecoration.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground; 2 | 3 | import android.content.Context; 4 | import android.graphics.Rect; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.View; 7 | 8 | /** 9 | * ItemDecoration implementation that applies an inset margin 10 | * around each child of the RecyclerView. The inset value is controlled 11 | * by a dimension resource. 12 | */ 13 | public class InsetDecoration extends RecyclerView.ItemDecoration { 14 | 15 | private int mInsets; 16 | 17 | public InsetDecoration(Context context) { 18 | mInsets = context.getResources().getDimensionPixelSize(R.dimen.card_insets); 19 | } 20 | 21 | @Override 22 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 23 | //We can supply forced insets for each item view here in the Rect 24 | outRect.set(mInsets, mInsets, mInsets, mInsets); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentManager; 5 | import android.support.v4.app.FragmentTransaction; 6 | import android.support.v4.widget.DrawerLayout; 7 | import android.support.v7.app.ActionBar; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.view.Menu; 10 | import android.view.MenuItem; 11 | 12 | import com.example.android.recyclerplayground.fragments.FixedTwoWayFragment; 13 | import com.example.android.recyclerplayground.fragments.HorizontalFragment; 14 | import com.example.android.recyclerplayground.fragments.NavigationDrawerFragment; 15 | import com.example.android.recyclerplayground.fragments.VerticalFragment; 16 | import com.example.android.recyclerplayground.fragments.VerticalGridFragment; 17 | import com.example.android.recyclerplayground.fragments.VerticalStaggeredGridFragment; 18 | 19 | 20 | public class MainActivity extends AppCompatActivity implements 21 | NavigationDrawerFragment.NavigationDrawerCallbacks { 22 | 23 | /** 24 | * Fragment managing the behaviors, interactions and presentation of the navigation drawer. 25 | */ 26 | private NavigationDrawerFragment mNavigationDrawerFragment; 27 | 28 | /** 29 | * Used to store the last screen title. For use in {@link #restoreActionBar()}. 30 | */ 31 | private CharSequence mTitle; 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_main); 37 | 38 | mNavigationDrawerFragment = (NavigationDrawerFragment) 39 | getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); 40 | mTitle = getTitle(); 41 | 42 | // Set up the drawer. 43 | mNavigationDrawerFragment.setUp( 44 | R.id.navigation_drawer, 45 | (DrawerLayout) findViewById(R.id.drawer_layout)); 46 | } 47 | 48 | @Override 49 | public void onNavigationDrawerItemSelected(int position) { 50 | // update the main content by replacing fragments 51 | FragmentManager fragmentManager = getSupportFragmentManager(); 52 | FragmentTransaction ft = fragmentManager.beginTransaction(); 53 | switch (position) { 54 | case 0: 55 | ft.replace(R.id.container, VerticalFragment.newInstance()); 56 | break; 57 | case 1: 58 | ft.replace(R.id.container, HorizontalFragment.newInstance()); 59 | break; 60 | case 2: 61 | ft.replace(R.id.container, VerticalGridFragment.newInstance()); 62 | break; 63 | case 3: 64 | ft.replace(R.id.container, VerticalStaggeredGridFragment.newInstance()); 65 | break; 66 | case 4: 67 | ft.replace(R.id.container, FixedTwoWayFragment.newInstance()); 68 | break; 69 | default: 70 | //Do nothing 71 | break; 72 | } 73 | 74 | ft.commit(); 75 | } 76 | 77 | public void restoreActionBar() { 78 | ActionBar actionBar = getSupportActionBar(); 79 | actionBar.setDisplayShowTitleEnabled(true); 80 | actionBar.setTitle(mTitle); 81 | actionBar.setDisplayHomeAsUpEnabled(true); 82 | actionBar.setHomeButtonEnabled(true); 83 | } 84 | 85 | @Override 86 | public boolean onCreateOptionsMenu(Menu menu) { 87 | if (!mNavigationDrawerFragment.isDrawerOpen()) { 88 | // Only show items in the action bar relevant to this screen 89 | // if the drawer is not showing. Otherwise, let the drawer 90 | // decide what to show in the action bar. 91 | restoreActionBar(); 92 | return true; 93 | } 94 | return super.onCreateOptionsMenu(menu); 95 | } 96 | 97 | @Override 98 | public boolean onOptionsItemSelected(MenuItem item) { 99 | // Handle action bar item clicks here. The action bar will 100 | // automatically handle clicks on the Home/Up button, so long 101 | // as you specify a parent activity in AndroidManifest.xml. 102 | return super.onOptionsItemSelected(item); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/NumberPickerDialog.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.Context; 5 | import android.content.DialogInterface; 6 | import android.os.Bundle; 7 | import android.widget.NumberPicker; 8 | 9 | 10 | public class NumberPickerDialog extends AlertDialog implements DialogInterface.OnClickListener { 11 | 12 | public interface OnNumberSelectedListener { 13 | public void onNumberSelected(int value); 14 | } 15 | 16 | private NumberPicker mPicker; 17 | private OnNumberSelectedListener mNumberSelectedListener; 18 | 19 | public NumberPickerDialog(Context context) { 20 | super(context); 21 | mPicker = new NumberPicker(context); 22 | } 23 | 24 | protected NumberPickerDialog(Context context, int theme) { 25 | super(context, theme); 26 | } 27 | 28 | protected NumberPickerDialog(Context context, boolean cancelable, OnCancelListener cancelListener) { 29 | super(context, cancelable, cancelListener); 30 | } 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | setButton(BUTTON_NEGATIVE, getContext().getString(android.R.string.cancel), this); 35 | setButton(BUTTON_POSITIVE, getContext().getString(android.R.string.ok), this); 36 | setView(mPicker); 37 | 38 | //Install contents 39 | super.onCreate(savedInstanceState); 40 | } 41 | 42 | public void setOnNumberSelectedListener(OnNumberSelectedListener listener) { 43 | mNumberSelectedListener = listener; 44 | } 45 | 46 | public void setPickerRange(int minValue, int maxValue) { 47 | mPicker.setMinValue(minValue); 48 | mPicker.setMaxValue(maxValue); 49 | } 50 | 51 | @Override 52 | public void onClick(DialogInterface dialog, int which) { 53 | if (which == BUTTON_POSITIVE && mNumberSelectedListener != null) { 54 | mNumberSelectedListener.onNumberSelected(mPicker.getValue()); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/adapters/GameItemView.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground.adapters; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.GridLayout; 6 | import android.widget.TextView; 7 | 8 | import com.example.android.recyclerplayground.R; 9 | 10 | public class GameItemView extends GridLayout { 11 | 12 | private TextView mHomeScore, mAwayScore; 13 | 14 | public GameItemView(Context context) { 15 | super(context); 16 | } 17 | 18 | public GameItemView(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | } 21 | 22 | public GameItemView(Context context, AttributeSet attrs, int defStyleAttr) { 23 | super(context, attrs, defStyleAttr); 24 | } 25 | 26 | @Override 27 | protected void onFinishInflate() { 28 | super.onFinishInflate(); 29 | 30 | mHomeScore = (TextView) findViewById(R.id.text_score_home); 31 | mAwayScore = (TextView) findViewById(R.id.text_score_away); 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return mAwayScore.getText() + "v" + mHomeScore.getText() 37 | + ": " + getLeft() + "," + getTop() 38 | + ": " + getMeasuredWidth() + "x" + getMeasuredHeight(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/adapters/SimpleAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground.adapters; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.AdapterView; 8 | import android.widget.TextView; 9 | 10 | import com.example.android.recyclerplayground.R; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Random; 15 | 16 | public class SimpleAdapter extends RecyclerView.Adapter { 17 | 18 | private ArrayList mItems; 19 | 20 | private AdapterView.OnItemClickListener mOnItemClickListener; 21 | 22 | public SimpleAdapter() { 23 | mItems = new ArrayList(); 24 | } 25 | 26 | /* 27 | * A common adapter modification or reset mechanism. As with ListAdapter, 28 | * calling notifyDataSetChanged() will trigger the RecyclerView to update 29 | * the view. However, this method will not trigger any of the RecyclerView 30 | * animation features. 31 | */ 32 | public void setItemCount(int count) { 33 | mItems.clear(); 34 | mItems.addAll(generateDummyData(count)); 35 | 36 | notifyDataSetChanged(); 37 | } 38 | 39 | /* 40 | * Inserting a new item at the head of the list. This uses a specialized 41 | * RecyclerView method, notifyItemInserted(), to trigger any enabled item 42 | * animations in addition to updating the view. 43 | */ 44 | public void addItem(int position) { 45 | if (position > mItems.size()) return; 46 | 47 | mItems.add(position, generateDummyItem()); 48 | notifyItemInserted(position); 49 | } 50 | 51 | /* 52 | * Inserting a new item at the head of the list. This uses a specialized 53 | * RecyclerView method, notifyItemRemoved(), to trigger any enabled item 54 | * animations in addition to updating the view. 55 | */ 56 | public void removeItem(int position) { 57 | if (position >= mItems.size()) return; 58 | 59 | mItems.remove(position); 60 | notifyItemRemoved(position); 61 | } 62 | 63 | @Override 64 | public VerticalItemHolder onCreateViewHolder(ViewGroup container, int viewType) { 65 | LayoutInflater inflater = LayoutInflater.from(container.getContext()); 66 | View root = inflater.inflate(R.layout.view_match_item, container, false); 67 | 68 | return new VerticalItemHolder(root, this); 69 | } 70 | 71 | @Override 72 | public void onBindViewHolder(VerticalItemHolder itemHolder, int position) { 73 | GameItem item = mItems.get(position); 74 | 75 | itemHolder.setAwayScore(String.valueOf(item.awayScore)); 76 | itemHolder.setHomeScore(String.valueOf(item.homeScore)); 77 | 78 | itemHolder.setAwayName(item.awayTeam); 79 | itemHolder.setHomeName(item.homeTeam); 80 | } 81 | 82 | @Override 83 | public int getItemCount() { 84 | return mItems.size(); 85 | } 86 | 87 | public void setOnItemClickListener(AdapterView.OnItemClickListener onItemClickListener) { 88 | mOnItemClickListener = onItemClickListener; 89 | } 90 | 91 | private void onItemHolderClick(VerticalItemHolder itemHolder) { 92 | if (mOnItemClickListener != null) { 93 | mOnItemClickListener.onItemClick(null, itemHolder.itemView, 94 | itemHolder.getAdapterPosition(), itemHolder.getItemId()); 95 | } 96 | } 97 | 98 | public static class GameItem { 99 | public String homeTeam; 100 | public String awayTeam; 101 | public int homeScore; 102 | public int awayScore; 103 | 104 | public GameItem(String homeTeam, String awayTeam, int homeScore, int awayScore) { 105 | this.homeTeam = homeTeam; 106 | this.awayTeam = awayTeam; 107 | this.homeScore = homeScore; 108 | this.awayScore = awayScore; 109 | } 110 | } 111 | 112 | public static class VerticalItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 113 | private TextView mHomeScore, mAwayScore; 114 | private TextView mHomeName, mAwayName; 115 | 116 | private SimpleAdapter mAdapter; 117 | 118 | public VerticalItemHolder(View itemView, SimpleAdapter adapter) { 119 | super(itemView); 120 | itemView.setOnClickListener(this); 121 | 122 | mAdapter = adapter; 123 | 124 | mHomeScore = (TextView) itemView.findViewById(R.id.text_score_home); 125 | mAwayScore = (TextView) itemView.findViewById(R.id.text_score_away); 126 | mHomeName = (TextView) itemView.findViewById(R.id.text_team_home); 127 | mAwayName = (TextView) itemView.findViewById(R.id.text_team_away); 128 | } 129 | 130 | @Override 131 | public void onClick(View v) { 132 | mAdapter.onItemHolderClick(this); 133 | } 134 | 135 | public void setHomeScore(CharSequence homeScore) { 136 | mHomeScore.setText(homeScore); 137 | } 138 | 139 | public void setAwayScore(CharSequence awayScore) { 140 | mAwayScore.setText(awayScore); 141 | } 142 | 143 | public void setHomeName(CharSequence homeName) { 144 | mHomeName.setText(homeName); 145 | } 146 | 147 | public void setAwayName(CharSequence awayName) { 148 | mAwayName.setText(awayName); 149 | } 150 | } 151 | 152 | public static GameItem generateDummyItem() { 153 | Random random = new Random(); 154 | return new GameItem("Upset Home", "Upset Away", 155 | random.nextInt(100), 156 | random.nextInt(100) ); 157 | } 158 | 159 | public static List generateDummyData(int count) { 160 | ArrayList items = new ArrayList(); 161 | 162 | for (int i=0; i < count; i++) { 163 | items.add(new SimpleAdapter.GameItem("Losers", "Winners", i, i+5)); 164 | } 165 | 166 | return items; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/adapters/SimpleStaggeredAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground.adapters; 2 | 3 | import android.view.View; 4 | 5 | import com.example.android.recyclerplayground.R; 6 | 7 | public class SimpleStaggeredAdapter extends SimpleAdapter { 8 | 9 | @Override 10 | public void onBindViewHolder(VerticalItemHolder itemHolder, int position) { 11 | super.onBindViewHolder(itemHolder, position); 12 | 13 | final View itemView = itemHolder.itemView; 14 | if (position % 4 == 0) { 15 | int height = itemView.getContext().getResources() 16 | .getDimensionPixelSize(R.dimen.card_staggered_height); 17 | itemView.setMinimumHeight(height); 18 | } else { 19 | itemView.setMinimumHeight(0); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/fragments/FixedTwoWayFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.widget.RecyclerView; 5 | 6 | import com.example.android.recyclerplayground.InsetDecoration; 7 | import com.example.android.recyclerplayground.adapters.SimpleAdapter; 8 | import com.example.android.recyclerplayground.layout.FixedGridLayoutManager; 9 | 10 | 11 | public class FixedTwoWayFragment extends RecyclerFragment{ 12 | 13 | public static FixedTwoWayFragment newInstance() { 14 | FixedTwoWayFragment fragment = new FixedTwoWayFragment(); 15 | Bundle args = new Bundle(); 16 | fragment.setArguments(args); 17 | return fragment; 18 | } 19 | 20 | @Override 21 | protected RecyclerView.LayoutManager getLayoutManager() { 22 | FixedGridLayoutManager manager = new FixedGridLayoutManager(); 23 | manager.setTotalColumnCount(10); 24 | 25 | return manager; 26 | } 27 | 28 | @Override 29 | protected RecyclerView.ItemDecoration getItemDecoration() { 30 | return new InsetDecoration(getActivity()); 31 | } 32 | 33 | @Override 34 | protected int getDefaultItemCount() { 35 | return 5; 36 | } 37 | 38 | @Override 39 | protected SimpleAdapter getAdapter() { 40 | return new SimpleAdapter(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/fragments/HorizontalFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | 7 | import com.example.android.recyclerplayground.InsetDecoration; 8 | import com.example.android.recyclerplayground.adapters.SimpleAdapter; 9 | 10 | public class HorizontalFragment extends RecyclerFragment { 11 | 12 | public static HorizontalFragment newInstance() { 13 | HorizontalFragment fragment = new HorizontalFragment(); 14 | Bundle args = new Bundle(); 15 | fragment.setArguments(args); 16 | return fragment; 17 | } 18 | 19 | @Override 20 | protected RecyclerView.LayoutManager getLayoutManager() { 21 | return new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false); 22 | } 23 | 24 | @Override 25 | protected RecyclerView.ItemDecoration getItemDecoration() { 26 | return new InsetDecoration(getActivity()); 27 | } 28 | 29 | @Override 30 | protected int getDefaultItemCount() { 31 | return 40; 32 | } 33 | 34 | @Override 35 | protected SimpleAdapter getAdapter() { 36 | return new SimpleAdapter(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/fragments/NavigationDrawerFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground.fragments; 2 | 3 | 4 | import android.app.Activity; 5 | import android.content.SharedPreferences; 6 | import android.content.res.Configuration; 7 | import android.os.Bundle; 8 | import android.preference.PreferenceManager; 9 | import android.support.v4.app.Fragment; 10 | import android.support.v4.view.GravityCompat; 11 | import android.support.v4.widget.DrawerLayout; 12 | import android.support.v7.app.ActionBarDrawerToggle; 13 | import android.view.LayoutInflater; 14 | import android.view.MenuItem; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | import android.widget.AdapterView; 18 | import android.widget.ArrayAdapter; 19 | import android.widget.ListView; 20 | 21 | import com.example.android.recyclerplayground.R; 22 | 23 | /** 24 | * Fragment used for managing interactions for and presentation of a navigation drawer. 25 | * See the 26 | * design guidelines for a complete explanation of the behaviors implemented here. 27 | */ 28 | public class NavigationDrawerFragment extends Fragment { 29 | 30 | /** 31 | * Remember the position of the selected item. 32 | */ 33 | private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position"; 34 | 35 | /** 36 | * Per the design guidelines, you should show the drawer on launch until the user manually 37 | * expands it. This shared preference tracks this. 38 | */ 39 | private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; 40 | 41 | /** 42 | * A pointer to the current callbacks instance (the Activity). 43 | */ 44 | private NavigationDrawerCallbacks mCallbacks; 45 | 46 | /** 47 | * Helper component that ties the action bar to the navigation drawer. 48 | */ 49 | private ActionBarDrawerToggle mDrawerToggle; 50 | 51 | private DrawerLayout mDrawerLayout; 52 | private ListView mDrawerListView; 53 | private View mFragmentContainerView; 54 | 55 | private int mCurrentSelectedPosition = 0; 56 | private boolean mFromSavedInstanceState; 57 | private boolean mUserLearnedDrawer; 58 | 59 | public NavigationDrawerFragment() { 60 | } 61 | 62 | @Override 63 | public void onCreate(Bundle savedInstanceState) { 64 | super.onCreate(savedInstanceState); 65 | 66 | // Read in the flag indicating whether or not the user has demonstrated awareness of the 67 | // drawer. See PREF_USER_LEARNED_DRAWER for details. 68 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); 69 | mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false); 70 | 71 | if (savedInstanceState != null) { 72 | mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION); 73 | mFromSavedInstanceState = true; 74 | } 75 | 76 | // Select either the default item (0) or the last selected item. 77 | selectItem(mCurrentSelectedPosition); 78 | } 79 | 80 | @Override 81 | public void onActivityCreated (Bundle savedInstanceState) { 82 | super.onActivityCreated(savedInstanceState); 83 | // Indicate that this fragment would like to influence the set of actions in the action bar. 84 | setHasOptionsMenu(true); 85 | } 86 | 87 | @Override 88 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 89 | Bundle savedInstanceState) { 90 | mDrawerListView = (ListView) inflater.inflate( 91 | R.layout.fragment_navigation_drawer, container, false); 92 | mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 93 | @Override 94 | public void onItemClick(AdapterView parent, View view, int position, long id) { 95 | selectItem(position); 96 | } 97 | }); 98 | mDrawerListView.setAdapter(new ArrayAdapter( 99 | getActivity(), 100 | android.R.layout.simple_list_item_activated_1, 101 | android.R.id.text1, 102 | new String[]{ 103 | getString(R.string.title_section1), 104 | getString(R.string.title_section2), 105 | getString(R.string.title_section3), 106 | getString(R.string.title_section4), 107 | getString(R.string.title_section5), 108 | })); 109 | mDrawerListView.setItemChecked(mCurrentSelectedPosition, true); 110 | return mDrawerListView; 111 | } 112 | 113 | public boolean isDrawerOpen() { 114 | return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView); 115 | } 116 | 117 | /** 118 | * Users of this fragment must call this method to set up the navigation drawer interactions. 119 | * 120 | * @param fragmentId The android:id of this fragment in its activity's layout. 121 | * @param drawerLayout The DrawerLayout containing this fragment's UI. 122 | */ 123 | public void setUp(int fragmentId, DrawerLayout drawerLayout) { 124 | mFragmentContainerView = getActivity().findViewById(fragmentId); 125 | mDrawerLayout = drawerLayout; 126 | 127 | // set a custom shadow that overlays the main content when the drawer opens 128 | mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); 129 | // set up the drawer's list view with items and click listener 130 | 131 | // ActionBarDrawerToggle ties together the the proper interactions 132 | // between the navigation drawer and the action bar app icon. 133 | mDrawerToggle = new ActionBarDrawerToggle( 134 | getActivity(), /* host Activity */ 135 | mDrawerLayout, /* DrawerLayout object */ 136 | R.string.navigation_drawer_open, /* "open drawer" description for accessibility */ 137 | R.string.navigation_drawer_close /* "close drawer" description for accessibility */ 138 | ) { 139 | @Override 140 | public void onDrawerClosed(View drawerView) { 141 | super.onDrawerClosed(drawerView); 142 | if (!isAdded()) { 143 | return; 144 | } 145 | 146 | getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu() 147 | } 148 | 149 | @Override 150 | public void onDrawerOpened(View drawerView) { 151 | super.onDrawerOpened(drawerView); 152 | if (!isAdded()) { 153 | return; 154 | } 155 | 156 | if (!mUserLearnedDrawer) { 157 | // The user manually opened the drawer; store this flag to prevent auto-showing 158 | // the navigation drawer automatically in the future. 159 | mUserLearnedDrawer = true; 160 | SharedPreferences sp = PreferenceManager 161 | .getDefaultSharedPreferences(getActivity()); 162 | sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply(); 163 | } 164 | 165 | getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu() 166 | } 167 | }; 168 | 169 | // If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer, 170 | // per the navigation drawer design guidelines. 171 | if (!mUserLearnedDrawer && !mFromSavedInstanceState) { 172 | mDrawerLayout.openDrawer(mFragmentContainerView); 173 | } 174 | 175 | // Defer code dependent on restoration of previous instance state. 176 | mDrawerLayout.post(new Runnable() { 177 | @Override 178 | public void run() { 179 | mDrawerToggle.syncState(); 180 | } 181 | }); 182 | 183 | mDrawerLayout.setDrawerListener(mDrawerToggle); 184 | } 185 | 186 | private void selectItem(int position) { 187 | mCurrentSelectedPosition = position; 188 | if (mDrawerListView != null) { 189 | mDrawerListView.setItemChecked(position, true); 190 | } 191 | if (mDrawerLayout != null) { 192 | mDrawerLayout.closeDrawer(mFragmentContainerView); 193 | } 194 | if (mCallbacks != null) { 195 | mCallbacks.onNavigationDrawerItemSelected(position); 196 | } 197 | } 198 | 199 | @Override 200 | public void onAttach(Activity activity) { 201 | super.onAttach(activity); 202 | try { 203 | mCallbacks = (NavigationDrawerCallbacks) activity; 204 | } catch (ClassCastException e) { 205 | throw new ClassCastException("Activity must implement NavigationDrawerCallbacks."); 206 | } 207 | } 208 | 209 | @Override 210 | public void onDetach() { 211 | super.onDetach(); 212 | mCallbacks = null; 213 | } 214 | 215 | @Override 216 | public void onSaveInstanceState(Bundle outState) { 217 | super.onSaveInstanceState(outState); 218 | outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition); 219 | } 220 | 221 | @Override 222 | public void onConfigurationChanged(Configuration newConfig) { 223 | super.onConfigurationChanged(newConfig); 224 | // Forward the new configuration the drawer toggle component. 225 | mDrawerToggle.onConfigurationChanged(newConfig); 226 | } 227 | 228 | @Override 229 | public boolean onOptionsItemSelected(MenuItem item) { 230 | if (mDrawerToggle.onOptionsItemSelected(item)) { 231 | return true; 232 | } 233 | 234 | return super.onOptionsItemSelected(item); 235 | } 236 | 237 | /** 238 | * Callbacks interface that all activities using this fragment must implement. 239 | */ 240 | public static interface NavigationDrawerCallbacks { 241 | /** 242 | * Called when an item in the navigation drawer is selected. 243 | */ 244 | void onNavigationDrawerItemSelected(int position); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/fragments/RecyclerFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.Menu; 8 | import android.view.MenuInflater; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.AdapterView; 13 | import android.widget.Toast; 14 | 15 | import com.example.android.recyclerplayground.NumberPickerDialog; 16 | import com.example.android.recyclerplayground.R; 17 | import com.example.android.recyclerplayground.adapters.SimpleAdapter; 18 | 19 | public abstract class RecyclerFragment extends Fragment implements AdapterView.OnItemClickListener { 20 | 21 | private RecyclerView mList; 22 | private SimpleAdapter mAdapter; 23 | 24 | /** Required Overrides for Sample Fragments */ 25 | 26 | protected abstract RecyclerView.LayoutManager getLayoutManager(); 27 | protected abstract RecyclerView.ItemDecoration getItemDecoration(); 28 | protected abstract int getDefaultItemCount(); 29 | protected abstract SimpleAdapter getAdapter(); 30 | 31 | @Override 32 | public void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setHasOptionsMenu(true); 35 | } 36 | 37 | @Override 38 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 39 | Bundle savedInstanceState) { 40 | View rootView = inflater.inflate(R.layout.fragment_recycler, container, false); 41 | 42 | mList = (RecyclerView) rootView.findViewById(R.id.section_list); 43 | mList.setLayoutManager(getLayoutManager()); 44 | mList.addItemDecoration(getItemDecoration()); 45 | 46 | mList.getItemAnimator().setAddDuration(1000); 47 | mList.getItemAnimator().setChangeDuration(1000); 48 | mList.getItemAnimator().setMoveDuration(1000); 49 | mList.getItemAnimator().setRemoveDuration(1000); 50 | 51 | mAdapter = getAdapter(); 52 | mAdapter.setItemCount(getDefaultItemCount()); 53 | mAdapter.setOnItemClickListener(this); 54 | mList.setAdapter(mAdapter); 55 | 56 | return rootView; 57 | } 58 | 59 | @Override 60 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 61 | inflater.inflate(R.menu.grid_options, menu); 62 | } 63 | 64 | @Override 65 | public boolean onOptionsItemSelected(MenuItem item) { 66 | NumberPickerDialog dialog; 67 | switch (item.getItemId()) { 68 | case R.id.action_add: 69 | dialog = new NumberPickerDialog(getActivity()); 70 | dialog.setTitle("Position to Add"); 71 | dialog.setPickerRange(0, mAdapter.getItemCount()); 72 | dialog.setOnNumberSelectedListener(new NumberPickerDialog.OnNumberSelectedListener() { 73 | @Override 74 | public void onNumberSelected(int value) { 75 | mAdapter.addItem(value); 76 | } 77 | }); 78 | dialog.show(); 79 | 80 | return true; 81 | case R.id.action_remove: 82 | dialog = new NumberPickerDialog(getActivity()); 83 | dialog.setTitle("Position to Remove"); 84 | dialog.setPickerRange(0, mAdapter.getItemCount()-1); 85 | dialog.setOnNumberSelectedListener(new NumberPickerDialog.OnNumberSelectedListener() { 86 | @Override 87 | public void onNumberSelected(int value) { 88 | mAdapter.removeItem(value); 89 | } 90 | }); 91 | dialog.show(); 92 | 93 | return true; 94 | case R.id.action_empty: 95 | mAdapter.setItemCount(0); 96 | return true; 97 | case R.id.action_small: 98 | mAdapter.setItemCount(5); 99 | return true; 100 | case R.id.action_medium: 101 | mAdapter.setItemCount(25); 102 | return true; 103 | case R.id.action_large: 104 | mAdapter.setItemCount(196); 105 | return true; 106 | case R.id.action_scroll_zero: 107 | mList.scrollToPosition(0); 108 | return true; 109 | case R.id.action_smooth_zero: 110 | mList.smoothScrollToPosition(0); 111 | return true; 112 | default: 113 | return super.onOptionsItemSelected(item); 114 | } 115 | } 116 | 117 | @Override 118 | public void onItemClick(AdapterView parent, View view, int position, long id) { 119 | Toast.makeText(getActivity(), 120 | "Clicked: " + position + ", index " + mList.indexOfChild(view), 121 | Toast.LENGTH_SHORT).show(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/fragments/VerticalFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | 7 | import com.example.android.recyclerplayground.DividerDecoration; 8 | import com.example.android.recyclerplayground.adapters.SimpleAdapter; 9 | 10 | public class VerticalFragment extends RecyclerFragment { 11 | 12 | public static VerticalFragment newInstance() { 13 | VerticalFragment fragment = new VerticalFragment(); 14 | Bundle args = new Bundle(); 15 | fragment.setArguments(args); 16 | return fragment; 17 | } 18 | 19 | @Override 20 | protected RecyclerView.LayoutManager getLayoutManager() { 21 | return new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false); 22 | } 23 | 24 | @Override 25 | protected RecyclerView.ItemDecoration getItemDecoration() { 26 | //We must draw dividers ourselves if we want them in a list 27 | return new DividerDecoration(getActivity()); 28 | } 29 | 30 | @Override 31 | protected int getDefaultItemCount() { 32 | return 100; 33 | } 34 | 35 | @Override 36 | protected SimpleAdapter getAdapter() { 37 | return new SimpleAdapter(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/fragments/VerticalGridFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.widget.GridLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | 7 | import com.example.android.recyclerplayground.InsetDecoration; 8 | import com.example.android.recyclerplayground.adapters.SimpleAdapter; 9 | 10 | public class VerticalGridFragment extends RecyclerFragment { 11 | 12 | public static VerticalGridFragment newInstance() { 13 | VerticalGridFragment fragment = new VerticalGridFragment(); 14 | Bundle args = new Bundle(); 15 | fragment.setArguments(args); 16 | return fragment; 17 | } 18 | 19 | @Override 20 | protected RecyclerView.LayoutManager getLayoutManager() { 21 | return new GridLayoutManager(getActivity(), 2, GridLayoutManager.VERTICAL, false); 22 | } 23 | 24 | @Override 25 | protected RecyclerView.ItemDecoration getItemDecoration() { 26 | return new InsetDecoration(getActivity()); 27 | } 28 | 29 | @Override 30 | protected int getDefaultItemCount() { 31 | return 100; 32 | } 33 | 34 | @Override 35 | protected SimpleAdapter getAdapter() { 36 | return new SimpleAdapter(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/fragments/VerticalStaggeredGridFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.support.v7.widget.StaggeredGridLayoutManager; 6 | 7 | import com.example.android.recyclerplayground.InsetDecoration; 8 | import com.example.android.recyclerplayground.adapters.SimpleAdapter; 9 | import com.example.android.recyclerplayground.adapters.SimpleStaggeredAdapter; 10 | 11 | public class VerticalStaggeredGridFragment extends RecyclerFragment { 12 | 13 | public static VerticalStaggeredGridFragment newInstance() { 14 | VerticalStaggeredGridFragment fragment = new VerticalStaggeredGridFragment(); 15 | Bundle args = new Bundle(); 16 | fragment.setArguments(args); 17 | return fragment; 18 | } 19 | 20 | @Override 21 | protected RecyclerView.LayoutManager getLayoutManager() { 22 | return new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); 23 | } 24 | 25 | @Override 26 | protected RecyclerView.ItemDecoration getItemDecoration() { 27 | return new InsetDecoration(getActivity()); 28 | } 29 | 30 | @Override 31 | protected int getDefaultItemCount() { 32 | return 100; 33 | } 34 | 35 | @Override 36 | protected SimpleAdapter getAdapter() { 37 | return new SimpleStaggeredAdapter(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/recyclerplayground/layout/FixedGridLayoutManager.java: -------------------------------------------------------------------------------- 1 | package com.example.android.recyclerplayground.layout; 2 | 3 | import android.content.Context; 4 | import android.graphics.PointF; 5 | import android.support.v7.widget.LinearSmoothScroller; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.util.AttributeSet; 8 | import android.util.Log; 9 | import android.util.SparseArray; 10 | import android.util.SparseIntArray; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | import java.util.HashSet; 15 | import java.util.List; 16 | 17 | /** 18 | * A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation 19 | * that places children in a two-dimensional grid, sized to a fixed column count 20 | * value. User scrolling is possible in both horizontal and vertical directions 21 | * to view the data set. 22 | * 23 | *

The column count is controllable via {@link #setTotalColumnCount(int)}. The layout manager 24 | * will generate the number of rows necessary to accommodate the data set based on 25 | * the fixed column count. 26 | * 27 | *

This manager does make some assumptions to simplify the implementation: 28 | *

    29 | *
  • All child views are assumed to be the same size
  • 30 | *
  • The window of visible views is a constant
  • 31 | *
32 | */ 33 | public class FixedGridLayoutManager extends RecyclerView.LayoutManager { 34 | 35 | private static final String TAG = FixedGridLayoutManager.class.getSimpleName(); 36 | 37 | private static final int DEFAULT_COUNT = 1; 38 | 39 | /* View Removal Constants */ 40 | private static final int REMOVE_VISIBLE = 0; 41 | private static final int REMOVE_INVISIBLE = 1; 42 | 43 | /* Fill Direction Constants */ 44 | private static final int DIRECTION_NONE = -1; 45 | private static final int DIRECTION_START = 0; 46 | private static final int DIRECTION_END = 1; 47 | private static final int DIRECTION_UP = 2; 48 | private static final int DIRECTION_DOWN = 3; 49 | 50 | /* First (top-left) position visible at any point */ 51 | private int mFirstVisiblePosition; 52 | /* Consistent size applied to all child views */ 53 | private int mDecoratedChildWidth; 54 | private int mDecoratedChildHeight; 55 | /* Number of columns that exist in the grid */ 56 | private int mTotalColumnCount = DEFAULT_COUNT; 57 | /* Metrics for the visible window of our data */ 58 | private int mVisibleColumnCount; 59 | private int mVisibleRowCount; 60 | 61 | /* Used for tracking off-screen change events */ 62 | private int mFirstChangedPosition; 63 | private int mChangedPositionCount; 64 | 65 | /** 66 | * Set the number of columns the layout manager will use. This will 67 | * trigger a layout update. 68 | * @param count Number of columns. 69 | */ 70 | public void setTotalColumnCount(int count) { 71 | mTotalColumnCount = count; 72 | requestLayout(); 73 | } 74 | 75 | /* 76 | * You must return true from this method if you want your 77 | * LayoutManager to support anything beyond "simple" item 78 | * animations. Enabling this causes onLayoutChildren() to 79 | * be called twice on each animated change; once for a 80 | * pre-layout, and again for the real layout. 81 | */ 82 | @Override 83 | public boolean supportsPredictiveItemAnimations() { 84 | return true; 85 | } 86 | 87 | /* 88 | * Called by RecyclerView when a view removal is triggered. This is called 89 | * before onLayoutChildren() in pre-layout if the views removed are not visible. We 90 | * use it in this case to inform pre-layout that a removal took place. 91 | * 92 | * This method is still called if the views removed were visible, but it will 93 | * happen AFTER pre-layout. 94 | */ 95 | @Override 96 | public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) { 97 | mFirstChangedPosition = positionStart; 98 | mChangedPositionCount = itemCount; 99 | } 100 | 101 | /* 102 | * This method is your initial call from the framework. You will receive it when you 103 | * need to start laying out the initial set of views. This method will not be called 104 | * repeatedly, so don't rely on it to continually process changes during user 105 | * interaction. 106 | * 107 | * This method will be called when the data set in the adapter changes, so it can be 108 | * used to update a layout based on a new item count. 109 | * 110 | * If predictive animations are enabled, you will see this called twice. First, with 111 | * state.isPreLayout() returning true to lay out children in their initial conditions. 112 | * Then again to lay out children in their final locations. 113 | */ 114 | @Override 115 | public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { 116 | //We have nothing to show for an empty data set but clear any existing views 117 | if (getItemCount() == 0) { 118 | detachAndScrapAttachedViews(recycler); 119 | return; 120 | } 121 | if (getChildCount() == 0 && state.isPreLayout()) { 122 | //Nothing to do during prelayout when empty 123 | return; 124 | } 125 | 126 | //Clear change tracking state when a real layout occurs 127 | if (!state.isPreLayout()) { 128 | mFirstChangedPosition = mChangedPositionCount = 0; 129 | } 130 | 131 | if (getChildCount() == 0) { //First or empty layout 132 | //Scrap measure one child 133 | View scrap = recycler.getViewForPosition(0); 134 | addView(scrap); 135 | measureChildWithMargins(scrap, 0, 0); 136 | 137 | /* 138 | * We make some assumptions in this code based on every child 139 | * view being the same size (i.e. a uniform grid). This allows 140 | * us to compute the following values up front because they 141 | * won't change. 142 | */ 143 | mDecoratedChildWidth = getDecoratedMeasuredWidth(scrap); 144 | mDecoratedChildHeight = getDecoratedMeasuredHeight(scrap); 145 | 146 | detachAndScrapView(scrap, recycler); 147 | } 148 | 149 | //Always update the visible row/column counts 150 | updateWindowSizing(); 151 | 152 | SparseIntArray removedCache = null; 153 | /* 154 | * During pre-layout, we need to take note of any views that are 155 | * being removed in order to handle predictive animations 156 | */ 157 | if (state.isPreLayout()) { 158 | removedCache = new SparseIntArray(getChildCount()); 159 | for (int i=0; i < getChildCount(); i++) { 160 | final View view = getChildAt(i); 161 | LayoutParams lp = (LayoutParams) view.getLayoutParams(); 162 | 163 | if (lp.isItemRemoved()) { 164 | //Track these view removals as visible 165 | removedCache.put(lp.getViewLayoutPosition(), REMOVE_VISIBLE); 166 | } 167 | } 168 | 169 | //Track view removals that happened out of bounds (i.e. off-screen) 170 | if (removedCache.size() == 0 && mChangedPositionCount > 0) { 171 | for (int i = mFirstChangedPosition; i < (mFirstChangedPosition + mChangedPositionCount); i++) { 172 | removedCache.put(i, REMOVE_INVISIBLE); 173 | } 174 | } 175 | } 176 | 177 | 178 | int childLeft; 179 | int childTop; 180 | if (getChildCount() == 0) { //First or empty layout 181 | //Reset the visible and scroll positions 182 | mFirstVisiblePosition = 0; 183 | childLeft = getPaddingLeft(); 184 | childTop = getPaddingTop(); 185 | } else if (!state.isPreLayout() 186 | && getVisibleChildCount() >= state.getItemCount()) { 187 | //Data set is too small to scroll fully, just reset position 188 | mFirstVisiblePosition = 0; 189 | childLeft = getPaddingLeft(); 190 | childTop = getPaddingTop(); 191 | } else { //Adapter data set changes 192 | /* 193 | * Keep the existing initial position, and save off 194 | * the current scrolled offset. 195 | */ 196 | final View topChild = getChildAt(0); 197 | childLeft = getDecoratedLeft(topChild); 198 | childTop = getDecoratedTop(topChild); 199 | 200 | /* 201 | * When data set is too small to scroll vertically, adjust vertical offset 202 | * and shift position to the first row, preserving current column 203 | */ 204 | if (!state.isPreLayout() && getVerticalSpace() > (getTotalRowCount() * mDecoratedChildHeight)) { 205 | mFirstVisiblePosition = mFirstVisiblePosition % getTotalColumnCount(); 206 | childTop = getPaddingTop(); 207 | 208 | //If the shift overscrolls the column max, back it off 209 | if ((mFirstVisiblePosition + mVisibleColumnCount) > state.getItemCount()) { 210 | mFirstVisiblePosition = Math.max(state.getItemCount() - mVisibleColumnCount, 0); 211 | childLeft = getPaddingLeft(); 212 | } 213 | } 214 | 215 | /* 216 | * Adjust the visible position if out of bounds in the 217 | * new layout. This occurs when the new item count in an adapter 218 | * is much smaller than it was before, and you are scrolled to 219 | * a location where no items would exist. 220 | */ 221 | int maxFirstRow = getTotalRowCount() - (mVisibleRowCount-1); 222 | int maxFirstCol = getTotalColumnCount() - (mVisibleColumnCount-1); 223 | boolean isOutOfRowBounds = getFirstVisibleRow() > maxFirstRow; 224 | boolean isOutOfColBounds = getFirstVisibleColumn() > maxFirstCol; 225 | if (isOutOfRowBounds || isOutOfColBounds) { 226 | int firstRow; 227 | if (isOutOfRowBounds) { 228 | firstRow = maxFirstRow; 229 | } else { 230 | firstRow = getFirstVisibleRow(); 231 | } 232 | int firstCol; 233 | if (isOutOfColBounds) { 234 | firstCol = maxFirstCol; 235 | } else { 236 | firstCol = getFirstVisibleColumn(); 237 | } 238 | mFirstVisiblePosition = firstRow * getTotalColumnCount() + firstCol; 239 | 240 | childLeft = getHorizontalSpace() - (mDecoratedChildWidth * mVisibleColumnCount); 241 | childTop = getVerticalSpace() - (mDecoratedChildHeight * mVisibleRowCount); 242 | 243 | //Correct cases where shifting to the bottom-right overscrolls the top-left 244 | // This happens on data sets too small to scroll in a direction. 245 | if (getFirstVisibleRow() == 0) { 246 | childTop = Math.min(childTop, getPaddingTop()); 247 | } 248 | if (getFirstVisibleColumn() == 0) { 249 | childLeft = Math.min(childLeft, getPaddingLeft()); 250 | } 251 | } 252 | } 253 | 254 | //Clear all attached views into the recycle bin 255 | detachAndScrapAttachedViews(recycler); 256 | 257 | //Fill the grid for the initial layout of views 258 | fillGrid(DIRECTION_NONE, childLeft, childTop, recycler, state, removedCache); 259 | 260 | //Evaluate any disappearing views that may exist 261 | if (!state.isPreLayout() && !recycler.getScrapList().isEmpty()) { 262 | final List scrapList = recycler.getScrapList(); 263 | final HashSet disappearingViews = new HashSet(scrapList.size()); 264 | 265 | for (RecyclerView.ViewHolder holder : scrapList) { 266 | final View child = holder.itemView; 267 | final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 268 | if (!lp.isItemRemoved()) { 269 | disappearingViews.add(child); 270 | } 271 | } 272 | 273 | for (View child : disappearingViews) { 274 | layoutDisappearingView(child); 275 | } 276 | } 277 | } 278 | 279 | @Override 280 | public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) { 281 | //Completely scrap the existing layout 282 | removeAllViews(); 283 | } 284 | 285 | /* 286 | * Rather than continuously checking how many views we can fit 287 | * based on scroll offsets, we simplify the math by computing the 288 | * visible grid as what will initially fit on screen, plus one. 289 | */ 290 | private void updateWindowSizing() { 291 | mVisibleColumnCount = (getHorizontalSpace() / mDecoratedChildWidth) + 1; 292 | if (getHorizontalSpace() % mDecoratedChildWidth > 0) { 293 | mVisibleColumnCount++; 294 | } 295 | 296 | //Allow minimum value for small data sets 297 | if (mVisibleColumnCount > getTotalColumnCount()) { 298 | mVisibleColumnCount = getTotalColumnCount(); 299 | } 300 | 301 | 302 | mVisibleRowCount = (getVerticalSpace()/ mDecoratedChildHeight) + 1; 303 | if (getVerticalSpace() % mDecoratedChildHeight > 0) { 304 | mVisibleRowCount++; 305 | } 306 | 307 | if (mVisibleRowCount > getTotalRowCount()) { 308 | mVisibleRowCount = getTotalRowCount(); 309 | } 310 | } 311 | 312 | private void fillGrid(int direction, RecyclerView.Recycler recycler, RecyclerView.State state) { 313 | fillGrid(direction, 0, 0, recycler, state, null); 314 | } 315 | 316 | private void fillGrid(int direction, int emptyLeft, int emptyTop, 317 | RecyclerView.Recycler recycler, 318 | RecyclerView.State state, 319 | SparseIntArray removedPositions) { 320 | if (mFirstVisiblePosition < 0) mFirstVisiblePosition = 0; 321 | if (mFirstVisiblePosition >= getItemCount()) mFirstVisiblePosition = (getItemCount() - 1); 322 | 323 | /* 324 | * First, we will detach all existing views from the layout. 325 | * detachView() is a lightweight operation that we can use to 326 | * quickly reorder views without a full add/remove. 327 | */ 328 | SparseArray viewCache = new SparseArray(getChildCount()); 329 | int startLeftOffset = emptyLeft; 330 | int startTopOffset = emptyTop; 331 | if (getChildCount() != 0) { 332 | final View topView = getChildAt(0); 333 | startLeftOffset = getDecoratedLeft(topView); 334 | startTopOffset = getDecoratedTop(topView); 335 | switch (direction) { 336 | case DIRECTION_START: 337 | startLeftOffset -= mDecoratedChildWidth; 338 | break; 339 | case DIRECTION_END: 340 | startLeftOffset += mDecoratedChildWidth; 341 | break; 342 | case DIRECTION_UP: 343 | startTopOffset -= mDecoratedChildHeight; 344 | break; 345 | case DIRECTION_DOWN: 346 | startTopOffset += mDecoratedChildHeight; 347 | break; 348 | } 349 | 350 | //Cache all views by their existing position, before updating counts 351 | for (int i=0; i < getChildCount(); i++) { 352 | int position = positionOfIndex(i); 353 | final View child = getChildAt(i); 354 | viewCache.put(position, child); 355 | } 356 | 357 | //Temporarily detach all views. 358 | // Views we still need will be added back at the proper index. 359 | for (int i=0; i < viewCache.size(); i++) { 360 | detachView(viewCache.valueAt(i)); 361 | } 362 | } 363 | 364 | /* 365 | * Next, we advance the visible position based on the fill direction. 366 | * DIRECTION_NONE doesn't advance the position in any direction. 367 | */ 368 | switch (direction) { 369 | case DIRECTION_START: 370 | mFirstVisiblePosition--; 371 | break; 372 | case DIRECTION_END: 373 | mFirstVisiblePosition++; 374 | break; 375 | case DIRECTION_UP: 376 | mFirstVisiblePosition -= getTotalColumnCount(); 377 | break; 378 | case DIRECTION_DOWN: 379 | mFirstVisiblePosition += getTotalColumnCount(); 380 | break; 381 | } 382 | 383 | /* 384 | * Next, we supply the grid of items that are deemed visible. 385 | * If these items were previously there, they will simply be 386 | * re-attached. New views that must be created are obtained 387 | * from the Recycler and added. 388 | */ 389 | int leftOffset = startLeftOffset; 390 | int topOffset = startTopOffset; 391 | 392 | for (int i = 0; i < getVisibleChildCount(); i++) { 393 | int nextPosition = positionOfIndex(i); 394 | 395 | /* 396 | * When a removal happens out of bounds, the pre-layout positions of items 397 | * after the removal are shifted to their final positions ahead of schedule. 398 | * We have to track off-screen removals and shift those positions back 399 | * so we can properly lay out all current (and appearing) views in their 400 | * initial locations. 401 | */ 402 | int offsetPositionDelta = 0; 403 | if (state.isPreLayout()) { 404 | int offsetPosition = nextPosition; 405 | 406 | for (int offset = 0; offset < removedPositions.size(); offset++) { 407 | //Look for off-screen removals that are less-than this 408 | if (removedPositions.valueAt(offset) == REMOVE_INVISIBLE 409 | && removedPositions.keyAt(offset) < nextPosition) { 410 | //Offset position to match 411 | offsetPosition--; 412 | } 413 | } 414 | offsetPositionDelta = nextPosition - offsetPosition; 415 | nextPosition = offsetPosition; 416 | } 417 | 418 | if (nextPosition < 0 || nextPosition >= state.getItemCount()) { 419 | //Item space beyond the data set, don't attempt to add a view 420 | continue; 421 | } 422 | 423 | //Layout this position 424 | View view = viewCache.get(nextPosition); 425 | if (view == null) { 426 | /* 427 | * The Recycler will give us either a newly constructed view, 428 | * or a recycled view it has on-hand. In either case, the 429 | * view will already be fully bound to the data by the 430 | * adapter for us. 431 | */ 432 | view = recycler.getViewForPosition(nextPosition); 433 | addView(view); 434 | 435 | /* 436 | * Update the new view's metadata, but only when this is a real 437 | * layout pass. 438 | */ 439 | if (!state.isPreLayout()) { 440 | LayoutParams lp = (LayoutParams) view.getLayoutParams(); 441 | lp.row = getGlobalRowOfPosition(nextPosition); 442 | lp.column = getGlobalColumnOfPosition(nextPosition); 443 | } 444 | 445 | /* 446 | * It is prudent to measure/layout each new view we 447 | * receive from the Recycler. We don't have to do 448 | * this for views we are just re-arranging. 449 | */ 450 | measureChildWithMargins(view, 0, 0); 451 | layoutDecorated(view, leftOffset, topOffset, 452 | leftOffset + mDecoratedChildWidth, 453 | topOffset + mDecoratedChildHeight); 454 | 455 | } else { 456 | //Re-attach the cached view at its new index 457 | attachView(view); 458 | viewCache.remove(nextPosition); 459 | } 460 | 461 | if (i % mVisibleColumnCount == (mVisibleColumnCount - 1)) { 462 | leftOffset = startLeftOffset; 463 | topOffset += mDecoratedChildHeight; 464 | 465 | //During pre-layout, on each column end, apply any additional appearing views 466 | if (state.isPreLayout()) { 467 | layoutAppearingViews(recycler, view, nextPosition, removedPositions.size(), offsetPositionDelta); 468 | } 469 | } else { 470 | leftOffset += mDecoratedChildWidth; 471 | } 472 | } 473 | 474 | /* 475 | * Finally, we ask the Recycler to scrap and store any views 476 | * that we did not re-attach. These are views that are not currently 477 | * necessary because they are no longer visible. 478 | */ 479 | for (int i=0; i < viewCache.size(); i++) { 480 | final View removingView = viewCache.valueAt(i); 481 | recycler.recycleView(removingView); 482 | } 483 | } 484 | 485 | /* 486 | * You must override this method if you would like to support external calls 487 | * to shift the view to a given adapter position. In our implementation, this 488 | * is the same as doing a fresh layout with the given position as the top-left 489 | * (or first visible), so we simply set that value and trigger onLayoutChildren() 490 | */ 491 | @Override 492 | public void scrollToPosition(int position) { 493 | if (position >= getItemCount()) { 494 | Log.e(TAG, "Cannot scroll to "+position+", item count is "+getItemCount()); 495 | return; 496 | } 497 | 498 | //Set requested position as first visible 499 | mFirstVisiblePosition = position; 500 | //Toss all existing views away 501 | removeAllViews(); 502 | //Trigger a new view layout 503 | requestLayout(); 504 | } 505 | 506 | /* 507 | * You must override this method if you would like to support external calls 508 | * to animate a change to a new adapter position. The framework provides a 509 | * helper scroller implementation (LinearSmoothScroller), which we leverage 510 | * to do the animation calculations. 511 | */ 512 | @Override 513 | public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, final int position) { 514 | if (position >= getItemCount()) { 515 | Log.e(TAG, "Cannot scroll to "+position+", item count is "+getItemCount()); 516 | return; 517 | } 518 | 519 | /* 520 | * LinearSmoothScroller's default behavior is to scroll the contents until 521 | * the child is fully visible. It will snap to the top-left or bottom-right 522 | * of the parent depending on whether the direction of travel was positive 523 | * or negative. 524 | */ 525 | LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) { 526 | /* 527 | * LinearSmoothScroller, at a minimum, just need to know the vector 528 | * (x/y distance) to travel in order to get from the current positioning 529 | * to the target. 530 | */ 531 | @Override 532 | public PointF computeScrollVectorForPosition(int targetPosition) { 533 | final int rowOffset = getGlobalRowOfPosition(targetPosition) 534 | - getGlobalRowOfPosition(mFirstVisiblePosition); 535 | final int columnOffset = getGlobalColumnOfPosition(targetPosition) 536 | - getGlobalColumnOfPosition(mFirstVisiblePosition); 537 | 538 | return new PointF(columnOffset * mDecoratedChildWidth, rowOffset * mDecoratedChildHeight); 539 | } 540 | }; 541 | scroller.setTargetPosition(position); 542 | startSmoothScroll(scroller); 543 | } 544 | 545 | /* 546 | * Use this method to tell the RecyclerView if scrolling is even possible 547 | * in the horizontal direction. 548 | */ 549 | @Override 550 | public boolean canScrollHorizontally() { 551 | //We do allow scrolling 552 | return true; 553 | } 554 | 555 | /* 556 | * This method describes how far RecyclerView thinks the contents should scroll horizontally. 557 | * You are responsible for verifying edge boundaries, and determining if this scroll 558 | * event somehow requires that new views be added or old views get recycled. 559 | */ 560 | @Override 561 | public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { 562 | if (getChildCount() == 0) { 563 | return 0; 564 | } 565 | 566 | //Take leftmost measurements from the top-left child 567 | final View topView = getChildAt(0); 568 | //Take rightmost measurements from the top-right child 569 | final View bottomView = getChildAt(mVisibleColumnCount-1); 570 | 571 | //Optimize the case where the entire data set is too small to scroll 572 | int viewSpan = getDecoratedRight(bottomView) - getDecoratedLeft(topView); 573 | if (viewSpan < getHorizontalSpace()) { 574 | //We cannot scroll in either direction 575 | return 0; 576 | } 577 | 578 | int delta; 579 | boolean leftBoundReached = getFirstVisibleColumn() == 0; 580 | boolean rightBoundReached = getLastVisibleColumn() >= getTotalColumnCount(); 581 | if (dx > 0) { // Contents are scrolling left 582 | //Check right bound 583 | if (rightBoundReached) { 584 | //If we've reached the last column, enforce limits 585 | int rightOffset = getHorizontalSpace() - getDecoratedRight(bottomView) + getPaddingRight(); 586 | delta = Math.max(-dx, rightOffset); 587 | } else { 588 | //No limits while the last column isn't visible 589 | delta = -dx; 590 | } 591 | } else { // Contents are scrolling right 592 | //Check left bound 593 | if (leftBoundReached) { 594 | int leftOffset = -getDecoratedLeft(topView) + getPaddingLeft(); 595 | delta = Math.min(-dx, leftOffset); 596 | } else { 597 | delta = -dx; 598 | } 599 | } 600 | 601 | offsetChildrenHorizontal(delta); 602 | 603 | if (dx > 0) { 604 | if (getDecoratedRight(topView) < 0 && !rightBoundReached) { 605 | fillGrid(DIRECTION_END, recycler, state); 606 | } else if (!rightBoundReached) { 607 | fillGrid(DIRECTION_NONE, recycler, state); 608 | } 609 | } else { 610 | if (getDecoratedLeft(topView) > 0 && !leftBoundReached) { 611 | fillGrid(DIRECTION_START, recycler, state); 612 | } else if (!leftBoundReached) { 613 | fillGrid(DIRECTION_NONE, recycler, state); 614 | } 615 | } 616 | 617 | /* 618 | * Return value determines if a boundary has been reached 619 | * (for edge effects and flings). If returned value does not 620 | * match original delta (passed in), RecyclerView will draw 621 | * an edge effect. 622 | */ 623 | return -delta; 624 | } 625 | 626 | /* 627 | * Use this method to tell the RecyclerView if scrolling is even possible 628 | * in the vertical direction. 629 | */ 630 | @Override 631 | public boolean canScrollVertically() { 632 | //We do allow scrolling 633 | return true; 634 | } 635 | 636 | /* 637 | * This method describes how far RecyclerView thinks the contents should scroll vertically. 638 | * You are responsible for verifying edge boundaries, and determining if this scroll 639 | * event somehow requires that new views be added or old views get recycled. 640 | */ 641 | @Override 642 | public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { 643 | if (getChildCount() == 0) { 644 | return 0; 645 | } 646 | 647 | //Take top measurements from the top-left child 648 | final View topView = getChildAt(0); 649 | //Take bottom measurements from the bottom-right child. 650 | final View bottomView = getChildAt(getChildCount()-1); 651 | 652 | //Optimize the case where the entire data set is too small to scroll 653 | int viewSpan = getDecoratedBottom(bottomView) - getDecoratedTop(topView); 654 | if (viewSpan < getVerticalSpace()) { 655 | //We cannot scroll in either direction 656 | return 0; 657 | } 658 | 659 | int delta; 660 | int maxRowCount = getTotalRowCount(); 661 | boolean topBoundReached = getFirstVisibleRow() == 0; 662 | boolean bottomBoundReached = getLastVisibleRow() >= maxRowCount; 663 | if (dy > 0) { // Contents are scrolling up 664 | //Check against bottom bound 665 | if (bottomBoundReached) { 666 | //If we've reached the last row, enforce limits 667 | int bottomOffset; 668 | if (rowOfIndex(getChildCount() - 1) >= (maxRowCount - 1)) { 669 | //We are truly at the bottom, determine how far 670 | bottomOffset = getVerticalSpace() - getDecoratedBottom(bottomView) 671 | + getPaddingBottom(); 672 | } else { 673 | /* 674 | * Extra space added to account for allowing bottom space in the grid. 675 | * This occurs when the overlap in the last row is not large enough to 676 | * ensure that at least one element in that row isn't fully recycled. 677 | */ 678 | bottomOffset = getVerticalSpace() - (getDecoratedBottom(bottomView) 679 | + mDecoratedChildHeight) + getPaddingBottom(); 680 | } 681 | 682 | delta = Math.max(-dy, bottomOffset); 683 | } else { 684 | //No limits while the last row isn't visible 685 | delta = -dy; 686 | } 687 | } else { // Contents are scrolling down 688 | //Check against top bound 689 | if (topBoundReached) { 690 | int topOffset = -getDecoratedTop(topView) + getPaddingTop(); 691 | 692 | delta = Math.min(-dy, topOffset); 693 | } else { 694 | delta = -dy; 695 | } 696 | } 697 | 698 | offsetChildrenVertical(delta); 699 | 700 | if (dy > 0) { 701 | if (getDecoratedBottom(topView) < 0 && !bottomBoundReached) { 702 | fillGrid(DIRECTION_DOWN, recycler, state); 703 | } else if (!bottomBoundReached) { 704 | fillGrid(DIRECTION_NONE, recycler, state); 705 | } 706 | } else { 707 | if (getDecoratedTop(topView) > 0 && !topBoundReached) { 708 | fillGrid(DIRECTION_UP, recycler, state); 709 | } else if (!topBoundReached) { 710 | fillGrid(DIRECTION_NONE, recycler, state); 711 | } 712 | } 713 | 714 | /* 715 | * Return value determines if a boundary has been reached 716 | * (for edge effects and flings). If returned value does not 717 | * match original delta (passed in), RecyclerView will draw 718 | * an edge effect. 719 | */ 720 | return -delta; 721 | } 722 | 723 | /* 724 | * This is a helper method used by RecyclerView to determine 725 | * if a specific child view can be returned. 726 | */ 727 | @Override 728 | public View findViewByPosition(int position) { 729 | for (int i=0; i < getChildCount(); i++) { 730 | if (positionOfIndex(i) == position) { 731 | return getChildAt(i); 732 | } 733 | } 734 | 735 | return null; 736 | } 737 | 738 | /** Boilerplate to extend LayoutParams for tracking row/column of attached views */ 739 | 740 | /* 741 | * Even without extending LayoutParams, we must override this method 742 | * to provide the default layout parameters that each child view 743 | * will receive when added. 744 | */ 745 | @Override 746 | public RecyclerView.LayoutParams generateDefaultLayoutParams() { 747 | return new LayoutParams( 748 | ViewGroup.LayoutParams.WRAP_CONTENT, 749 | ViewGroup.LayoutParams.WRAP_CONTENT); 750 | } 751 | @Override 752 | public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) { 753 | return new LayoutParams(c, attrs); 754 | } 755 | @Override 756 | public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { 757 | if (lp instanceof ViewGroup.MarginLayoutParams) { 758 | return new LayoutParams((ViewGroup.MarginLayoutParams) lp); 759 | } else { 760 | return new LayoutParams(lp); 761 | } 762 | } 763 | @Override 764 | public boolean checkLayoutParams(RecyclerView.LayoutParams lp) { 765 | return lp instanceof LayoutParams; 766 | } 767 | 768 | public static class LayoutParams extends RecyclerView.LayoutParams { 769 | 770 | //Current row in the grid 771 | public int row; 772 | //Current column in the grid 773 | public int column; 774 | 775 | public LayoutParams(Context c, AttributeSet attrs) { 776 | super(c, attrs); 777 | } 778 | public LayoutParams(int width, int height) { 779 | super(width, height); 780 | } 781 | public LayoutParams(ViewGroup.MarginLayoutParams source) { 782 | super(source); 783 | } 784 | public LayoutParams(ViewGroup.LayoutParams source) { 785 | super(source); 786 | } 787 | public LayoutParams(RecyclerView.LayoutParams source) { 788 | super(source); 789 | } 790 | } 791 | 792 | /** Animation Layout Helpers */ 793 | 794 | /* Helper to obtain and place extra appearing views */ 795 | private void layoutAppearingViews(RecyclerView.Recycler recycler, View referenceView, int referencePosition, int extraCount, int offset) { 796 | //Nothing to do... 797 | if (extraCount < 1) return; 798 | 799 | //FIXME: This code currently causes double layout of views that are still visible… 800 | for (int extra = 1; extra <= extraCount; extra++) { 801 | //Grab the next position after the reference 802 | final int extraPosition = referencePosition + extra; 803 | if (extraPosition < 0 || extraPosition >= getItemCount()) { 804 | //Can't do anything with this 805 | continue; 806 | } 807 | 808 | /* 809 | * Obtain additional position views that we expect to appear 810 | * as part of the animation. 811 | */ 812 | View appearing = recycler.getViewForPosition(extraPosition); 813 | addView(appearing); 814 | 815 | //Find layout delta from reference position 816 | final int newRow = getGlobalRowOfPosition(extraPosition + offset); 817 | final int rowDelta = newRow - getGlobalRowOfPosition(referencePosition + offset); 818 | final int newCol = getGlobalColumnOfPosition(extraPosition + offset); 819 | final int colDelta = newCol - getGlobalColumnOfPosition(referencePosition + offset); 820 | 821 | layoutTempChildView(appearing, rowDelta, colDelta, referenceView); 822 | } 823 | } 824 | 825 | /* Helper to place a disappearing view */ 826 | private void layoutDisappearingView(View disappearingChild) { 827 | /* 828 | * LayoutManager has a special method for attaching views that 829 | * will only be around long enough to animate. 830 | */ 831 | addDisappearingView(disappearingChild); 832 | 833 | //Adjust each disappearing view to its proper place 834 | final LayoutParams lp = (LayoutParams) disappearingChild.getLayoutParams(); 835 | 836 | final int newRow = getGlobalRowOfPosition(lp.getViewAdapterPosition()); 837 | final int rowDelta = newRow - lp.row; 838 | final int newCol = getGlobalColumnOfPosition(lp.getViewAdapterPosition()); 839 | final int colDelta = newCol - lp.column; 840 | 841 | layoutTempChildView(disappearingChild, rowDelta, colDelta, disappearingChild); 842 | } 843 | 844 | 845 | /* Helper to lay out appearing/disappearing children */ 846 | private void layoutTempChildView(View child, int rowDelta, int colDelta, View referenceView) { 847 | //Set the layout position to the global row/column difference from the reference view 848 | int layoutTop = getDecoratedTop(referenceView) + rowDelta * mDecoratedChildHeight; 849 | int layoutLeft = getDecoratedLeft(referenceView) + colDelta * mDecoratedChildWidth; 850 | 851 | measureChildWithMargins(child, 0, 0); 852 | layoutDecorated(child, layoutLeft, layoutTop, 853 | layoutLeft + mDecoratedChildWidth, 854 | layoutTop + mDecoratedChildHeight); 855 | } 856 | 857 | /** Private Helpers and Metrics Accessors */ 858 | 859 | /* Return the overall column index of this position in the global layout */ 860 | private int getGlobalColumnOfPosition(int position) { 861 | return position % mTotalColumnCount; 862 | } 863 | /* Return the overall row index of this position in the global layout */ 864 | private int getGlobalRowOfPosition(int position) { 865 | return position / mTotalColumnCount; 866 | } 867 | 868 | /* 869 | * Mapping between child view indices and adapter data 870 | * positions helps fill the proper views during scrolling. 871 | */ 872 | private int positionOfIndex(int childIndex) { 873 | int row = childIndex / mVisibleColumnCount; 874 | int column = childIndex % mVisibleColumnCount; 875 | 876 | return mFirstVisiblePosition + (row * getTotalColumnCount()) + column; 877 | } 878 | 879 | private int rowOfIndex(int childIndex) { 880 | int position = positionOfIndex(childIndex); 881 | 882 | return position / getTotalColumnCount(); 883 | } 884 | 885 | private int getFirstVisibleColumn() { 886 | return (mFirstVisiblePosition % getTotalColumnCount()); 887 | } 888 | 889 | private int getLastVisibleColumn() { 890 | return getFirstVisibleColumn() + mVisibleColumnCount; 891 | } 892 | 893 | private int getFirstVisibleRow() { 894 | return (mFirstVisiblePosition / getTotalColumnCount()); 895 | } 896 | 897 | private int getLastVisibleRow() { 898 | return getFirstVisibleRow() + mVisibleRowCount; 899 | } 900 | 901 | private int getVisibleChildCount() { 902 | return mVisibleColumnCount * mVisibleRowCount; 903 | } 904 | 905 | private int getTotalColumnCount() { 906 | if (getItemCount() < mTotalColumnCount) { 907 | return getItemCount(); 908 | } 909 | 910 | return mTotalColumnCount; 911 | } 912 | 913 | private int getTotalRowCount() { 914 | if (getItemCount() == 0 || mTotalColumnCount == 0) { 915 | return 0; 916 | } 917 | int maxRow = getItemCount() / mTotalColumnCount; 918 | //Bump the row count if it's not exactly even 919 | if (getItemCount() % mTotalColumnCount != 0) { 920 | maxRow++; 921 | } 922 | 923 | return maxRow; 924 | } 925 | 926 | private int getHorizontalSpace() { 927 | return getWidth() - getPaddingRight() - getPaddingLeft(); 928 | } 929 | 930 | private int getVerticalSpace() { 931 | return getHeight() - getPaddingBottom() - getPaddingTop(); 932 | } 933 | } 934 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devunwired/recyclerview-playground/299515e0cfe4caea78eaf7ba12f7c9cf926b6063/app/src/main/res/drawable-hdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devunwired/recyclerview-playground/299515e0cfe4caea78eaf7ba12f7c9cf926b6063/app/src/main/res/drawable-mdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devunwired/recyclerview-playground/299515e0cfe4caea78eaf7ba12f7c9cf926b6063/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devunwired/recyclerview-playground/299515e0cfe4caea78eaf7ba12f7c9cf926b6063/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_game.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 12 | 16 | 17 | 22 | 24 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_navigation_drawer.xml: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_recycler.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_match_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | 21 | 30 | 31 | 41 | 42 | 52 | 53 | 60 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/res/menu/grid_options.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 11 | 12 | 16 | 20 | 24 | 28 | 29 | 33 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devunwired/recyclerview-playground/299515e0cfe4caea78eaf7ba12f7c9cf926b6063/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devunwired/recyclerview-playground/299515e0cfe4caea78eaf7ba12f7c9cf926b6063/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devunwired/recyclerview-playground/299515e0cfe4caea78eaf7ba12f7c9cf926b6063/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devunwired/recyclerview-playground/299515e0cfe4caea78eaf7ba12f7c9cf926b6063/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devunwired/recyclerview-playground/299515e0cfe4caea78eaf7ba12f7c9cf926b6063/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 240dp 5 | 6 | 8dp 7 | 240dp 8 | 240dp 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RecyclerPlayground 5 | Vertical List 6 | Horizontal List 7 | Vertical Grid 8 | Vertical Staggered Grid 9 | Fixed Two-Way List 10 | Open navigation drawer 11 | Close navigation drawer 12 | 13 | Empty 14 | Small Grid 15 | Medium Grid 16 | Large Grid 17 | Scroll Home 18 | Smooth Scroll Home 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.1.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devunwired/recyclerview-playground/299515e0cfe4caea78eaf7ba12f7c9cf926b6063/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Apr 18 11:29:54 MDT 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------