我有一个项目,我需要用两种方式在一行上滑动才能启动一个 Action 。 向左滑动应该从特定列表中删除有问题的项目(而不是从原始列表中删除它,从而将其留在 recyclerview 数据集中),向右滑动应该将有问题的项目添加到另一个列表(同样不是原始列表) . 我实现了一个 ItemTouchHelperAdapter 和一个 ItemTouchHelperCallback,我可以检测到向右/向左的滑动,但是 View 从屏幕上移开,剩下一个空白矩形。有什么想法吗?
public interface ItemTouchHelperAdapter {
/**
* Called when an item has been dismissed by a swipe.<br/>
* <br/>
* Implementations should call {@link RecyclerView.Adapter#notifyItemRemoved(int)} after
* adjusting the underlying data to reflect this removal.
*
* @param position The position of the item dismissed.
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
* @see RecyclerView.ViewHolder#getAdapterPosition()
*/
void onItemLeftSwipe(int position);
void onItemRightSwipe(int position);
}
ItemTouchHelperCallback
public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
private final ItemTouchHelperAdapter mAdapter;
public ItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
mAdapter = adapter;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
// int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(0, swipeFlags);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
if (direction == ItemTouchHelper.START)
mAdapter.onItemLeftSwipe(viewHolder.getAdapterPosition());
else if (direction == ItemTouchHelper.END)
mAdapter.onItemRightSwipe(viewHolder.getAdapterPosition());
else
System.out.println("direction: " + direction);
}
@Override
public boolean isLongPressDragEnabled() {
return false;
}
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
}
这是在我的适配器类中:
@Override
public void onItemLeftSwipe(int position) {
System.out.println("swiped left on " + mDataset.get(position).getName());
}
@Override
public void onItemRightSwipe(int position) {
System.out.println("swiped right on " + mDataset.get(position).getName());
}
最佳答案
public class MainActivity extends AppCompatActivity {
RecyclerView mRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
setUpRecyclerView();
}
private void setUpRecyclerView() {
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(new TestAdapter());
mRecyclerView.setHasFixedSize(true);
setUpItemTouchHelper();
setUpAnimationDecoratorHelper();
}
private void setUpItemTouchHelper() {
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
Drawable background;
Drawable xMark;
int xMarkMargin;
boolean initiated;
private void init() {
background = new ColorDrawable(Color.WHITE);
xMark = ContextCompat.getDrawable(MainActivity.this, R.drawable.ic_clear_24dp);
xMark.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP);
xMarkMargin = (int) MainActivity.this.getResources().getDimension(R.dimen.ic_clear_margin);
initiated = true;
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
{
int position = viewHolder.getAdapterPosition();
TestAdapter testAdapter = (TestAdapter)recyclerView.getAdapter();
if (testAdapter.isUndoOn() && testAdapter.isPendingRemoval(position))
{
return 0;
}
return super.getSwipeDirs(recyclerView, viewHolder);
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
int swipedPosition = viewHolder.getAdapterPosition();
TestAdapter adapter = (TestAdapter)mRecyclerView.getAdapter();
boolean undoOn = adapter.isUndoOn();
if (undoOn) {
adapter.pendingRemoval(swipedPosition);
} else {
adapter.remove(swipedPosition);
}
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
View itemView = viewHolder.itemView;
if (viewHolder.getAdapterPosition() == -1) {
return;
}
if (!initiated) {
init();
}
background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
background.draw(c);
int itemHeight = itemView.getBottom() - itemView.getTop();
int intrinsicWidth = xMark.getIntrinsicWidth();
int intrinsicHeight = xMark.getIntrinsicWidth();
int xMarkLeft = itemView.getRight() - xMarkMargin - intrinsicWidth;
int xMarkRight = itemView.getRight() - xMarkMargin;
int xMarkTop = itemView.getTop() + (itemHeight - intrinsicHeight)/2;
int xMarkBottom = xMarkTop + intrinsicHeight;
xMark.setBounds(xMarkLeft, xMarkTop, xMarkRight, xMarkBottom);
xMark.draw(c);
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
};
ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
mItemTouchHelper.attachToRecyclerView(mRecyclerView);
}
private void setUpAnimationDecoratorHelper() {
mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
Drawable background;
boolean initiated;
private void init() {
background = new ColorDrawable(Color.RED);
initiated = true;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (!initiated) {
init();
}
if (parent.getItemAnimator().isRunning())
{
View lastViewComingDown = null;
View firstViewComingUp = null;
int left = parent.getHeight();
int right = parent.getWidth();
int top = 0;
int bottom = 0;
int childCount = parent.getLayoutManager().getChildCount();
for (int i = 0; i < childCount; i++) {
View child = parent.getLayoutManager().getChildAt(i);
if (child.getTranslationY() < 0) {
// view is coming down
lastViewComingDown = child;
} else if (child.getTranslationY() > 0) {
// view is coming up
if (firstViewComingUp == null) {
firstViewComingUp = child;
}
}
}
if (lastViewComingDown != null && firstViewComingUp != null) {
// views are coming down AND going up to fill the void
top = lastViewComingDown.getBottom() + (int) lastViewComingDown.getTranslationY();
bottom = firstViewComingUp.getTop() + (int) firstViewComingUp.getTranslationY();
} else if (lastViewComingDown != null) {
// views are going down to fill the void
top = lastViewComingDown.getBottom() + (int) lastViewComingDown.getTranslationY();
bottom = lastViewComingDown.getBottom();
} else if (firstViewComingUp != null) {
// views are coming up to fill the void
top = firstViewComingUp.getTop();
bottom = firstViewComingUp.getTop() + (int) firstViewComingUp.getTranslationY();
}
background.setBounds(left, top, right, bottom);
background.draw(c);
}
super.onDraw(c, parent, state);
}
});
}
class TestAdapter extends RecyclerView.Adapter {
private static final int PENDING_REMOVAL_TIMEOUT = 3000; // 3sec
List<String> items;
List<String> itemsPendingRemoval;
int lastInsertedIndex; // so we can add some more items for testing purposes
boolean undoOn; // is undo on, you can turn it on from the toolbar menu
private Handler handler = new Handler(); // hanlder for running delayed runnables
HashMap<String, Runnable> pendingRunnables = new HashMap<>(); // map of items to pending runnables, so we can cancel a removal if need be
public TestAdapter() {
items = new ArrayList<>();
itemsPendingRemoval = new ArrayList<>();
// let's generate some items
lastInsertedIndex = 15;
// this should give us a couple of screens worth
for (int i=1; i<= lastInsertedIndex; i++) {
items.add("Item " + i);
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new TestViewHolder(parent);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
TestViewHolder viewHolder = (TestViewHolder)holder;
final String item = items.get(position);
if (itemsPendingRemoval.contains(item)) {
// we need to show the "undo" state of the row
//viewHolder.itemView.setBackgroundColor(Color.RED);
viewHolder.itemView.setBackgroundColor(Color.WHITE);
viewHolder.titleTextView.setVisibility(View.GONE);
//viewHolder.undoButton.setVisibility(View.VISIBLE);
viewHolder.undoButton.setVisibility(View.GONE);
viewHolder.undoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// user wants to undo the removal, let's cancel the pending task
Runnable pendingRemovalRunnable = pendingRunnables.get(item);
pendingRunnables.remove(item);
if (pendingRemovalRunnable != null) handler.removeCallbacks(pendingRemovalRunnable);
itemsPendingRemoval.remove(item);
// this will rebind the row in "normal" state
notifyItemChanged(items.indexOf(item));
}
});
} else {
// we need to show the "normal" state
viewHolder.itemView.setBackgroundColor(Color.WHITE);
viewHolder.titleTextView.setVisibility(View.VISIBLE);
viewHolder.titleTextView.setText(item);
viewHolder.undoButton.setVisibility(View.GONE);
viewHolder.undoButton.setOnClickListener(null);
}
}
@Override
public int getItemCount() {
return items.size();
}
public void addItems(int howMany){
if (howMany > 0) {
for (int i = lastInsertedIndex + 1; i <= lastInsertedIndex + howMany; i++) {
items.add("Item " + i);
notifyItemInserted(items.size() - 1);
}
lastInsertedIndex = lastInsertedIndex + howMany;
}
}
public void setUndoOn(boolean undoOn) {
this.undoOn = undoOn;
}
public boolean isUndoOn() {
return undoOn;
}
public void pendingRemoval(int position) {
final String item = items.get(position);
if (!itemsPendingRemoval.contains(item)) {
itemsPendingRemoval.add(item);
// this will redraw row in "undo" state
notifyItemChanged(position);
// let's create, store and post a runnable to remove the item
Runnable pendingRemovalRunnable = new Runnable() {
@Override
public void run() {
remove(items.indexOf(item));
}
};
handler.postDelayed(pendingRemovalRunnable, PENDING_REMOVAL_TIMEOUT);
pendingRunnables.put(item, pendingRemovalRunnable);
}
}
public void remove(int position) {
String item = items.get(position);
if (itemsPendingRemoval.contains(item)) {
itemsPendingRemoval.remove(item);
}
if (items.contains(item)) {
items.remove(position);
notifyItemRemoved(position);
}
}
public boolean isPendingRemoval(int position) {
String item = items.get(position);
return itemsPendingRemoval.contains(item);
}
}
static class TestViewHolder extends RecyclerView.ViewHolder
{
TextView titleTextView;
Button undoButton;
public TestViewHolder(ViewGroup parent)
{
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.row_view, parent, false));
titleTextView = (TextView) itemView.findViewById(R.id.title_text_view);
undoButton = (Button) itemView.findViewById(R.id.undo_button);
}
}
}
关于android - 如何在不关闭的情况下在android中的recyclerview上滑动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37388676/
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除