how to set span count in each position in recyclerview - android

how to achieve this?
when i apply this logic
gridLayoutManager =
GridLayoutManager(this#RidesListActivity, 6)
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (position > 11)
1
else
2
}
}
so it return this
click link to check output
https://i.stack.imgur.com/sJDCy.png
please tell me how i can do this?
i want to achieve this
click link to check output
https://i.stack.imgur.com/tq901.jpg

Related

How to change position of Adapter in Android

In my application I want change set new value to position of recyclerview adapter.
With java language I can it with below code:
#Override
public void onBindViewHolder(MonthFragmentAdapter.ViewHolder holder, int position) {
position += 6 - (position % 7) * 2;
}
But in kotlin I can't use below code and show me val error for position.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
position += 6 - position % 7 * 2
}
Show me Val cannot be reassigned! I know I can't set new value for val, but how can I change my code for fix it?
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
var newPosition = position
newPosition += 6 - newPosition % 7 * 2
}
Use a new variable to change the value.
It is Kotlin feature, you can't change the function parameter.
chainging adapter position is not possible, but you can do it with taht way
if (position == yourposition){//do that}

Android RecyclerView Make fixed grid layout with combined rows

I would like to make a layout like this:
I tried with this code, but the second and third item's height are same as the first item.
val lm = GridLayoutManager(requireContext(), 2, RecyclerView.HORIZONTAL, false)
lm.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (position == 0) 2
else 1
}
}

Recycler view post Item deletion, has deleted views overlapping other views

I already tried this solution here, but unfortunately doesnt work in my scenario.
Ill keep it simple: I have multiple viewHolders with multiple animations for a chat App,
Since I have no touch listeners to register the adapter position of the typing indicators, I have:
In my CustomAdapter
private var typingIndicatorAdapterPosition: Int = -1
private var inlineErrorAdapterPosition: Int = -1
In my onBindViewHolder
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder.itemViewType) {
...
USER_REQUEST_TEXT -> {
val userRequestViewHolder = (holder as UserRequestViewHolder)
configUserRequestViewHolder(userRequestViewHolder, position)
userRequestViewHolder.setIsRecyclable(false)
}
TYPE_INDICATOR -> {
val typingIndicatorViewHolder = (holder as TypingIndicatorViewHolder)
configTypingIndicatorViewHolder(typingIndicatorViewHolder, position)
typingIndicatorAdapterPosition = typingIndicatorViewHolder.layoutPosition
typingIndicatorViewHolder.setIsRecyclable(true)
}
INLINE_ERROR -> {
val inlineErrorViewHolder = (holder as InlineErrorViewHolder)
configInlineErrorViewHolder(inlineErrorViewHolder, position)
inlineErrorAdapterPosition = inlineErrorViewHolder.layoutPosition
inlineErrorViewHolder.setIsRecyclable(true)
}
}
}
my adapter code for deletion :
fun removeTypingIndicator() {
if(typingIndicatorAdapterPosition > 0) {
if(messageContainerList[typingIndicatorAdapterPosition].messageType == TYPE_INDICATOR) {
messageContainerList.removeAt(typingIndicatorAdapterPosition)
notifyItemRemoved(typingIndicatorAdapterPosition)
notifyItemRangeChanged(typingIndicatorAdapterPosition, itemCount - 1)
typingIndicatorAdapterPosition = -1
}
}
}
Note - I do not prefer notifyDataSetChanged() etc. as it cancels the animations.
here are some screen shots:
This line looks incorrect:
notifyItemRangeChanged(typingIndicatorAdapterPosition, itemCount - 1)
Assuming you're trying to change all the items from the typing indicator position through the end of the list, it looks like you're trying to use it as (start, end), but the parameters are actually (start, itemCount), with itemCount being the number of items changed at that index. See the documentation here for more information.

How to change the spancount for every viewholder with conditions other than position in android

I'm trying to change the spanCount for every viewHolder item in the Recyclerview according to the condition of the data in the items of the recyclerview.Currently, I'm changing the spanCount with the position. But How am I able to change the span of each Items(Viewholder) according to the conditions other than position? For example I want to do like if (type == Item.type) return 1
Some examples or hints would be lovely! I would love to hear from you!
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (adapter.isMainHead(position) && tabIndex == 0) {
gridLayoutManager.spanCount
} else {
1
}
}
}
Hope this example will help you.
(layoutManager as GridLayoutManager).setSpanSizeLookup(object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
when (adapter.getItemViewType(position)) {
CalendarAdapter.TYPE_HEADER -> return 7
CalendarAdapter.TYPE_ITEM -> return 1
CalendarAdapter.TYPE_SPAN -> return dataArrayList[position].myVar.toInt()
else -> return -1
}
}
})

RecyclerView.Adapter.notifyItemMoved(0,1) scrolls screen

I have a RecyclerView managed by a LinearlayoutManager, if I swap item 1 with 0 and then call mAdapter.notifyItemMoved(0,1), the moving animation causes the screen to scroll. How can I prevent it?
Sadly the workaround presented by yigit scrolls the RecyclerView to the top. This is the best workaround I found till now:
// figure out the position of the first visible item
int firstPos = manager.findFirstCompletelyVisibleItemPosition();
int offsetTop = 0;
if(firstPos >= 0) {
View firstView = manager.findViewByPosition(firstPos);
offsetTop = manager.getDecoratedTop(firstView) - manager.getTopDecorationHeight(firstView);
}
// apply changes
adapter.notify...
// reapply the saved position
if(firstPos >= 0) {
manager.scrollToPositionWithOffset(firstPos, offsetTop);
}
Call scrollToPosition(0) after moving items. Unfortunately, i assume, LinearLayoutManager tries to keep first item stable, which moves so it moves the list with it.
Translate #Andreas Wenger's answer to kotlin:
val firstPos = manager.findFirstCompletelyVisibleItemPosition()
var offsetTop = 0
if (firstPos >= 0) {
val firstView = manager.findViewByPosition(firstPos)!!
offsetTop = manager.getDecoratedTop(firstView) - manager.getTopDecorationHeight(firstView)
}
// apply changes
adapter.notify...
if (firstPos >= 0) {
manager.scrollToPositionWithOffset(firstPos, offsetTop)
}
In my case, the view can have a top margin, which also needs to be counted in the offset, otherwise the recyclerview will not scroll to the intended position. To do so, just write:
val topMargin = (firstView.layoutParams as? MarginLayoutParams)?.topMargin ?: 0
offsetTop = manager.getDecoratedTop(firstView) - manager.getTopDecorationHeight(firstView) - topMargin
Even easier if you have ktx dependency in your project:
offsetTop = manager.getDecoratedTop(firstView) - manager.getTopDecorationHeight(firstView) - firstView.marginTop
I've faced the same problem. Nothing of the suggested helped. Each solution fix and breakes different cases.
But this workaround worked for me:
adapter.registerAdapterDataObserver(object: RecyclerView.AdapterDataObserver() {
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
if (fromPosition == 0 || toPosition == 0)
binding.recycler.scrollToPosition(0)
}
})
It helps to prevent scrolling while moving the first item for cases: direct notifyItemMoved and via ItemTouchHelper (drag and drop)
I have faced the same problem. In my case, the scroll happens on the first visible item (not only on the first item in the dataset). And I would like to thanks everybody because their answers help me to solve this problem.
I inspire my solution based on Andreas Wenger' answer and from resoluti0n' answer
And, here is my solution (in Kotlin):
RecyclerViewOnDragFistItemScrollSuppressor.kt
class RecyclerViewOnDragFistItemScrollSuppressor private constructor(
lifecycleOwner: LifecycleOwner,
private val recyclerView: RecyclerView
) : LifecycleObserver {
private val adapterDataObserver = object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
suppressScrollIfNeeded(fromPosition, toPosition)
}
}
init {
lifecycleOwner.lifecycle.addObserver(this)
}
#OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun registerAdapterDataObserver() {
recyclerView.adapter?.registerAdapterDataObserver(adapterDataObserver) ?: return
}
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun unregisterAdapterDataObserver() {
recyclerView.adapter?.unregisterAdapterDataObserver(adapterDataObserver) ?: return
}
private fun suppressScrollIfNeeded(fromPosition: Int, toPosition: Int) {
(recyclerView.layoutManager as LinearLayoutManager).apply {
var scrollPosition = -1
if (isFirstVisibleItem(fromPosition)) {
scrollPosition = fromPosition
} else if (isFirstVisibleItem(toPosition)) {
scrollPosition = toPosition
}
if (scrollPosition == -1) return
scrollToPositionWithCalculatedOffset(scrollPosition)
}
}
companion object {
fun observe(
lifecycleOwner: LifecycleOwner,
recyclerView: RecyclerView
): RecyclerViewOnDragFistItemScrollSuppressor {
return RecyclerViewOnDragFistItemScrollSuppressor(lifecycleOwner, recyclerView)
}
}
}
private fun LinearLayoutManager.isFirstVisibleItem(position: Int): Boolean {
apply {
return position == findFirstVisibleItemPosition()
}
}
private fun LinearLayoutManager.scrollToPositionWithCalculatedOffset(position: Int) {
apply {
val offset = findViewByPosition(position)?.let {
getDecoratedTop(it) - getTopDecorationHeight(it)
} ?: 0
scrollToPositionWithOffset(position, offset)
}
}
and then, you may use it as (e.g. fragment):
RecyclerViewOnDragFistItemScrollSuppressor.observe(
viewLifecycleOwner,
binding.recyclerView
)
LinearLayoutManager has done this for you in LinearLayoutManager.prepareForDrop.
All you need to provide is the moving (old) View and the target (new) View.
layoutManager.prepareForDrop(oldView, targetView, -1, -1)
// the numbers, x and y don't matter to LinearLayoutManager's implementation of prepareForDrop
It's an "unofficial" API because it states in the source
// This method is only intended to be called (and should only ever be called) by
// ItemTouchHelper.
public void prepareForDrop(#NonNull View view, #NonNull View target, int x, int y) {
...
}
But it still works and does exactly what the other answers say, doing all the offset calculations accounting for layout direction for you.
This is actually the same method that is called by LinearLayoutManager when used by an ItemTouchHelper to account for this dreadful bug.

Categories

Resources