Android 在 Recyclerview 中使用 itemTouchHelper 在 cardview 上拖动 elevation translationz
Android drag elevation translationz on cardview with itemTouchHelper in Recyclerview
我在弄清楚如何在拖动时临时提升 cardView 时遇到问题。我将回收器视图与 cardViews 一起使用,这是我的 itemtouchhelper:
class ListTouchHelper extends ItemTouchHelper.Callback {
private final ActionCompletionContract contract;
public ListTouchHelper(ActionCompletionContract contract) {
this.contract = contract;
}
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
return makeMovementFlags(dragFlags, swipeFlags);
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
contract.onViewMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
if (direction == ItemTouchHelper.LEFT) {
contract.onViewSwipedLeft(viewHolder.getAdapterPosition());
} else if (direction == ItemTouchHelper.RIGHT) {
contract.onViewSwipedRight(viewHolder.getAdapterPosition());
}
}
public interface ActionCompletionContract {
void onViewMoved(int oldPosition, int newPosition);
void onViewSwipedLeft(int position);
void onViewSwipedRight(int position);
}
}
我已经设法暂时提升它:
这导致:(阴影以某种方式被剪掉了?)
但是,一旦稍微移动视图,高度就会消失:
我的问题是:如何在拖动卡片时获取高度(包括阴影)?
提前致谢!
好的,我解决了,有点。我认为由于某些其他原因它默认情况下不起作用,但是我设法编写了一个解决方法。
private boolean first = true; //first draw of cardView?
private boolean last = false; //last draw of cardView?
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
//add elevation on first draw
if (first) {
ViewPropertyAnimator animator = viewHolder.itemView.animate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //consider SDK version
viewHolder.itemView.setTranslationZ(7);
animator.start();
}
first = false;
}
//remove translationZ in last edit
if (last) {
ViewPropertyAnimator animator = viewHolder.itemView.animate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //consider SDK version
viewHolder.itemView.setTranslationZ(0);
animator.start();
}
//reset values
last=false;
first=true;
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
@Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
last = true; //only one more OnChildDrawWillBeCalled
}
以上代码已添加到您的 ItemTouchHelper.Callback 中,一切都应该有效。
基本思路是手动控制translationz的绘制。
为此,我弄清楚第一个 canvas 何时绘制,最后一个何时绘制,而 cancas 将显示阴影。
Maxbe 再补充一条评论:ViewPropertyAnimator 与 xml 布局文件的结合非常不直观且存在错误,因此如果可以避免,我建议您这样做,而是尝试以编程方式为视图更改设置动画并效果。
希望这对某人有所帮助。
@LivinTheNoobLife 在您的解决方案中使用了 ViewPropertyAnimator,但您没有为其设置任何转换,因此不会应用任何动画。
这是我的解决方案,具有正常工作的浮动动画:
class DragHelper extends ItemTouchHelper.Callback {
private boolean cardPicked = true;
private boolean reset = false;
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
// elevate only when picked for the first time
if (cardPicked) {
ViewPropertyAnimator animator = viewHolder.itemView.animate();
animator.translationZ(16);
animator.setDuration(200);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
cardPicked = false;
}
// when your item is not floating anymore
if (reset){
ViewPropertyAnimator animator = viewHolder.itemView.animate();
animator.translationZ(0);
animator.setDuration(200);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
cardPicked = true;
reset = false;
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
// As the doc says
// Called by the ItemTouchHelper when the user interaction with an element is over and it also completed its animation.
@Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
// interaction is over, time to reset our elevation
reset = true;
}
}
我在弄清楚如何在拖动时临时提升 cardView 时遇到问题。我将回收器视图与 cardViews 一起使用,这是我的 itemtouchhelper:
class ListTouchHelper extends ItemTouchHelper.Callback {
private final ActionCompletionContract contract;
public ListTouchHelper(ActionCompletionContract contract) {
this.contract = contract;
}
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
return makeMovementFlags(dragFlags, swipeFlags);
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
contract.onViewMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
if (direction == ItemTouchHelper.LEFT) {
contract.onViewSwipedLeft(viewHolder.getAdapterPosition());
} else if (direction == ItemTouchHelper.RIGHT) {
contract.onViewSwipedRight(viewHolder.getAdapterPosition());
}
}
public interface ActionCompletionContract {
void onViewMoved(int oldPosition, int newPosition);
void onViewSwipedLeft(int position);
void onViewSwipedRight(int position);
}
}
我已经设法暂时提升它:
这导致:(阴影以某种方式被剪掉了?)
但是,一旦稍微移动视图,高度就会消失:
我的问题是:如何在拖动卡片时获取高度(包括阴影)?
提前致谢!
好的,我解决了,有点。我认为由于某些其他原因它默认情况下不起作用,但是我设法编写了一个解决方法。
private boolean first = true; //first draw of cardView?
private boolean last = false; //last draw of cardView?
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
//add elevation on first draw
if (first) {
ViewPropertyAnimator animator = viewHolder.itemView.animate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //consider SDK version
viewHolder.itemView.setTranslationZ(7);
animator.start();
}
first = false;
}
//remove translationZ in last edit
if (last) {
ViewPropertyAnimator animator = viewHolder.itemView.animate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //consider SDK version
viewHolder.itemView.setTranslationZ(0);
animator.start();
}
//reset values
last=false;
first=true;
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
@Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
last = true; //only one more OnChildDrawWillBeCalled
}
以上代码已添加到您的 ItemTouchHelper.Callback 中,一切都应该有效。
基本思路是手动控制translationz的绘制。
为此,我弄清楚第一个 canvas 何时绘制,最后一个何时绘制,而 cancas 将显示阴影。
Maxbe 再补充一条评论:ViewPropertyAnimator 与 xml 布局文件的结合非常不直观且存在错误,因此如果可以避免,我建议您这样做,而是尝试以编程方式为视图更改设置动画并效果。
希望这对某人有所帮助。
@LivinTheNoobLife 在您的解决方案中使用了 ViewPropertyAnimator,但您没有为其设置任何转换,因此不会应用任何动画。
这是我的解决方案,具有正常工作的浮动动画:
class DragHelper extends ItemTouchHelper.Callback {
private boolean cardPicked = true;
private boolean reset = false;
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
// elevate only when picked for the first time
if (cardPicked) {
ViewPropertyAnimator animator = viewHolder.itemView.animate();
animator.translationZ(16);
animator.setDuration(200);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
cardPicked = false;
}
// when your item is not floating anymore
if (reset){
ViewPropertyAnimator animator = viewHolder.itemView.animate();
animator.translationZ(0);
animator.setDuration(200);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
cardPicked = true;
reset = false;
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
// As the doc says
// Called by the ItemTouchHelper when the user interaction with an element is over and it also completed its animation.
@Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
// interaction is over, time to reset our elevation
reset = true;
}
}