【问题标题】:Android RecyclerView Button Click Effect Multiple Times When ScrollingAndroid RecyclerView按钮滚动时多次点击效果
【发布时间】:2016-05-26 23:06:51
【问题描述】:

我有一个RecyclerView,它列出了一个文本和一个按钮。 如果我单击按钮,Toast 文本会显示位置,按钮颜色将更改为 #343434。 当我点击一个按钮时效果很好,但如果我循环我的RecyclerView 另一个按钮颜色改变但不点击,只有背景颜色改变。

如下图所示: 首先,我单击项目 1 并出现 Toast 文本,按钮背景更改。 然后我向下循环,第 15 项的按钮背景正在改变。 最后我向上滚动到第一个位置项目 1 的按钮颜色仍然是原始颜色,并且项目 4 的按钮背景颜色发生了变化。

这是我的代码块:

public class HandleTouchRecyclerViewActivity extends BaseActivity implements ObservableScrollViewCallbacks {
private static final String TAG = HandleTouchRecyclerViewActivity.class.getSimpleName();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_handletouchrecyclerview);

    ObservableRecyclerView recyclerView = (ObservableRecyclerView) findViewById(R.id.scroll);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setHasFixedSize(true);
    recyclerView.setScrollViewCallbacks(this);
    recyclerView.setAdapter(new CustomAdapter(this, getDummyData()));
}

@Override
public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {
    Log.v(TAG, "onScrollChanged: scrollY: " + scrollY + " firstScroll: " + firstScroll + " dragging: " + dragging);
}

@Override
public void onDownMotionEvent() {
    Log.v(TAG, "onDownMotionEvent");
}

@Override
public void onUpOrCancelMotionEvent(ScrollState scrollState) {
    Log.v(TAG, "onUpOrCancelMotionEvent: scrollState: " + scrollState);
}

public static class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
    private Context mContext;
    private LayoutInflater mInflater;
    private ArrayList<String> mItems;

    public CustomAdapter(Context context, ArrayList<String> items) {
        mContext = context;
        mInflater = LayoutInflater.from(context);
        mItems = items;
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ViewHolder(mContext, mInflater.inflate(R.layout.list_item_handletouch, parent, false));
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int position) {
        viewHolder.textView.setText(mItems.get(position));
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView textView;
        Context context;

        public ViewHolder(Context context, View view) {
            super(view);
            this.context = context;
            this.textView = (TextView) view.findViewById(android.R.id.text1);
            view.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    click(getLayoutPosition() + 1,v);

                    v.setBackgroundColor(Color.parseColor("#343434"))

                }
            });
        }

        private void click(int i,View view) {
            String message = "Button " + i + " is clicked";
            view.setBackgroundColor(Color.parseColor("#343434"));
            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();

            Log.v(TAG, "click: " + message);
        }
    }
}

}

那么,我该如何避免呢? (Android中的截图和代码Android Observable-ScrollView

【问题讨论】:

  • 你能发布你的代码吗?
  • 我没有代码。我编辑了 Android-Observable ScrollView 。您可以在顶部看到链接。我刚刚编辑了 HandleTouchRecyclerView 活动。你可以在 github 页面上看到这个。谢谢
  • 对不起,没有代码我们如何跟踪错误?
  • 我在上面编辑了代码。

标签: android android-recyclerview


【解决方案1】:

您必须将项目的状态存储在某处,并在调用onBindViewHolder() 时恢复它们。

在RecyclerView中,view的数量和item count不一样;它通常小于那个值。

将首先创建项目 1-10 的视图,如果其中一些,例如项目 1-4 被滚动出去,为项目 1-4 创建的视图将被重用于/绑定到项目 11-14 等等上。 (我只是编造了数字。它们可能会因情况而异)

换句话说,视图创建一次,绑定多次。

在你的情况下,

  • 向下滚动时,首先创建并绑定到项目 1 的视图是 回收并绑定到第 15 项。
  • 当您再次向上滚动时,视图(第 15 项,深色按钮)会滚动出来并绑定到第 4 项
  • item 1 绑定到之前绑定到其他 item 的其他视图,它具有原始按钮颜色。

您需要跟踪所有项目状态,并让视图在onBindViewHolder() 中反映它们

编辑:

这些是在您滚动并单击按钮时发生的情况。 (为简单起见,我将项目数限制为 10 个,并假设屏幕上最多可以绘制 3 个项目。)

初始状态是这样的:

  • 查看 #1 - 文本:“项目 1”,原始颜色
  • 查看 #2 - 文本:“项目 2”,原始颜色
  • 查看 #3 - 文本:“第 3 项”,原色
  • 查看 #4 -(这是为将来使用而预先创建的,尚未显示)

这里,onCreateViewHolder() 被调用了 4 次;它将布局资源膨胀到视图中并为视图创建视图持有者。 onBindViewHolder() 也被调用了 4 次;设置视图的文本

然后你点击第 1 项,

  • 查看 #1 - 文本:“项目 1”,dark color
  • 查看 #2 - 文本:“项目 2”,原始颜色
  • 查看 #3 - 文本:“第 3 项”,原色
  • 视图 #4 -(尚未显示,原始颜色)

View #1 的按钮颜色现在变暗了。

向下滚动一点,

  • View #1 - (scrolled out, not visible, dark color)
  • 查看 #2 - 文本:“项目 2”,原始颜色
  • 查看 #3 - 文本:“第 3 项”,原色
  • View #4 - text:"Item 4", original color

现在视图 #1 已滚动。视图 #4 变得可见,onBindViewHolder() 将视图的文本设置为“项目 4”。

滚动更多,

  • 查看 #2 - 文本:“项目 2”,原始颜色
  • 查看 #3 - 文本:“第 3 项”,原色
  • 查看 #4 - 文本:“第 4 项”,原色
  • View #1 - text:"Item 5", dark color (now this view is recycled)

看最后一行;没有新创建视图,并调用 onBindViewHolder() 将视图 #1 的文本设置为“项目 5”。 但是,onBindViewHolder() 的实现不会设置按钮颜色。

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
    viewHolder.textView.setText(mItems.get(position));
}

而且您的 click() 方法不会跟踪单击了哪个项目。

private void click(int i,View view) {
    String message = "Button " + i + " is clicked";
    view.setBackgroundColor(Color.parseColor("#343434"))
    Toast.makeText(context, message, Toast.LENGTH_SHORT).show();

    Log.v(TAG, "click: " + message);
}

它只是改变视图的按钮颜色,并不关心点击了哪个项目。 所以onBindViewHolder()没有办法检查这个项目之前是否被点击过。

这是一种可能的解决方案:

// declare an array to check which item has been clicked
private boolean[] mIsItemClicked = new boolean[mItems.size()];

private void initClickedItems() {
    for (int i = 0; i < mIsItemClicked; ++i) {
        mIsItemClicked = false;
    }
}

private void click(int i,View view) {
    mIsItemClicked[i] = !mIsItemClicked[i]; // toggle
    String message = "Button " + i + " is clicked";
    view.setBackgroundColor(Color.parseColor("#343434"))
    Toast.makeText(context, message, Toast.LENGTH_SHORT).show();

    Log.v(TAG, "click: " + message);
}

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
    viewHolder.textView.setText(mItems.get(position));
    int buttonColor;
    if (mIsItemClicked[i])
        buttonColor = Color.parseColor("#343434");
    else
        buttonColor = Color.parseColor("#ffffff");  // whatever the original color is
    viewHolder.itemView.findViewById(R.id.button).setBackgroundColor(buttonColor);
}

每当调用onBindViewHolder() 时,它都会检查是否单击了该项目并设置按钮颜色。 click() 方法还检查单击了哪个项目。

【讨论】:

  • 伙计,请回答,但我需要更详细的答案。你能解释一下吗?^^
  • @YasinKaçmaz 我编辑了我的答案。您可以跳到“编辑:”并从那里开始阅读。
  • 很好的解释我也有同样的问题
  • 你好@nexus5x。我们尝试了您的答案,但对我不起作用。实际上,我们想在带有 recyclerview 的按钮上使用 onClicklistner。当我们点击按钮时,它会改变按钮的颜色,当我们向下滚动时,上面按钮的属性会自动重复到下一个按钮。如果您对如何使用 recyclerview 进行按钮单击有任何想法。您也可以通过邮件与我联系 (amit@wittyfeed.com)
  • @R.PSingh 这似乎与原始问题不太相关。我认为最好为它发布一个包含更多细节的新问题。
【解决方案2】:

您可以看到 nexus5x 的答案。他说视图 1-10 是首先创建的。这是真的,但不是那样的,RecyclerView 根据设备屏幕创建项目。例如:您有 2 个名为 A 和 B 的设备。设备 A 的屏幕更大,屏幕上适合 10 个项目,这很好。设备 B 的屏幕较小,屏幕上可能可以容纳 7 个项目。

所以在设备 A 中,首先创建了 10 个项目,向下滚动一些新项目将被创建。在设备 B 中,前 7 个项目将被创建,然后是 bla bla bla..

所以这个问题为我解决了。如果想在视图上添加一些开关或状态,您必须添加一些表示该状态的字段。

对不起,我的英语不好。如果您不理解下面的评论,我会尝试解释更多:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 2016-05-25
    • 1970-01-01
    • 2021-08-13
    • 1970-01-01
    • 2012-04-05
    相关资源
    最近更新 更多