偶然发现this video时,我正在查看Google的Material文档。它显示了一个卡片项目,可以向右滑动以收藏该项目。
我想反映此行为,但已失败多次。我可以找到的所有库和教程都是关于swipe-to-delete
的。我试图使两个视图相互堆叠,在其中应滑动顶部的一个,以便下面的一个可见。我尝试使用ItemTouchHelper
来实现这一点,但是该类似乎只能促进swipe-to-delete
和move
对列表动作进行重新排序。
该滑动动作如何实现?
答案 0 :(得分:6)
您需要使用ItemTouchHelper
dismiss
,并将支持拖放到RecyclerView
。RecyclerView
和Callback类一起使用,该类配置启用了哪种类型的交互,并在用户执行这些操作时接收事件。以下是示例代码如何将 ItemTouchHelper
与 RecyclerView
StackActivity
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import java.util.ArrayList;
public class StackActivity extends AppCompatActivity {
RecyclerView myRecyclerView;
private ArrayList<ItemModel> arrayList = new ArrayList<>();
FavAdapter favAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stack);
myRecyclerView = findViewById(R.id.myRecyclerView);
myRecyclerView.setLayoutManager(new LinearLayoutManager(this));
myRecyclerView.setHasFixedSize(true);
// here i'm adding dummy data inside list
addDataInList();
// setting adapter to RecyclerView
favAdapter = new FavAdapter(this, arrayList);
myRecyclerView.setAdapter(favAdapter);
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
// when user swipe thr recyclerview item to right remove item from avorite list
if (direction == ItemTouchHelper.RIGHT) {
favAdapter.addToFav(viewHolder.getAdapterPosition(), false);
}
// when user swipe thr recyclerview item to left remove item from avorite list
else if (direction == ItemTouchHelper.LEFT) {
favAdapter.addToFav(viewHolder.getAdapterPosition(), true);
}
}
}).attachToRecyclerView(myRecyclerView);
}
//method to add dummy data inside ourlist
private void addDataInList() {
arrayList.add(new ItemModel("Item 1", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 2", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 3", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 4", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 5", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 6", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 7", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 8", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 9", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 10", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 11", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 12", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 13", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 14", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 15", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 16", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 17", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 18", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 19", "https://i.stack.imgur.com/1dWdI.jpg", false));
arrayList.add(new ItemModel("Item 20", "https://i.stack.imgur.com/1dWdI.jpg", false));
}
}
activity_stack布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/myRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
FavAdapter类
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import java.util.ArrayList;
public class FavAdapter extends RecyclerView.Adapter<FavAdapter.ViewHolder> {
private Context context;
private ArrayList<ItemModel> arrayList = new ArrayList<>();
public FavAdapter(Context context, ArrayList<ItemModel> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.custom_fav_layout, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
// here i'm check that if item is already added in favorite or not
//based on boolean flag i'm managed to set weather the item is in favorite or not
// this flag is also use full to keep state of out favorite when we scroll our recyclerview
holder.ivFavImage.setImageResource(arrayList.get(position).isFavorite()
? R.drawable.ic_favorite : R.drawable.ic_fav_white);
holder.tvProductName.setText(arrayList.get(position).getItemName());
Glide.with(context)
.load(arrayList.get(position).getImageUrl())
.apply(new RequestOptions().
placeholder(R.drawable.ic_placeholder)
.error(R.drawable.ic_error))
.into(holder.ivProductImage);
}
// this method is used to add or remove item from favorite list when use swipe the recyclerview item using ItemTouchHelper
public void addToFav(int position, boolean flag) {
arrayList.get(position).setFavorite(flag);
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return arrayList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView ivProductImage, ivFavImage;
TextView tvProductName;
public ViewHolder(View itemView) {
super(itemView);
ivProductImage = itemView.findViewById(R.id.ivProductImage);
ivFavImage = itemView.findViewById(R.id.ivFavImage);
tvProductName = itemView.findViewById(R.id.tvProductName);
ivFavImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (arrayList.get(getAdapterPosition()).isFavorite()) {
arrayList.get(getAdapterPosition()).setFavorite(false);
} else {
arrayList.get(getAdapterPosition()).setFavorite(true);
}
notifyDataSetChanged();
}
});
}
}
}
custom_fav_layout布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardElevation="5dp"
app:cardUseCompatPadding="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/ivProductImage"
android:layout_width="match_parent"
android:layout_height="150dp"
android:scaleType="fitXY"
android:adjustViewBounds="true"
android:src="@color/colorNavBar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/tvProductName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/app_name"
android:paddingStart="5dp"
android:textColor="#FFFFFF"
android:textStyle="bold" />
<ImageView
android:id="@+id/ivFavImage"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/ic_favorite" />
</LinearLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
ItemModel damodel类
public class ItemModel {
boolean isFavorite;
String ItemName,imageUrl;
public ItemModel( String itemName, String imageUrl,boolean isFavorite) {
this.isFavorite = isFavorite;
ItemName = itemName;
this.imageUrl = imageUrl;
}
public boolean isFavorite() {
return isFavorite;
}
public void setFavorite(boolean favorite) {
isFavorite = favorite;
}
public String getItemName() {
return ItemName;
}
public void setItemName(String itemName) {
ItemName = itemName;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
}
R.drawable.ic_favorite
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF00"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
</vector>
drawable.ic_fav_white
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFF"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
</vector>
https://www.youtube.com/watch?v=GKD-SDPWD3k&feature=youtu.be
有关信息,您可以在下面的文章中查看
如果您想使用任何库而不是选中此
答案 1 :(得分:2)
默认的ItemTouchHelper
为onSwiped
提供了一个回调,该回调可以包含您选择的任何逻辑,而不仅仅是删除。您绝对可以使用将项目标记为收藏的代码。但是,我相信这需要对项目进行完全轻扫,而不是视频中所示的部分轻扫。
以下两种方法都使用Canvas和graphics类进行细粒度的控制,并且您应该能够反映行为。
This article介绍了滑动项目时如何显示操作按钮。它修改了SwipeRevealLayout library并删除了不必要的滑动方向处理。
有关更详细的分步说明,您也可以签出this article。当它显示“编辑”和“删除”按钮时,可以替换这些按钮的onClick回调中的代码,以将该项目标记为收藏。
答案 2 :(得分:2)
这是一个很好的效果,但是没有标准的现成方法可以执行AFAIK。 Android确实提供了一组用于创建效果的工具,所以让我们看一下如何实现效果。
方法
定义具有两层的布局:底层包含一个具有心形形状的容器。这将是充满活力的心脏。顶层将是向右滑动以显示基础层的层。
为心脏创建动画。下面,我将介绍一种方法,该方法基于azizbekian对堆栈溢出问题here的回答来创建“心跳”动画。
创建一个扩展ItemTouchHelper.SimpleCallback的类:在该类中,您将需要覆盖onChildDraw()
来照顾在其中设置的滑动面板的运动。以上1)的布局。 onChildDraw()
也是执行动画的好地方。当滑动视图滑动到您将定义的“触发点”时,将触发动画。此类具有其他需要重写的方法。见下文。
item_card.xml
这是RecyclerVIew
项的两层布局。在这里,我使用FrameLayouts
,但也可以使用其他视图组。这就是它的样子。您看到的心脏位于顶层。跳动的心在下面。可以根据项目是否喜欢将顶部心脏设置为可见/不可见。看起来像这样:
然后打开(我手动设置了translationX
。)
<FrameLayout android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:id="@+id/heartFrame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:visibility="gone">
<ImageView
android:id="@+id/heart"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="top|center_horizontal"
android:padding="8dp"
card_view:srcCompat="@drawable/heart" />
</FrameLayout>
<androidx.cardview.widget.CardView
android:id="@+id/slidingPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="visible"
card_view:cardBackgroundColor="@android:color/background_light"
card_view:cardElevation="5dp"
card_view:cardUseCompatPadding="true">
<ImageView
android:id="@+id/imageView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="35dp"
android:paddingTop="8dp"
card_view:srcCompat="@drawable/ic_android_green_24dp" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:paddingStart="8dp"
android:paddingBottom="8dp"
android:text="This is some text"
android:textSize="20sp" />
<ImageView
android:id="@+id/favorite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|start"
android:padding="8dp"
android:tint="@android:color/holo_red_dark"
card_view:srcCompat="@drawable/heart" />
</androidx.cardview.widget.CardView>
</FrameLayout>
心跳动画
这是azizbekian动画的封装版本。目标视图将是id = heart的视图。要为背景着色,可以使用以心脏为中心的circular reveal动画。
private AnimatorSet getHeartBeatAnimation(View target) {
final float from = 1.0f;
final float to = 1.3f;
ObjectAnimator scaleX = ObjectAnimator.ofFloat(target, View.SCALE_X, from, to);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y, from, to);
ObjectAnimator translationZ = ObjectAnimator.ofFloat(target, View.TRANSLATION_Z, from, to);
AnimatorSet set1 = new AnimatorSet();
set1.playTogether(scaleX, scaleY, translationZ);
set1.setDuration(100);
set1.setInterpolator(new AccelerateInterpolator());
ObjectAnimator scaleXBack = ObjectAnimator.ofFloat(target, View.SCALE_X, to, from);
ObjectAnimator scaleYBack = ObjectAnimator.ofFloat(target, View.SCALE_Y, to, from);
ObjectAnimator translationZBack = ObjectAnimator.ofFloat(target, View.TRANSLATION_Z, to, from);
Path path = new Path();
path.moveTo(0.0f, 0.0f);
path.lineTo(0.5f, 1.3f);
path.lineTo(0.75f, 0.8f);
path.lineTo(1.0f, 1.0f);
PathInterpolator pathInterpolator = new PathInterpolator(path);
AnimatorSet set2 = new AnimatorSet();
set2.playTogether(scaleXBack, scaleYBack, translationZBack);
set2.setDuration(300);
set2.setInterpolator(pathInterpolator);
AnimatorSet animSet = new AnimatorSet();
animSet.playSequentially(set1, set2);
return animSet;
}
onChildDraw()
请参阅onChildDraw()
here的文档。
int maxRightShift = 400; // Example only. This should not be hard-coded and should be set elsewhere.
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder,
float dX, float dY, int actionState, boolean isCurrentlyActive) {
MyRecyclerViewAdapter.ItemViewHolder vh = (MyRecyclerViewAdapter.ItemViewHolder) viewHolder;
// Don't let the sliding view slide more than maxRightShift amount.
if (dX >= maxRightShift && mFavoriteChangedPosition == RecyclerView.NO_POSITION) {
// Capture the position that has changed. Only on change per sliding event.
mFavoriteChangedPosition = vh.getAdapterPosition();
// Trigger the animation and do, potentially, some housekeeping.
// setFavoriteActivation will have the animation set and triggered.
vh.setFavoriteActivation(!vh.isFavorite());
}
// Shift just the CardView and leave underlying views.
vh.mCardView.setTranslationX(dX);
}
其他要在ItemTouchHelper.SimpleCallback
中覆盖的方法
onSelectedChanged()
和clearView
onMove()
和onSwiped()
-在这些方法中不执行任何操作isItemViewSwipeEnabled()
isLongPressDragEnabled()
还有很多细节,但这是重要部分的概述。