问题
I have a RecyclerView with items inside like this:
I use ItemTouchHelper.SimpleCallback
to listen for swipe and onChildDraw() to draw a canvas when items are swiped:
A bit more swipe:
My problem:
I want to simulate a swipe (at run time) just on first item inside the list of items; I need first item to go (more or less ) -100 px on its X axis and then go back to original position. How to achieve this?
回答1:
I created some utility method in case someone needs it
import android.animation.ValueAnimator
import android.os.SystemClock
import android.view.MotionEvent
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
/**
* @author Oleh Haidaienko
* @since 29.07.2020
*/
object SwipeUtils {
/**
* Programmatically swipe RecyclerView item
* @param recyclerView RecyclerView which item will be swiped
* @param index Position of item
* @param distance Swipe distance
* @param direction Swipe direction, can be [ItemTouchHelper.START] or [ItemTouchHelper.END]
* @param time Animation time in milliseconds
*/
fun swipeRecyclerViewItem(
recyclerView: RecyclerView,
index: Int,
distance: Int,
direction: Int,
time: Long
) {
val childView = recyclerView.getChildAt(index) ?: return
val x = childView.width / 2F
val viewLocation = IntArray(2)
childView.getLocationInWindow(viewLocation)
val y = (viewLocation[1] + childView.height) / 2F
val downTime = SystemClock.uptimeMillis()
recyclerView.dispatchTouchEvent(
MotionEvent.obtain(
downTime,
downTime,
MotionEvent.ACTION_DOWN,
x,
y,
0
)
)
ValueAnimator.ofInt(0, distance).apply {
duration = time
addUpdateListener {
val dX = it.animatedValue as Int
val mX = when (direction) {
ItemTouchHelper.END -> x + dX
ItemTouchHelper.START -> x - dX
else -> 0F
}
recyclerView.dispatchTouchEvent(
MotionEvent.obtain(
downTime,
SystemClock.uptimeMillis(),
MotionEvent.ACTION_MOVE,
mX,
y,
0
)
)
}
}.start()
}
}
回答2:
i guess you could achieve that with RecyclerView
by simply registering DataObserver
for your Adapter
which will notify you when an item has been removed, inserted or changed. lets consider this scenario
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override public void onChanged() {
super.onChanged();
adapter.unregisterAdapterDataObserver(this);//unregister the adapter since no longer needed.
}
});
in above code will notify you when an item has been inserted you could then use recyclerView.getChildAt(0).translateX(-100);
to simulate the swipe and simply recyclerView.getChildAt(0).translateX(0);
to reposition
the X
of the view
back within the method onChanged()
.
回答3:
This extension functions could help you:
fun ViewGroup.performSwipeToLeft(target: View, distance: Float) {
this.performSwipe(target, distanceX = -distance, distanceY = 0f)
}
fun ViewGroup.performSwipeToRight(target: View, distance: Float) {
this.performSwipe(target, distanceX = +distance, distanceY = 0f)
}
fun ViewGroup.performSwipe(target: View, distanceX: Float, distanceY: Float) {
val parentCoords = intArrayOf(0, 0)
this.getLocationInWindow(parentCoords)
val childCoords = intArrayOf(0, 0)
target.getLocationInWindow(childCoords)
val initGlobalX = childCoords[0].toFloat() + 1f
val initGlobalY = childCoords[1].toFloat() + 1f
val initLocalX = (childCoords[0] - parentCoords[0]).toFloat() + 1f
val initLocalY = (childCoords[1] - parentCoords[1]).toFloat() + 1f
val downTime = SystemClock.uptimeMillis()
var eventTime = SystemClock.uptimeMillis()
this.dispatchTouchEvent(
MotionEvent.obtain(
downTime,
eventTime,
MotionEvent.ACTION_DOWN,
initGlobalX,
initGlobalY,
0
).apply {
setLocation(initLocalX, initLocalY)
source = InputDevice.SOURCE_TOUCHSCREEN
}
)
val steps = 20
var i = 0
while (i in 0..steps) {
val globalX = initGlobalX + i * distanceX / steps
val globalY = initGlobalY + i * distanceY / steps
val localX = initLocalX + i * distanceX / steps
val localY = initLocalY + i * distanceY / steps
if (globalX <= 10f || globalY <= 10f) {
break
}
this.dispatchTouchEvent(
MotionEvent.obtain(
downTime,
++eventTime,
MotionEvent.ACTION_MOVE,
globalX,
globalY,
0
).apply {
setLocation(localX, localY)
source = InputDevice.SOURCE_TOUCHSCREEN
}
)
i++
}
this.dispatchTouchEvent(
MotionEvent.obtain(
downTime,
++eventTime,
MotionEvent.ACTION_UP,
initGlobalX + i * distanceX,
initGlobalY + i * distanceY,
0
).apply {
setLocation(initLocalX + i * distanceX, initLocalY + i * distanceY)
source = InputDevice.SOURCE_TOUCHSCREEN
}
)
}
In your case you could use this as follows:
recylerView.performSwipeToLeft(recyclerView.getChildAt(0), 100f)
来源:https://stackoverflow.com/questions/34401166/how-to-swipe-one-row-of-recyclerview-programatically-at-run-time