This is the adapter:
class ContactsAdapter(val context: Context, private val users: MutableList<Contacts>, val itemClick: (Contacts) -> Unit) : RecyclerView.Adapter<ContactsAdapter.ViewHolder>(){
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.remove.setOnClickListener {
val builder = AlertDialog.Builder(context)
builder.setMessage(R.string.delete_contact)
builder.setPositiveButton(R.string.yes){_, _ ->
users.removeAt(position)
notifyItemRemoved(position)
}
builder.setNegativeButton(R.string.no){_,_ ->
}
val dialog: AlertDialog = builder.create()
dialog.show()
}
}
override fun getItemCount() = users.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.contacts, parent, false)
return ViewHolder(view, itemClick)
}
class ViewHolder(itemView: View?, val itemClick: (Contacts) -> Unit) : RecyclerView.ViewHolder(itemView!!){
val remove = itemView!!.removecontact!!
}
}
I got 2 items for testing, when I delete the second then first one it's fine, but when first then second one then the app crashes and the error is:
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
at java.util.ArrayList.remove(ArrayList.java:503)
at com.xxx.xxx.classes.ContactsAdapter$onBindViewHolder$2$1.onClick(ContactsAdapter.kt:57)
at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:177)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
What could be the problem?
Thanks in advance
As you can see, it is an IndexOutOfBoundsException, because you are attempting to access the index 1 in an array of size 1. This is mostly because you are directly using the position argument from onBindViewHolder from inside the AlertDialog's setPositiveButton call.
Instead use the holder.getAdapterPosition method to get the latest position. This should prevent the crash.
Edit #1: What I mean is to replace the position usages with holder.getAdapterPosition(). Your onBindViewHolder should look like this after the edits:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.remove.setOnClickListener {
val builder = AlertDialog.Builder(context)
builder.setMessage(R.string.delete_contact)
builder.setPositiveButton(R.string.yes){_, _ ->
users.removeAt(holder.getAdapterPosition())
notifyItemRemoved(holder.getAdapterPosition())
}
builder.setNegativeButton(R.string.no){_,_ ->
}
val dialog: AlertDialog = builder.create()
dialog.show()
}
}
I got 2 items for testing, when I delete the second then first one it's fine, but when first then second one then the app crashes.
My guess is you do not refresh of the indezes of the list Items in your view
When you remove your fist item everything is fine you removed Index 0 from a list of 2
But your second crashes because you try to remove Index 1 (the second item) of a list that only has one element left.
your "notifyItemRemoved(position)" has to reassign the indezes of all the items that are left after removing one
Firstly, set the click listener outside the bindview holder as it is the bad practice. Just set it on onCreateViewHolder. Then, you will come to know that the listener gets called for the first time as soon as the adapter is set. For that you need to stop it from getting called by simply declaring a bool variable and assigning it to false in the adapter class like this.
private var islistenerCalledFirst: Boolean = false;
Then in the OnCreateViewHolder set the listener. Note, you can get position of the item by calling viewHolder.getAdapterPosition().
holder.remove.setOnClickListener {
if(islistenerCalledFirst){
... //your logic
}
islistenerCalledFirst = true
}
Hope this works for you.
Related
I am using this library to put a carousel view in an Android app: https://github.com/ImaginativeShohag/Why-Not-Image-Carousel
I'm also trying to use the showcase type, but a prerequisite to use this type is creating a custom layout for the carousel items.
Creating the layout I understand, but the OP uses this example in Kotlin to show how the custom layout is actually used:
binding.carousel3.carouselListener = object : CarouselListener {
override fun onCreateViewHolder(
layoutInflater: LayoutInflater,
parent: ViewGroup
): ViewBinding? {
return ItemCustomFixedSizeLayout1Binding.inflate(layoutInflater, parent, false)
}
override fun onBindViewHolder(
binding: ViewBinding,
item: CarouselItem,
position: Int
) {
val currentBinding = binding as ItemCustomFixedSizeLayout1Binding
currentBinding.imageView.apply {
scaleType = imageScaleType
// carousel_default_placeholder is the default placeholder comes with
// the library.
setImage(item, R.drawable.carousel_default_placeholder)
}
}
}
val listThree = mutableListOf<CarouselItem>()
for (item in DataSet.three) {
listThree.add(
CarouselItem(
imageUrl = item.first,
caption = item.second
)
)
}
binding.carousel3.setData(listThree)
binding.customCaption.isSelected = true
binding.carousel3.onScrollListener = object : CarouselOnScrollListener {
override fun onScrollStateChanged(
recyclerView: RecyclerView,
newState: Int,
position: Int,
carouselItem: CarouselItem?
) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
carouselItem?.apply {
binding.customCaption.text = caption
}
}
}
override fun onScrolled(
recyclerView: RecyclerView,
dx: Int,
dy: Int,
position: Int,
carouselItem: CarouselItem?
) {
// ...
}
}
// Custom navigation
binding.btnGotoPrevious.setOnClickListener {
binding.carousel3.previous()
}
binding.btnGotoNext.setOnClickListener {
binding.carousel3.next()
}
I'm having some trouble figuring out what exactly this code is doing and how it would look in Java. Any pointers would be greatly appreciated!
A Quick Guess
It seems that the listener is providing the callback of Recycler View. If you need me to guess within a second, and I will say the custom view is a Recycler View using listener to allow users to register the Recycler View methods (which is the Adapter using in the RV)
Deep Investigation
1st question: What is the Custom View class for id=carousel3 in KotlinActivity in the sample project
Ans: org.imaginativeworld.whynotimagecarousel.ImageCarousel.
(P.S. identical between activity_kotlin.xml and activity_test.xml)
(Below is a screen cap, don't try to click the links since it will not work :))
Let's got to search ImageCarousel and we will find ImageCarousel.kt. Let's find CarouselListener in there
We can see that when CarouselListener is set, it will immediately assign to adapter?.listener (Just ignore the "?" sign if you are not familiar with Kotlin)
2nd question: What is adapter here?
Ans from the same file:
private var adapter: FiniteCarouselAdapter? = null
3rd question: What is FiniteCarouselAdapter?
Ans: Its a RecyclerView.Adapter
open class FiniteCarouselAdapter(
...
) : RecyclerView.Adapter<FiniteCarouselAdapter.MyViewHolder>() {
Last question: How is it related to FiniteCarouselAdapter#listener/CarouselListener/adapter?.listener?
When the RecyclerView#Adapter requires to call the ViewHolder method, it will call to CarouselListener methods instead.
In FiniteCarouselAdapter:
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
...
// Init listeners
listener?.onBindViewHolder(
holder.binding,
item,
realItemPosition
)
As CarouselListener is an interface, the method implementation will be defined in the KotlinActivity instead.
In KotlinActivity:
The above code in your question : )
I'm trying to make something like below:
On that screenshot of facebook lite app:
At part marked "1": is a vertical recyclerview which contains posts.
At part marked "2": is a horizontal recyclerview which contains the stories.
At part marked "3": is the same recyclerview as at part marked "1" which contains posts.
I have already made the recyclerview for posts and It works well. Now I want to know how should I make the recyclerview for stories or friendship suggestion and make the two recyclerviews appear like on Facebook app ?
How could I have recyclerviews similar to the one in facebook app ?
Facebook show multiples recyclerViews One vertical where it shows the posts some others horizontal where it
shows stories or sometimes friendship suggestion.
Do you undestand me ?
Please tell me if I should explain more my issue.
Thanks.
you can define different View Holders for a Recycler List
the horizontal lists are just a view holder with another recycler inside them but with horizontal orientation in the main list. you can choose which view holder you want to use in OnCreatViewHolder
You should implement RecyclerView with multi-view type which gives you the various layout on a single RecyclerView adapter.
By doing this you need a model class which should have a field for define the different type.
Please have a look below sample code
Sample Model class
data class Sample(
var a: String = ""
...
var type: String = ""
)
Field type in model class above will define a unique view-type of adapter
Sample Adapter Class
class SampleAdapter(
private val context: Context,
private val items: ArrayList<Sample>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
private const val HEADER = 1
private const val STORY = 2
private const val FEED = 3
}
override fun getItemViewType(position: Int): Int {
return when(items[position].type) {
Constant.HEADER -> HEADER
Constant.STORY -> STORY
else -> FEED
}
}
override fun getItemCount(): Int = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when(viewType) {
HEADER -> HeaderViewHolder(LayoutInflater.from(context).inflate(R.layout_header, parent, false))
STORY -> StoryViewHolder(LayoutInflater.from(context).inflate(R.layout_story, parent, false))
else -> FeedViewHolder(LayoutInflater.from(context).inflate(R.layout_feed, parent, false))
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when(holder) {
is HeaderViewHolder -> holder.onBind()
is StoryViewHolder -> holder.onBind()
is FeedViewHolder -> holder.onBind()
}
}
inner class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun onBind() {
....your business logic
}
}
inner class StoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun onBind() {
....your business logic
}
}
inner class FeedViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun onBind() {
....your business logic
}
}
}
And when you are using the above adapter in Activity or Fragment. You should define the type of each item in ArrayList
For example: samples.add(Sample(...., type=Constant.Header))
Problem
I am writing an android application that has to handle a variable amount of elements in a RecyclerView. When I add an element, some data gets saved into a configuration file along with an UUID.
Either when the application starts or when the element gets created or removed by the user during runtime, the RecyclerView gets updated and shows all currently existing elements.
Each of these elements has a button to remove it and its additional data from the configuration. When I press this button, the elements data successfully gets removed from the configuration and it turns invisible leading the RecyclerView only to show the remaining elements.
However, when I try to insert a new element, it doesn't get shown but rather the element that has been removed previously during this session.
Tries
I have been looking for a solution for a while now.
First of all, I startet reading the documentation over and over again, thinking that I missed something. I also viewed some online examples on how to remove an item from a RecyclerView properly but I can't get it working.
Code
package com.rappee.protectiveparking.ui
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import com.rappee.protectiveparking.R
import com.rappee.protectiveparking.core.Resources
import java.util.*
class ScrollerAdapter : RecyclerView.Adapter< ConsoleViewHolder >() ,
DragItemTouchHelperAdapter {
private var stockpile: Queue< String > = LinkedList< String >()
private var holder: MutableList< ConsoleViewHolder > = ArrayList()
fun push( identification: String ) {
this.stockpile.add( identification )
this.notifyItemInserted( this.itemCount - 1 )
}
fun remove( position: Int ) {
this.holder.removeAt( position )
this.notifyItemRemoved( position )
}
override fun onCreateViewHolder( parent: ViewGroup , type: Int ): ConsoleViewHolder {
val view: ConsoleViewHolder = ConsoleViewHolder( LayoutInflater.from( parent.context ).inflate( R.layout.console , parent , false ) , this.stockpile.poll() )
this.holder.add( view )
return view
}
override fun onBindViewHolder( holder: ConsoleViewHolder , position: Int ) {
return
}
override fun getItemCount(): Int {
return this.holder.size + this.stockpile.size
}
override fun onItemMove( from: Int , to: Int ): Boolean {
Collections.swap( this.holder , to , from )
for ( i in 0 until this.holder.size ) {
Resources.CONFIGURATION.push( "${this.holder.get( i ).identification}.${Resources.KEY_POSITION}" , "$i" )
}
this.notifyItemMoved( from , to )
return true
}
}
Behaviour
When I try to insert a new element, I actually put the UUID into 'stockpile' and call 'notifyItemInserted', so that 'onCreateViewHolder' gets activated. When this happens, I fetch the foremost UUID from 'stockpile' in form of a String to give it to the constructor of 'ConsoleViewHolder' as a parameter.
Only when this method gets called, an actual View gets pushed into the RecyclerView ( not my decision! ).
However, when I insert a new UUID and update the RecyclerView after an item got deleted, this method doesn't get called so that no new View can be shown.
The only thing that changes is that the previously deleted View pops back up.
MY QUESTION: How can I fix this behaviour of my RecyclerView showing deleted items instead of newly pushed ones?
There is no need to store the view holders yourself.
It should look something like this:
private var stockpile = MutableList< String >()
fun push( identification: String ) {
stockpile.add( identification )
notifyItemInserted( itemCount - 1 )
}
fun remove( position: Int ) {
stockpile.removeAt( position )
notifyItemRemoved( position )
}
override fun onCreateViewHolder( parent: ViewGroup , type: Int ): ConsoleViewHolder {
val view: ConsoleViewHolder = ConsoleViewHolder( LayoutInflater.from( parent.context ).inflate( R.layout.console , parent , false ) , this.stockpile.poll() )
return view
}
override fun onBindViewHolder( holder: ConsoleViewHolder , position: Int ) {
//replace with something sensible you want to show in your view
view.someTextView.text = stockpile[position].name
return
}
override fun getItemCount(): Int {
return this.stockpile.size
}
I think you have a bad understanding of what the view holder does. Remove private var holder: MutableList< ConsoleViewHolder > = ArrayList()and all references to it. Do not edit viewholders outside of onBindViewHolder. The weird behaviour you experience is because you do not actually remove the item from the underlying stockpile and then the recyclerview is confused when you tell it that there is a new item, because your count is way off.
see the example at https://developer.android.com/guide/topics/ui/layout/recyclerview for a reference implementation.
Preparation:
RecyclerView with RecyclerView.Adapter binded to SQLite Cursor (via ContentProvider && Loader). RecyclerView and RecyclerView.Adapter linked with SelectionTracker as design suggests.
SelectionTracker builded with StableIdKeyProvider.
On first step - delete an item:
Select RecyclerViews's an item with a long press (cheers to SelectionTracker's SelectionObserver), draw Action Bar Context Menu, fire
the delete action, do the SQL deletion task.
After SQL deletion ends, do the Cursor Loader renewal with
restartLoader call.
onLoadFinished fired, new Cursor obtained, on
RecyclerView.Adapter method notifyDataSetChanged called.
RecyclerView.Adapter redraw RecyclerView content, and all is looks
good.
On second step - do the selection of some other item. Crash:
java.lang.IllegalArgumentException
at androidx.core.util.Preconditions.checkArgument(Preconditions.java:38)
at androidx.recyclerview.selection.DefaultSelectionTracker.anchorRange(DefaultSelectionTracker.java:269)
at androidx.recyclerview.selection.MotionInputHandler.selectItem(MotionInputHandler.java:60)
at androidx.recyclerview.selection.TouchInputHandler.onLongPress(TouchInputHandler.java:132)
at androidx.recyclerview.selection.GestureRouter.onLongPress(GestureRouter.java:96)
at android.view.GestureDetector.dispatchLongPress(GestureDetector.java:779)
at android.view.GestureDetector.access$200(GestureDetector.java:40)
at android.view.GestureDetector$GestureHandler.handleMessage(GestureDetector.java:293)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
What I see on first step while deletion item in progress.
While StableIdKeyProvider do internal job with onDetached ViewHolder item, it don't see previously assigned ViewHolder's position within an Adapter:
void onDetached(#NonNull View view) {
RecyclerView.ViewHolder holder = mRecyclerView.findContainingViewHolder(view);
int position = holder.getAdapterPosition();
long id = holder.getItemId();
if (position != RecyclerView.NO_POSITION && id != RecyclerView.NO_ID) {
int position here is RecyclerView.NO_POSITION
Thats why the RecyclerView crashes after - StableIdKeyProvider's cache contains old snapshot of ID's without deletion affected.
The question is - WHY? and HOW to renew the cache of StableIdKeyProvider?
Another note:
While I read the RecyclerView code, I see this comment:
* Note that if you've called {#link RecyclerView.Adapter#notifyDataSetChanged()}, until the
* next layout pass, the return value of this method will be {#NO_POSITION}.
I am not understood what exactly mean this words. Perhaps I faced with described situation - notifyDataSetChanged called in not appropriate time? Or I need to call it twice?
PS.
Sorry for about literary description, there is a lot of complexity code
I am ended up to play with StableIdKeyProvider and switch to plain my own implementation of ItemKeyProvider:
new ItemKeyProvider<Long>(ItemKeyProvider.SCOPE_MAPPED) {
#Override
public Long getKey(int position) {
return adapter.getItemId(position);
}
#Override
public int getPosition(#NonNull Long key) {
RecyclerView.ViewHolder viewHolder = recyclerList.findViewHolderForItemId(key);
return viewHolder == null ? RecyclerView.NO_POSITION : viewHolder.getLayoutPosition();
}
}
Crash is gone, RecyclerView's navigation/selection/modification looks OK.
What about StableIdKeyProvider ?.. Hmm, may be it is not designed to work with mutable content of RecyclerView.
Update 2021-12-03
Last week I got a new round with fighting on RecycleView.
As mentioned on the question - exactly problem is the CACHE of StableIdKeyProvider. And switch to ItemKeyProvider is the workaround.
As code of StableIdKeyProvider explains, chache tied to the window's evens: attach and detach. So, and the comment which I am quoted above - is the exactly pointed to the problem: when new Cursor arrives - reattaching Cursor to the Adapter and notifying - requires to fire at right time. "Right time" - is enqueue this job in layout-message-thread. In this way RecyclerView and underlying "toolbox" can correct perform an update itself. For doing so, just wrap providing a new Cursor inside the post runnable method. The code:
#Override
public void onLoadFinished(#NonNull Loader<Cursor> loader, Cursor data) {
recycler.post(new Runnable() {
#Override
public void run() {
adapter.swapCursor(data);
}
});
...
My problem was solved by setting setHasStableIds(true) in Recycle view adapter and overriding getItemId, It seems that Tracker require both setHasStableIds(true) and overrindinggetItemId in adapter I got this error after setting stable Ids true without overriding getItemId
init {
setHasStableIds(true)
}
override fun getItemId(position: Int) = position.toLong()
override fun getItemViewType(position: Int) = position
Ran into the same problem and after a long search I have found the answer:
You just need to override the method in your Recycler Adapter view.
override fun getItemId(position: Int): Long = position.toLong()
I encountered the same issue with the StableIdKeyProvider. Writing a custom implementation of ItemKeyProvider seems to do the trick. Here's a basic Kotlin implementation you can use when building a selection tracker for a RecyclerView:
class RecyclerViewIdKeyProvider(private val recyclerView: RecyclerView)
: ItemKeyProvider<Long>(ItemKeyProvider.SCOPE_MAPPED) {
override fun getKey(position: Int): Long? {
return recyclerView.adapter?.getItemId(position)
?: throw IllegalStateException("RecyclerView adapter is not set!")
}
override fun getPosition(key: Long): Int {
val viewHolder = recyclerView.findViewHolderForItemId(key)
return viewHolder?.layoutPosition ?: RecyclerView.NO_POSITION
}
}
In my case, the problem is related to the initialization of ItemDetailsLookup.ItemDetails in ViewHolder. As it turned out, getAdapterPosition() may return the wrong position during ViewHolder binding. The solution is to call getAdapterPosition() at the time call getItemDetails() in ItemDetailsLookup.
I got it resolved by keeping the initialization and setting selectiontracker to the adapter at the very end.
capturedThumbnailListAdapter = new CapturedThumbnailListAdapter(this);
capturedThumbnailListAdapter.setCapturedThumbnailList(capturedThumbnailList);
viewBinding.capturedImagesRv.setLayoutManager(new LinearLayoutManager(this,
LinearLayoutManager.HORIZONTAL, true));
viewBinding.capturedImagesRv.setAdapter(capturedThumbnailListAdapter);
SelectionTracker<Long> tracker = new SelectionTracker.Builder<Long>("thumb_selection",
viewBinding.capturedImagesRv, new StableIdKeyProvider(viewBinding.capturedImagesRv),
new CapturedThumbnailListAdapter.ItemLookUp(viewBinding.capturedImagesRv),
StorageStrategy.createLongStorage())
.withSelectionPredicate(SelectionPredicates.createSelectAnything())
.build();
capturedThumbnailListAdapter.setSelectionTracker(tracker);
I need to develop a tag picker, like the one Foursquare uses for tastes, and the one Flipboard uses for "finding new topics."
I came across this library, Foursquare-CollectionPicker at github.
However, it uses a linear layout, which can reduce the performance for numerous child views when scrolling.
Hence, I need to use a recyclerview. Can anyone suggest how to replicate this with a recyclerview? My problem is that for each row in the recyclerview, the column count could be different, based on the size/number of the child views in each row (tags, in this case).
Thank you.
You could use FlexboxLayoutManager in a recycler view. All you need to do is create the layout manager as below. Don't forget to add your own recyclerAdapter to the recycler view as well of course.
FlexboxLayoutManager layoutManager = new FlexboxLayoutManager(context);
layoutManager.setFlexWrap(FlexWrap.WRAP);
recyclerView.setLayoutManager(layoutManager);
https://android-developers.googleblog.com/2017/02/build-flexible-layouts-with.html
https://blog.devcenter.co/unboxing-the-flexboxlayout-a7cfd125f023
If all you need is a recycler view that changes the number of columns (using a standard Google provided GridLayoutManager in RecyclerView), you don't need any custom code at all.
(pseudo code)
Prerequisites
You use a RecylerView with a GridLayoutManager (import androidx.recyclerview.widget.GridLayoutManager)
Your Adapter has a Type (so different viewTypes can inflate different ViewHolders).
You can initialize your grid Layout like:
private lateinit var layoutManager: GridLayoutManager
private val adapter = YourAdapter()
Activity#onCreate(...) {
super.onCreate(savedInstance)
setContentView(...)
layoutManager = GridLayoutManager(this, 2) //defaults to two columns
yourRecyclerView.layoutmanager = layoutManager //set it
yourRecyclerView.adapter = adapter
//Here goes the magic
}
What is the Magic?
Something like this:
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when (adapter.getItemViewType(position)) {
adapter.viewTypeOneColumn -> 1
adapter.viewTypeTwoColumns -> 2
else -> -1
}
}
}
This obviously assumes you have a "view type" (of any sort)
it can be as simple as:
class YourAdapter : ... {
internal val viewTypeOneColumn = 0
internal val viewTypeTwoColumns = 1
...
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when(viewType) {
viewTypeOneColumn -> ViewHolderForOneColumn(...)
viewTypeTwoColumns -> ViewHolderForTwoColumns(...)
else -> throw IllegalArgumentException("You must supply a valid type for this adapter")
}
}
override fun getItemViewType(position: Int): Int {
return getItem(position).someThingThatClassifiesThem // this obviously depend on what you use to define what each item is...
}
}
And that's all you really need.
I have once created a sample that does this for displaying "ads" inside a RecyclerView: You can check it here (it was updated a while ago but the code still works the same).
https://github.com/Gryzor/GridToShowAds
You will need to write your own layout manager, take a look at these blogs:
http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/
http://simpleandstupid.com/2015/05/01/recyclerview-and-its-custom-layoutmanager/