android - RecyclerView 每秒更新一次并有一个 longPressListener

android - RecyclerView updating every second and have a longPressListener

我有一个 RecyclerView,在每个列表项中都有一个计数器,当计数器回调被调用时,它更新了相关字段并调用 nottifyDataSetChanged

它工作正常,但问题是,我还附加了一个长按监听器,因为当用户按下项目时数据会更新,按下就会丢失

我该如何处理?

更新代码:

public void setCountDown(int countDown){
    mCountDown = countDown;
    notifyDataSetChanged();
}

长按监听器:

    holder.container.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            FragmentManager fm = ((AppCompatActivity)activity).getSupportFragmentManager();
            EditAccountFragment editAccountFragmentDialog = EditAccountFragment.newInstance(_id);
            editAccountFragmentDialog.show(fm, "fragment_edit_account");

            return false;
        }
    });

根据请求添加适配器

public class AccountAdapter extends RecyclerView.Adapter<AccountAdapter.ItemViewHolder> {

    private static final String TAG = "ITEM_ADAPTER";

    private Cursor itemCursor;
    private Activity activity;
    private ItemColumnIds feedColumnIds;
    private int mCountDown;
    private boolean ignoreDatasetChange = false;

    /**
     * initilaizer for recycler view adapter, takes in activity although better way is to handle
     * activity related tasks within activiyt, but this is just done to speed up the overall process
     * @param activity
     */
    public AccountAdapter(Activity activity){
        this.activity = activity;
    }

    @Override
    public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(activity).inflate(R.layout.account_item,
                parent, false);

        feedColumnIds = new ItemColumnIds();

        if (itemCursor != null){
            // initilize column indices information
            feedColumnIds.colId = itemCursor.getColumnIndex(
                    AuthContract.AccountEntry._ID);
            feedColumnIds.colAcctId = itemCursor.getColumnIndex(
                    AuthContract.AccountEntry.COLUMN_USERNAME);
            feedColumnIds.colPin = itemCursor.getColumnIndex(
                    AuthContract.AccountEntry.COLUMN_PIN);
            feedColumnIds.colIssuer = itemCursor.getColumnIndex(
                    AuthContract.AccountEntry.COLUMN_ISSUER);
        }

        return new ItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final ItemViewHolder holder, int position) {

        if(itemCursor == null)
            return;

        if(!itemCursor.moveToPosition(position))
            return;

        //get values from cursor
        String pin = itemCursor.getString(feedColumnIds.colPin);
        String email = itemCursor.getString(feedColumnIds.colAcctId);
        String issuer = itemCursor.getString(feedColumnIds.colIssuer);
        final long _id = itemCursor.getLong(feedColumnIds.colId);


        //set view values
        holder.tvEmail.setText(email);
        holder.tvPin.setText(pin);
        holder.tvIssuer.setText(issuer);
        holder.apCountDown.setProgress(mCountDown);

        holder.container.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String pin = holder.tvPin.getText().toString();
                ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
                ClipData clip = ClipData.newPlainText("Secret Key", pin);
                clipboard.setPrimaryClip(clip);

                Toast.makeText(activity, "OTP code "+ pin +" copied to clipboard", Toast.LENGTH_SHORT).show();

            }
        });

        holder.container.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                FragmentManager fm = ((AppCompatActivity)activity).getSupportFragmentManager();
                EditAccountFragment editAccountFragmentDialog = EditAccountFragment.newInstance(_id);
                editAccountFragmentDialog.show(fm, "fragment_edit_account");

                return false;
            }

        });

    }

    @Override
    public int getItemCount() {
        if(itemCursor == null)
            return 0;
        return itemCursor.getCount();
    }



    /**
     * close previurous cursor if any and replace that with the new on from host class
     * @param cursor
     */
    public void swapCursor(Cursor cursor) {
        if(itemCursor != null) {
            itemCursor.close();
            itemCursor = null;
        }

        itemCursor = cursor;
        notifyDataSetChanged();

    }

    public void setCountDown(int countDown){
        mCountDown = countDown;

        if(!ignoreDatasetChange)
            notifyDataSetChanged();
    }

    /**
     * View golder for auction item entry
     */
    public class ItemViewHolder extends RecyclerView.ViewHolder {
        TextView tvEmail;
        TextView tvPin;
        TextView tvIssuer;
//        TextView tvCountDown;
        ArcProgress apCountDown;
        View container;

        public ItemViewHolder(View itemView) {
            super(itemView);
            tvEmail = (TextView) itemView.findViewById(R.id.tv_email);
            tvPin = (TextView) itemView.findViewById(R.id.tv_pin);
            tvIssuer = (TextView) itemView.findViewById(R.id.tv_issuer);
            apCountDown = (ArcProgress) itemView.findViewById(R.id.arc_progress);
            container = itemView.findViewById(R.id.container);
        }
    }

    /**
     * databse column id holder for auction item
     */
    class ItemColumnIds {

        public int colId;
        public int colPin;
        public int colAcctId;
        public int colIssuer;

    }

}

我认为您应该考虑在用户触摸视图时停止快速刷新。

为此,您只需实施 onTouchEvent,并在检测到 MotionEvent.ACTION_DOWN 时设置一个标志。

...
if (event.getAction()==MotionEvent.ACTION_DOWN) isUserTouching = true;
if (event.getAction()==MotionEvent.ACTION_UP) isUserTouching = false;
...

然后,您的更新程序代码变为:

public void setCountDown(int countDown){
   if (!isUserTouching) {
      mCountDown = countDown;
      notifyDataSetChanged();
   }
}

您必须在创建视图持有者时设置单击和长按侦听器,而不是在视图持有者的绑定中。

将您的视图持有者更新为:

public class ItemViewHolder extends RecyclerView.ViewHolder {
        TextView tvEmail;
        TextView tvPin;
        TextView tvIssuer;
//        TextView tvCountDown;
        ArcProgress apCountDown;
        View container;

        public ItemViewHolder(View itemView) {
            super(itemView);
            tvEmail = (TextView) itemView.findViewById(R.id.tv_email);
            tvPin = (TextView) itemView.findViewById(R.id.tv_pin);
            tvIssuer = (TextView) itemView.findViewById(R.id.tv_issuer);
            apCountDown = (ArcProgress) itemView.findViewById(R.id.arc_progress);
            container = itemView.findViewById(R.id.container);

            container.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String pin = tvPin.getText().toString();
                    ClipboardManager clipboard = (ClipboardManager) itemView.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
                    ClipData clip = ClipData.newPlainText("Secret Key", pin);
                    clipboard.setPrimaryClip(clip);

                    Toast.makeText(itemView.getContext(), "OTP code "+ pin +" copied to clipboard", Toast.LENGTH_SHORT).show();

                }
            });

            container.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    FragmentManager fm = ((AppCompatActivity)itemView.getContext()).getSupportFragmentManager();
                    EditAccountFragment editAccountFragmentDialog = EditAccountFragment.newInstance(_id);
                    editAccountFragmentDialog.show(fm, "fragment_edit_account");

                    return true; //if you handle it, return true!
                }

            });
        }
    }

并删除 onBindViewHolder 方法中的 holder.container.setOnClickListenerholder.container.setOnLongClickListener

已修复!

所以基本上问题是,每次碰巧有一个更新来对抗整个适配器填充整个视图,包括监听器,甚至在 ViewHolder 中设置监听器也不起作用。

我所做的是,我扩展了布局管理器并添加了一种方法来仅更新视图的计数器小部件,而其余部分保持不变。

这是 TimedGridLayoutManager:

public class TimedGridLayoutManager extends GridLayoutManager {

   public TimedGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
    }

    //updates the counter every second
    public void setCountDown(int countDown) {
        //return null if there are no items and avoid crash
        if(this.getItemCount() <= 0)
            return;

        //get first and last visible child
        int firstPos = this.findFirstVisibleItemPosition();
        int lastPos = this.findLastVisibleItemPosition();

        //loop through the range and set counter
        for(int i = firstPos; i <= lastPos; i++) {
            View itemView = this.findViewByPosition(i);
            ArcProgress arcCounter = (ArcProgress) itemView.findViewById(R.id.arc_counter);
            arcCounter.setProgress(countDown);

        }
    }
}

它不知道它是否是目前最有效的解决方案,但它对我来说就像一个魅力,而且我根本不必触及我的适配器代码。