c# 实现刷卡
Let's say a user of your site wants to edit a list item without opening the item and looking for editing options. If you can enable this functionality, it gives that user a good User Experience.
假设您网站的用户想要在不打开列表项并寻找编辑选项的情况下编辑列表项。 如果您可以启用此功能,它将为该用户带来良好的用户体验 。
Pocket, a bookmarking app owned by Mozilla, does something similar. You can share/archive/delete your saved articles directly from the list without opening the article. Then you can click the menu button in the top-right corner and select your edit option.
Pocket是Mozilla拥有的书签应用程序,它执行类似的操作。 您可以直接从列表中共享/存档/删除保存的文章,而无需打开文章。 然后,您可以单击右上角的菜单按钮,然后选择编辑选项。
So in this tutorial we'll try to code this one out.
因此,在本教程中,我们将尝试对此进行编码。
Here's what we want to achieve:
这是我们要实现的目标 :
首先让我们创建一个普通的RecyclerView列表 (First let’s create a normal RecyclerView list)
RecyclerView is an advanced and flexible version of ListView and GridView. It's capable of holding large amounts of list data and has better performance than its predecessors.
RecyclerView是ListView和GridView的高级且灵活的版本。 它能够保存大量列表数据,并且比以前的版本具有更好的性能。
As the name suggests, RecyclerView 'recycles' the items of our list once it's out of view on scrolling and re-populates them when they come back to view. So the list container has to maintain only a limited number of views and not the entire list.
顾名思义,RecyclerView在滚动时看不到列表时会“回收”列表中的项目,并在它们重新显示时重新填充它们。 因此,列表容器只需要维护有限数量的视图,而不必维护整个列表。
It's so flexible that the new ViewPager2 class, used to create swipe-able tabs, is written over RecyclerView.
它非常灵活,可以在RecyclerView上编写用于创建可滑动标签的新ViewPager2类。
创建一个POJO(普通的旧Java对象)以保存列表数据 (Create a POJO (Plain Old Java Object) to hold the list data)
public class RecyclerEntity {private String title;private boolean showMenu = false;private int image;public RecyclerEntity() {}public RecyclerEntity(String title, int image, boolean showMenu) {this.title = title;this.showMenu = showMenu;this.image = image;}public int getImage() {return image;}public void setImage(int image) {this.image = image;}//... all the getters and setters
}
Notice we have a showMenu member here which will handle the visibility of the menu for that list item in our RecyclerView.
请注意,这里有一个showMenu成员,它将处理RecyclerView中该列表项的菜单可见性。
创建一个RecyclerView适配器 (Create a RecyclerView Adapter)
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {List<RecyclerEntity> list;Context context;public RecyclerAdapter(Context context, List<RecyclerEntity> articlesList) {this.list = articlesList;this.context = context;}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View v;v= LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_list, parent, false);return new MyViewHolder(v);}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {RecyclerEntity entity = list.get(position);if(holder instanceof MyViewHolder){((MyViewHolder)holder).title.setText(entity.getTitle());((MyViewHolder)holder).imageView.setImageDrawable(context.getResources().getDrawable(entity.getImage())); }}@Overridepublic int getItemCount() {return list.size();}public class MyViewHolder extends RecyclerView.ViewHolder {TextView title;ImageView imageView;ConstraintLayout container;public MyViewHolder(View itemView) {super(itemView);title = itemView.findViewById(R.id.title);imageView = itemView.findViewById(R.id.imageView);container = itemView.findViewById(R.id.container);}}
}
Usually we put our ViewHolder sub class (MyViewHolder) in the super class template. This lets us directly return our defined ViewHolder subclass object from the onCreateViewHolder() method. Then we don't have to cast it again and again in onBindViewHolder() method.
通常,我们将ViewHolder子类(MyViewHolder)放在超类模板中。 这使我们可以从onCreateViewHolder()方法直接返回定义的ViewHolder子类对象。 然后,我们不必在onBindViewHolder()方法中一次又一次地投射它。
But here we can't do that, and we'll learn why in a minute.
但是在这里我们无法做到这一点,我们将在一分钟内了解原因。
在活动中初始化RecyclerView (Initialise the RecyclerView in the Activity)
public class MainActivity extends AppCompatActivity {RecyclerView recyclerView;List<RecyclerEntity> list;RecyclerAdapter adapter;@RequiresApi(api = Build.VERSION_CODES.M)@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);recyclerView = findViewById(R.id.recyclerview);list = new ArrayList<>();list.add(new RecyclerEntity("This is the best title", R.drawable.one, false));list.add(new RecyclerEntity("This is the second-best title", R.drawable.two, false));//... rest of the list itemsadapter = new RecyclerAdapter(this, list);recyclerView.setLayoutManager(new LinearLayoutManager(this));recyclerView.setAdapter(adapter);}
}
Now let's start making things a little more interesting.
现在,让我们开始让事情变得更加有趣。
为菜单创建布局资源 (Create a layout resource for the menu )
And initialise it in Recycler Adapter:
并在Recycler Adapter中对其进行初始化:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {List<RecyclerEntity> list;Context context;private final int SHOW_MENU = 1;private final int HIDE_MENU = 2;public RecyclerAdapter(Context context, List<RecyclerEntity> articlesList) {this.list = articlesList;this.context = context;}@Overridepublic int getItemViewType(int position) {if(list.get(position).isShowMenu()){return SHOW_MENU;}else{return HIDE_MENU;}}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View v;if(viewType==SHOW_MENU){v= LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_menu, parent, false);return new MenuViewHolder(v);}else{v= LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_list, parent, false);return new MyViewHolder(v);}}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {RecyclerEntity entity = list.get(position);if(holder instanceof MyViewHolder){//... same as above}if(holder instanceof MenuViewHolder){//Menu Actions}}@Overridepublic int getItemCount() {return list.size();}public class MyViewHolder extends RecyclerView.ViewHolder {//... same as above}//Our menu viewpublic class MenuViewHolder extends RecyclerView.ViewHolder{public MenuViewHolder(View view){super(view);}}
}
Now we have two ViewHolder sub-classes in our adapter, MyViewHolder (the actual list item) and MenuViewHolder. Both inherit the same class so we return the parent class RecyclerView.ViewHolder from onCreateViewHolder().
现在我们的适配器中有两个ViewHolder子类,MyViewHolder(实际的列表项)和MenuViewHolder。 两者都继承同一个类,因此我们从返回父类RecyclerView.ViewHolder onCreateViewHolder()。
Our getItemViewType() method returns the int variable (viewType) which tells the kind of view we want to show in our RecyclerView for a particular position: that is, either MyViewHolder or MenuViewHolder.
我们的getItemViewType()方法返回int变量(viewType),该变量告诉我们要在RecyclerView中显示的特定位置的视图类型:MyViewHolder或MenuViewHolder。
This viewType variable is then used by onCreateViewHolder() which actually returns the respective ViewHolder object.
然后,onCreateViewHolder()使用此viewType变量,该变量实际上返回相应的ViewHolder对象。
在RecyclerAdapter中添加功能以显示/隐藏菜单 (Add the functions to show/hide menu in RecyclerAdapter)
public void showMenu(int position) {for(int i=0; i<list.size(); i++){list.get(i).setShowMenu(false);}list.get(position).setShowMenu(true);notifyDataSetChanged();}public boolean isMenuShown() {for(int i=0; i<list.size(); i++){if(list.get(i).isShowMenu()){return true;}}return false;}public void closeMenu() {for(int i=0; i<list.size(); i++){list.get(i).setShowMenu(false);}notifyDataSetChanged();}
Note that there are many ways to handle this. But for simplicity's sake we're keeping a boolean value in our POJO to maintain the menu's visibility.
请注意,有很多方法可以解决此问题。 但是为了简单起见,我们在POJO中保留一个布尔值以保持菜单的可见性。
After changing our data list, we call the notifyDataSetChanged() method to redraw the list.
更改数据列表后,我们调用notifyDataSetChanged()方法重绘该列表。
长按RecyclerAdapter中的列表项显示菜单 (Show the menu on long press of our list item in RecyclerAdapter)
@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {RecyclerEntity entity = list.get(position);if(holder instanceof MyViewHolder){((MyViewHolder)holder).title.setText(entity.getTitle());((MyViewHolder)holder).imageView.setImageDrawable(context.getResources().getDrawable(entity.getImage()));((MyViewHolder)holder).container.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {showMenu(position);return true;}});}if(holder instanceof MenuViewHolder){//Set Menu Actions like://((MenuViewHolder)holder).edit.setOnClickListener(null);}}
Again, setting events on our views can also be done in various ways.
同样,也可以通过各种方式在我们的视图上设置事件。
In our example, we have three actions in our menu. You can write your logic to handle those actions in the second if statement like shown in the comments.
在我们的示例中,菜单中包含三个操作。 您可以在第二条if语句中编写逻辑来处理这些动作,如注释中所示。
滑动显示菜单 (Show the menu on swipe )
To do this, we add a touch helper in our MainActivity.java:
为此,我们在MainActivity.java中添加了一个触摸助手:
public class MainActivity extends AppCompatActivity {RecyclerView recyclerView;List<RecyclerEntity> list;RecyclerAdapter adapter;@RequiresApi(api = Build.VERSION_CODES.M)@Overrideprotected void onCreate(Bundle savedInstanceState) {//... same as above adapter = new RecyclerAdapter(this, list);recyclerView.setLayoutManager(new LinearLayoutManager(this));recyclerView.setAdapter(adapter);ItemTouchHelper.SimpleCallback touchHelperCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {private final ColorDrawable background = new ColorDrawable(getResources().getColor(R.color.background));@Overridepublic boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {return false;}@Overridepublic void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {adapter.showMenu(viewHolder.getAdapterPosition());}@Overridepublic void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);View itemView = viewHolder.itemView;if (dX > 0) {background.setBounds(itemView.getLeft(), itemView.getTop(), itemView.getLeft() + ((int) dX), itemView.getBottom());} else if (dX < 0) {background.setBounds(itemView.getRight() + ((int) dX), itemView.getTop(), itemView.getRight(), itemView.getBottom());} else {background.setBounds(0, 0, 0, 0);}background.draw(c);}};ItemTouchHelper itemTouchHelper = new ItemTouchHelper(touchHelperCallback);itemTouchHelper.attachToRecyclerView(recyclerView);}
We call the showMenu() function inside our adapter when a list item is swiped.
刷列表项时,我们在适配器内部调用showMenu()函数。
The onChildDraw() function draws the background while we swipe. Otherwise there'll be a white background while swiping and our menu layout will show up with a pop.
滑动时,onChildDraw()函数绘制背景。 否则,在滑动时会出现白色背景,并且菜单布局会弹出。
隐藏菜单 (Hiding the menu)
There are three ways to hide our menu.
隐藏菜单的方法有三种。
- Hiding the menu when another row is swiped: 滑动另一行时隐藏菜单:
This case is already handled in showMenu() method in our Adapter. Before showing the menu for any row, we first call setShowMenu(false) for all the rows to hide the menu.
这种情况已经在我们适配器的showMenu()方法中处理过。 在显示任何行的菜单之前,我们首先为所有行调用setShowMenu(false)以隐藏菜单。
2. Hiding the menu when the back button is pressed (in our Activity):
2.当按下后退按钮时隐藏菜单(在我们的活动中):
@Overridepublic void onBackPressed() {if (adapter.isMenuShown()) {adapter.closeMenu();} else {super.onBackPressed();}}
3. Hiding the menu when a user scrolls the list:
3.当用户滚动列表时隐藏菜单:
recyclerView.setOnScrollChangeListener(new View.OnScrollChangeListener() {@Overridepublic void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {adapter.closeMenu();}});
Though pocket only has a long-press action to show the menu, in this example we've added swipe to show the menu for added functionality. You can hide your menu item on swipe right/left again, but I think it might confuse the user.
尽管Pocket只有长按才能显示菜单,但在此示例中,我们添加了滑动以显示菜单以增加功能。 您可以再次向右/向左滑动隐藏菜单项,但我认为这可能会使用户感到困惑。
结语 (Wrapping up)
If your app has a very large dataset to show in a RecyclerView, this type of UX might not be the way to go. In that case you should have a bulk-edit sort of functionality.
如果您的应用程序有很大的数据集要显示在RecyclerView中,则可能无法采用这种类型的UX。 在这种情况下,您应该具有大量编辑功能。
Also if your edit options are more than what you can adjust in a RecyclerView row but you still want to show some quick actions, you can show a Bottomsheet dialog on long press of your item and it can have all your edit options. The Google Drive android app does exactly the same thing.
同样,如果您的编辑选项超出了您在RecyclerView行中可以调整的范围,但是您仍想显示一些快速操作,则可以在长按该项目时显示“底部工作表”对话框,并且可以拥有所有的编辑选项。 Google Drive Android应用程序执行的操作完全相同。
If you want to implement a simple swipe to delete function, the code for that can be found here on Github.
如果您想实现简单的滑动删除功能,可以在Github上找到相关代码。
You can also check the source code for this project on Github.
您也可以在Github上检查该项目的源代码 。
Visit 22Boxes.com for more Mobile & Web development resources.
访问22Boxes.com,以获取更多移动和Web开发资源。
翻译自: https://www.freecodecamp.org/news/how-to-implement-swipe-for-options-in-recyclerview/
c# 实现刷卡