I am using a RecyclerView to display items horizontally. I want to set the selected item to center of the view like this
.
This is how I am doing it:
LinearLayoutManager layoutManager
= new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);
To obtain middle item on your screen from RecyclerView you can attach OnScrollListener to RecyclerView and inside listener you should get position of current items then you should check if area of given item is on middle of screen.
Code sample in Kotlin:
// Attach OnScrollListener to your RecyclerView
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
recyclerView.post {
selectMiddleItem()
}
}
})
// implementation of method that is called from OnScrollListener
private fun selectMiddleItem() {
val firstVisibleIndex = layoutManager.findFirstVisibleItemPosition()
val lastVisibleIndex = layoutManager.findLastVisibleItemPosition()
val visibleIndexes = listOf(firstVisibleIndex..lastVisibleIndex).flatten()
for (i in visibleIndexes) {
val vh = findViewHolderForLayoutPosition(i)
if (vh?.itemView == null) {
continue
}
val location = IntArray(2)
vh.itemView.getLocationOnScreen(location)
val x = location[0]
val halfWidth = vh.itemView.width * .5
val rightSide = x + halfWidth
val leftSide = x - halfWidth
val isInMiddle = screenWidth * .5 in leftSide..rightSide
if (isInMiddle) {
// "i" is your middle index and implement selecting it as you want
// optionsAdapter.selectItemAtIndex(i)
return
}
}
}
And as result you should get something like this:
This is for snapping the item in the center when scrolling, or when clicking on an ite.
You need to have a SnapHelper added to the RecyclerView. Here is how:
final RecyclerView recyclerViewObject = view.findViewById(R.id.recyclerViewObjectId);
final LinearSnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerViewObject);
recyclerViewObject.setOnFlingListener(snapHelper);
then you just call this code
recyclerViewObject.addOnItemTouchListener(
new RecyclerItemClickListener(getContext(), recyclerViewObject ,new RecyclerItemClickListener.OnItemClickListener() {
#Override public void onItemClick(View view, int position) {
recyclerViewObject.smoothScrollToPosition(position);
}
#Override public void onLongItemClick(View view, int position) {
}
})
);
You can achieve the same output with minimal lines of code. Where, view is view of selected adapter item and StaticData.SCREEN_WIDTH is device width.
View view = rvCategory.getChildAt(pos);
if (view == null)
return;
int scrollX = (view.getLeft() - (StaticData.SCREEN_WIDTH / 2)) + (view.getWidth() / 2);
rvCategory.smoothScrollBy(scrollX, 0);
Please try sort of this solution:
LinearLayoutManager layoutManager = ((LinearLayoutManager)recyclerView.getLayoutManager());
int totalVisibleItems = layoutManager.findLastVisibleItemPosition() - layoutManager.findFirstVisibleItemPosition()
int centeredItemPosition = totalVisibleItems / 2;
recyclerView.smoothScrollToPosition(position);
recyclerView.setScrollY(centeredItemPosition );
Hope this helps.
Define your custom layout manager as
class CenterLayoutManager : LinearLayoutManager {
constructor(context: Context) : super(context)
constructor(context: Context, orientation: Int, reverseLayout: Boolean) : super(context, orientation, reverseLayout)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
override fun smoothScrollToPosition(recyclerView: RecyclerView, state: RecyclerView.State, position: Int) {
val centerSmoothScroller = CenterSmoothScroller(recyclerView.context)
centerSmoothScroller.targetPosition = position
startSmoothScroll(centerSmoothScroller)
}
private class CenterSmoothScroller(context: Context) : LinearSmoothScroller(context) {
override fun calculateDtToFit(viewStart: Int, viewEnd: Int, boxStart: Int, boxEnd: Int, snapPreference: Int): Int = (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2)
}
}
Then assign this layout manager to your recycler view
myRecyclerview.layoutManager =
CenterLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
In recyclerview's item onClick method use
myRecyclerview.smoothScrollToPosition(position)
where position should get from onBindViewHolder
Also use LinearSnapHelper as
val snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(myRecyclerview)
it will controll scrolling effectively
Also attatch scroll listner to recyclerview to get item at center position
Recyclerview.setOnScrollListener(object:
RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
var view=recyclerView[0]
}
})
check out this stackoverflow answer for more details
Related
i try to implement programmatically version of MotionLayout by extending it. And i have a base activity ayout using RecyclerView.
However, when i add my motion layout as an item of the RecyclerView, the view is not recycled when i try to scrolling up and down.
And it works well when i use as a normal view (act as single view).
Here is the preview:
class SimpleMotionLayout #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : MotionLayout(context, attrs, defStyleAttr) {
private val motionScene = MotionScene(this)
private var _simpleTransition: MotionScene.Transition? = null
private lateinit var squareView: View
init {
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
initDefaultConstraint(this)
setMotion()
}
fun setMotion() {
_simpleTransition = createPlaceholderTransition(motionScene)
setDebugMode(DEBUG_SHOW_PATH)
/**
* The order matters here.
* [MotionScene.addTransition] adds the transition to the scene while
* [MotionScene.setTransition] sets the transition to be the current transition.
*/
motionScene.addTransition(_simpleTransition)
motionScene.setTransition(_simpleTransition)
scene = motionScene
setTransition(_simpleTransition!!.id)
animateView()
}
fun setSquareColor(color: Int) {
squareView.setBackgroundColor(color)
}
fun initDefaultConstraint(motionLayout: ConstraintLayout) {
// View
squareView = View(context).apply {
id = R.id.default_button
setBackgroundColor(Color.BLACK)
}
motionLayout.addView(
squareView,
LayoutParams(
fromDp(context, 52),
fromDp(context, 52)
)
)
val set = ConstraintSet()
set.clone(motionLayout)
// Setup constraint set to TOP, LEFT to the Parent
set.connect(
squareView.id,
TOP,
PARENT_ID,
TOP
)
set.connect(
squareView.id,
START,
PARENT_ID,
START
)
set.applyTo(motionLayout)
}
private fun setToEnd() {
val endSet = getConstraintSet(_simpleTransition?.endConstraintSetId ?: return)
endSet.clear(R.id.default_button, START)
endSet.connect(
R.id.default_button,
END,
PARENT_ID,
END
)
}
fun animateView() {
setToEnd()
_simpleTransition?.setOnSwipe(
OnSwipe().apply {
dragDirection = DRAG_END
touchAnchorId = R.id.default_button
touchAnchorSide = SIDE_START
onTouchUp = ON_UP_AUTOCOMPLETE_TO_START
setMaxAcceleration(500)
}
)
setTransition(_simpleTransition!!.id)
}
// Placeholder transition??
fun createPlaceholderTransition(motionScene: MotionScene): MotionScene.Transition? {
val startSetId = View.generateViewId()
val startSet = ConstraintSet()
startSet.clone(this)
val endSetId = View.generateViewId()
val endSet = ConstraintSet()
endSet.clone(this)
val transitionId = View.generateViewId()
return TransitionBuilder.buildTransition(
motionScene,
transitionId,
startSetId, startSet,
endSetId, endSet
)
}
/**
* Get px from dp
*/
private fun fromDp(context: Context, inDp: Int): Int {
val scale = context.resources.displayMetrics.density
return (inDp * scale).toInt()
}
}
Below is my adapter:
class SimpleMotionLayoutAdapter : RecyclerView.Adapter<SimpleMotionLayoutAdapter.ViewHolder>() {
val items = mutableListOf<Int>() // colors
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun setColor(color: Int) {
(view as SimpleMotionLayout).setSquareColor(color)
}
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = SimpleMotionLayout(parent.context)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.setColor(items[position])
}
override fun getItemCount(): Int = items.size
companion object {
const val TYPE_NORMAL = 0
const val TYPE_EXCEPTIONAL = 1
}
}
Am i missing implementation?
Thank you
In general you need to cache and restore the state of the MotionLayout when it gets Recycled.
Right now in onBindViewHolder you only set the Color.
Remember RecyclerView keeps a only a screens worth (+ about 3) of ViewHolders and reuses them using onBindViewHolder
At minimum you need to set the Progress of the MotionLayout.
Due to differences in timing you may need to set the progress in an onPost
I am trying to access the underlying data from a recyclerview item when it scrolls onto the screen.
I am using onAttachedToRecyclerView() inside my Adapter class. Then get the data within onScrolled().
Here is what I have so far:
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
val manager = recyclerView.layoutManager
Log.i("ProductAdapter","$manager")
if (manager is StaggeredGridLayoutManager) {
val slm: StaggeredGridLayoutManager = manager
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val firstVisibleItemPosition: Int
val firstVisibleItemPositions =
slm.findFirstVisibleItemPositions(null)
firstVisibleItemPosition = firstVisibleItemPositions[0]
if (firstVisibleItemPosition > -1) {
val item = getItem(firstVisibleItemPosition))
// do stuff with item data
}
}
})
}else{
Log.e("ProductAdapter","error")
}
}
when I set val manager = recyclerView.layoutManager, manager ends up being null. So the first if check never passes. My code is referencing this answer from another question.
Any ideas on how to fix this?
In my Fragment I was setting the adapter for the recyclerview one line before setting the layout manager.
Code before:
recyclerViewProducts.apply {
this.adapter = productAdapter
layoutManager = staggeredGridLayoutManager
itemAnimator?.changeDuration = 0
}
Code after:
recyclerViewProducts.apply {
layoutManager = staggeredGridLayoutManager
this.adapter = productAdapter
itemAnimator?.changeDuration = 0
}
Thanks to cactustictacs
So am having this recyclerview which will contain holders of multiple types one of which could be a scrollable horizontal list of edge to edge images, that are being scrolled automatically and have a current item indicator. so for this i used a viewholder which will itself contain another recyclerview and a dots indicator( which itself is another recycler view, so basically recyclerview = a list of vh , where one of the vh = 2 horizontal recyclerview).
title
[A,B,C,D...]
[+ ---]
title
[A,B,C,D...]
[+ --]
title
[A,B,C,D...]
[+ --]
title
[A,B,C,D...]
[+ --]
My innermost recylerview of horizontal images is created something like this:
class ImageAdapter : RecyclerView.Adapter<ImageVH>() {
var imageResList = mutableListOf<Int>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ImageVH(parent, viewType)
override fun onBindViewHolder(holder: ImageVH, pos: Int)
= holder.bindData(imageResList[pos % imageResList.size])
override fun getItemCount() = Int.MAX_VALUE
}
class ImageVH(v: View) : RecyclerView.ViewHolder(v) {
constructor(parent: ViewGroup, viewtype: Int) : this(
LayoutInflater.from(parent.context).inflate(R.layout.item_image, parent, false)
)
fun bindData(imageRes: Int) {
Glide.with(itemView.context).load("").error(imageRes).into(itemView.ivImage)
}
}
it is basically fooling the adapter to think as if i have a million images but will actually have just a few images. this creates an impression of circular scroll.
Next i will need something to change the dots indicator of the second recyclerview. for this i went into the parent of this recyclerview and attached an onScrollListener . The onScrollListener gives me 2 function: onScrolled and onScrollStateChanged.
with onScrolled , i determine when to change the next dots recyclerview's state to show the new dot. i do this via linear layout manager. when it gives findFirstCompletelyVisibleItemPosition as positive number .
with onScrollStateChanged(), i run a kind of recursion, where whenever i get the state as SCROLL_STATE_IDLE, I post a handler to scroll the recyclerview to next item after 2 seconds. after 2 seconds, it will automatically smooth scroll and again fire the same event, causing the handler to fire the same action again.
so the code looks something like this:
data class Rails(val title: String, val images: MutableList<Int>,val autoscroll:Boolean =false)
class RailsAdapter : RecyclerView.Adapter<RailVH>() {
var railsList = mutableListOf<Rails>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = RailVH(parent, viewType)
override fun onBindViewHolder(holder: RailVH, pos: Int) = holder.bindData(railsList[pos])
override fun getItemCount() = railsList.size
}
class RailVH(v: View) : RecyclerView.ViewHolder(v) {
constructor(parent: ViewGroup, viewtype: Int) : this(
LayoutInflater.from(parent.context).inflate(R.layout.item_rails, parent, false)
)
private var autoscrollImages = false
fun bindData(rails: Rails) {
autoscrollImages = rails.autoscroll
with(itemView) {
tvTitle?.text = rails.title
rvImagers?.apply {
adapter = ImageAdapter().also {
it.imageResList = rails.images
it.notifyDataSetChanged()
}
PagerSnapHelper().attachToRecyclerView(this)
isNestedScrollingEnabled = false
onFlingListener = null
addOnScrollListener(onScrollListener)
}
}
if(autoscrollImages){
bannerChangerHandler.postDelayed(bannerChangerRunnable,bannerChangerDelayMilllis)
}
}
private val onScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
//super.onScrolled(recyclerView, dx, dy)
val bannerLLManager = itemView.rvImagers?.layoutManager as? LinearLayoutManager
bannerLLManager?.let { linearLayoutManager ->
val bannerCurrentPos = linearLayoutManager.findFirstCompletelyVisibleItemPosition()
if (bannerCurrentPos >= 0) {
val rvDotsDataListSize = 5
val positionInRange = bannerCurrentPos % rvDotsDataListSize
Toast.makeText(
itemView.context,
"highlight dot #$positionInRange",
Toast.LENGTH_SHORT
).show()
}
}
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
//super.onScrollStateChanged(recyclerView, newState)
when (newState) {
RecyclerView.SCROLL_STATE_IDLE -> {
if(autoscrollImages){
Log.e(">>a>>", "RecyclerView.SCROLL_STATE_IDLE!")
bannerChangerHandler.postDelayed(bannerChangerRunnable, bannerChangerDelayMilllis
)
}
}
RecyclerView.SCROLL_STATE_DRAGGING -> {
Log.e(">>a>>", "RecyclerView.SCROLL_STATE_DRAGGING!")
bannerChangerHandler.removeCallbacks(bannerChangerRunnable)
}
else -> {
}
}
}
}
private val bannerChangerHandler: Handler = Handler()
private val bannerChangerRunnable = Runnable {
itemView.rvImagers?.apply {
val bannerManager = layoutManager as? LinearLayoutManager
bannerManager?.let {
val bannerCurrentPos = it.findFirstCompletelyVisibleItemPosition()
smoothScrollToPosition(bannerCurrentPos + 1)
}
}
}
private var bannerChangerDelayMilllis = 2000L
}
for brevity, assume whenever the toast is occuring, its going to scroll the 2nd dots indicator recyclerview .
This all seems to work in principle, but after sometimes the handler seems to fire twice or thrice , causing bad ux. sometimes it even goes berserks and stops showing any logs or anything and just makes the rails run infinetely very fast, like handler firing an autoscroll runner every millisecond.
handlers firing 2-3 times
So any help with this? i am assuming something is wrong at the implementation level, like firing handler events could be handled better?
Update:
thanks to #ADM , I got this working. I tweaked it as per my requirements, and had to forgo of circular scroll support in the reverse direction, but the given solution was enough to answer my query. thanks!
Handler is not an issue here its the Runnable. you are using and posting same Runnable each time thats why its getting piled up . You can not remove the previous call because you do not have a Tag or token to this delayed call . take a look at some of Handler's method like sendMessageDelayed these might help .
After giving it some thought i think you can move the Auto scroll part to SnapHelper. Not a full prove solution but i think it will work. You might have to put few checks in SnapHelper . Give it a try and let me know . i haven't tested it.
class AutoPagedSnapHelper(private var autoScrollInterval: Long) : PagerSnapHelper() {
private var recyclerView: RecyclerView? = null
private var currentPage = 0
private var isHold = false
private val autoScrollRunnable = Runnable {
recyclerView?.let {
if (recyclerView?.scrollState != RecyclerView.SCROLL_STATE_DRAGGING && !isHold) {
if (it.adapter != null) {
val lastPageIndex = (recyclerView?.adapter!!.itemCount - 1)
var nextIndex: Int
nextIndex = currentPage + 1
if (currentPage == lastPageIndex) {
nextIndex = 0
}
it.post {
val linearSmoothScroller = object : LinearSmoothScroller(recyclerView?.context) {
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi
}
}
linearSmoothScroller.targetPosition = nextIndex
(recyclerView?.layoutManager as LinearLayoutManager).startSmoothScroll(linearSmoothScroller)
}
}
} else {
postNextPage()
}
}
}
override fun attachToRecyclerView(recyclerView: RecyclerView?) {
super.attachToRecyclerView(recyclerView)
if (this.recyclerView === recyclerView) {
return
}
if (autoScrollInterval != 0L) {
this.recyclerView = recyclerView
this.recyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE || newState == RecyclerView.SCROLL_STATE_SETTLING) {
val itemPosition = (recyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition()
if (itemPosition != -1) {
currentPage = itemPosition
postNextPage()
}
}
}
})
postNextPage()
recyclerView?.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
override fun onInterceptTouchEvent(rv: RecyclerView, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
isHold = true
}
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
isHold = false
}
}
return false
}
override fun onTouchEvent(rv: RecyclerView, event: MotionEvent) {}
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
})
}
}
fun postNextPage() {
recyclerView?.handler?.removeCallbacks(autoScrollRunnable)
recyclerView?.postDelayed(autoScrollRunnable, autoScrollInterval)
}
companion object {
private const val MILLISECONDS_PER_INCH = 75f //default is 25f (bigger = slower)
}
}
This should take care of auto change page. You do not have to use scrollListener in Adapter. Give it a try.
I need to create a list with full screen videos.I used PagerSnapHelper for full screen child item. I have a single instance of Exoplayer and change video on scroll. Video also change on horizontally scroll.
RecyclerView.OnScrollListener() methods also called on horizontal scrolling.
Please suggest how I resolve these Issues.
RecyclerView should only scroll on vertical scrolling.
RecyclerView.OnScrollListener() should not call on horizontal scrolling
1. Scrolling Listener
class SnapOnScrollListener(
private val snapHelper: SnapHelper,
var behavior: Behavior = Behavior.NOTIFY_ON_SCROLL,
var onSnapPositionChangeListener: OnSnapPositionChangeListener? = null
) : RecyclerView.OnScrollListener() {
enum class Behavior {
NOTIFY_ON_SCROLL,
NOTIFY_ON_SCROLL_STATE_IDLE
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
System.out.println("dx.. $dx dy $dy")
if (behavior == Behavior.NOTIFY_ON_SCROLL) {
maybeNotifySnapPositionChange(recyclerView)
System.out.println("dx.. $dx dy $dy snapChange")
}
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
System.out.println("dx......")
if ( behavior == Behavior.NOTIFY_ON_SCROLL_STATE_IDLE
&& newState == RecyclerView.SCROLL_STATE_IDLE
) {
maybeNotifySnapPositionChange(recyclerView)
System.out.println("dx......SnapChange")
}
}
private fun maybeNotifySnapPositionChange(recyclerView: RecyclerView) {
val snapPosition = snapHelper.getSnapPosition(recyclerView)
val snapPositionChanged =
(snapPosition != RecyclerView.NO_POSITION)
if (snapPositionChanged) {
onSnapPositionChangeListener?.onSnapPositionChange(snapPosition)
}
}
fun SnapHelper.getSnapPosition(recyclerView: RecyclerView): Int {
val layoutManager = recyclerView.layoutManager ?: return RecyclerView.NO_POSITION
val snapView = findSnapView(layoutManager) ?: return RecyclerView.NO_POSITION
return layoutManager.getPosition(snapView)
}
}
RecyclerView Adapter
private fun manageLiveShowList() {
liveShowAdapter = LiveShowAdapter(List.productList)
val layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
mLiveShowRecycler?.layoutManager = layoutManager
mLiveShowRecycler?.adapter = liveShowAdapter
snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(mLiveShowRecycler)
manageScrollListener(snapHelper)
}
3. Add Scroll Listener
private fun manageScrollListener(snapHelper: SnapHelper) {
val snapOnScrollListener = SnapOnScrollListener(
snapHelper,
SnapOnScrollListener.Behavior.NOTIFY_ON_SCROLL,
this
)
mLiveShowRecycler?.addOnScrollListener(snapOnScrollListener)
}
To disable scrolling of the RecyclerView you can modify layout manager as it has canScrollVertically and canScrollHorizontally methods. Note: the content inside of the view holders in recycler view can be scrollable by itself. That is another issue.
In your case override canScrollHorizontally to always return false:
private fun manageLiveShowList() {
liveShowAdapter = LiveShowAdapter(List.productList)
val layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) {
override fun canScrollHorizontally(): Boolean = false
}
mLiveShowRecycler?.layoutManager = layoutManager
mLiveShowRecycler?.adapter = liveShowAdapter
snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(mLiveShowRecycler)
manageScrollListener(snapHelper)
}
I have setup a swipe gesture on the recyclerView item in order to edit it. I've done it like this:
private val swipeHelper = object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition //get position which is swipe
if (direction == ItemTouchHelper.RIGHT) {
… // do stuff
}
}
override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
val icon: Bitmap
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
val itemView = viewHolder.itemView
val height: Float = (itemView.bottom.toFloat() - itemView.top.toFloat())
val width = height / 3
if (dX > 0) {
paint.color = Color.parseColor("#FF9300")
val background = RectF(itemView.left.toFloat(), itemView.top.toFloat(), dX, itemView.bottom.toFloat())
c.drawRect(background, paint)
icon = BitmapFactory.decodeResource(resources, R.drawable.pencil)
val iconDest = RectF(
itemView.left.toFloat() + width,
itemView.top.toFloat() + width,
itemView.left.toFloat() + 2*width,
itemView.bottom.toFloat() - width)
c.drawBitmap(icon, null, iconDest, paint)
}
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
}
}
The problem I am facing, and that I cannot figure out how to resolve is that after the swipe, since I am not deleting the element, the animatino won't reset until I completely restart the activity.
Here is an explanatory image:
Has anyone ever dealt with this problem? Any Idea on how to fix it without having to restart the whole activity?
Call notifyItemChanged on you adapter. This will reset the adapter.
You can Do this in your Activity by calling notifyDatasetChanged(); or notifyItemChanged(); :
ItemTouchHelper.SimpleCallback callback = new RecyclerItemTouchHelper(this, 0, ItemTouchHelper.LEFT
, new RecyclerItemTouchHelper.RecyclerItemTouchHelperListener() {
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, final int position) {
if (direction == ItemTouchHelper.LEFT) {
// do what you want here and after that call the function below:
recyclerAdapter.notifyItemChanged(position);
}
}
});
new ItemTouchHelper(callback).attachToRecyclerView(recyclerView);