简体   繁体   English

RecyclerView 执行项目点击

[英]RecyclerView Perform Item Click

I have a RecyclerView that contains expandable items.我有一个包含可扩展项目的RecyclerView clicking on an item expands it.单击一个项目将其展开。 The problem is it also expand some other cards, unexpectedly.问题是它意外地还扩展了一些其他卡。 I checked everything and I couldn't find why is this happening, but I did manage to find out that the clicked item always somehow has the same id as the other expanded item.我检查了所有内容,但找不到为什么会发生这种情况,但我确实设法发现被点击的项目总是以某种方式与其他展开的项目具有相同的 ID。 The error occurs only when the list is big enough, so I think it has something to do with the RecyclerView s functionality.只有当列表足够大时才会出现错误,所以我认为它与RecyclerView的功能有关。 Also using notifyDataSetChanged() works, but it eliminates the animations, and I want the layout to be animated...也使用notifyDataSetChanged()工作,但它消除了动画,我希望布局动画......

this question looks to discuss the same problem I'm facing... but yet I don't know how to solve it.这个问题似乎讨论了我面临的同样问题......但我不知道如何解决它。

I couldn't understand why is this happening or how to fix this... below are some images and code to help you understand better, and maybe see if the problem is in the code...我不明白为什么会发生这种情况或如何解决这个问题……下面是一些图像和代码,可以帮助您更好地理解,也许看看问题是否出在代码中……

this is the RecyclerView :这是RecyclerView

在此处输入图片说明

An expanded card item looks like this:展开的卡片项目如下所示:

在此处输入图片说明

Here's my Adapters class:这是我的适配器类:

public class ActiveGoalsAdapter extends RecyclerView.Adapter<ActiveGoalsAdapter.ActiveGoalsViewHolder> {

    private Context context;
    private Cursor cursor;
    private ArrayList<Goal> activeGoals;
    private static boolean[] openedFromParent = new boolean[]{false, true}, editing = new boolean[]{false};

    public ActiveGoalsAdapter(Context context, ArrayList<Goal> activeGoals, Cursor cursor) {
        this.context = context;
        this.activeGoals = activeGoals;
        this.cursor = cursor;
    }

    public class ActiveGoalsViewHolder extends RecyclerView.ViewHolder {

        public LinearLayout shrunkContainer, subGoalsTitleContainer;
        public RelativeLayout expandedContainer, subGoalsRecyclerViewContainer, btnDelete, btnCancel, btnSave;
        public ConstraintLayout editPanel;
        public CustomProgressBar shrunkProgressBar, expandedProgressBar;
        public ImageButton btnExpandShrink, btnEdit, btnBackToParent;
        public TextView title, description;
        public RecyclerView subGoalsRecyclerView;
        public ExtendedEditText nameET, descriptionET;

        public ActiveGoalsViewHolder(@NonNull View itemView) {
            super(itemView);

            shrunkContainer = itemView.findViewById(R.id.shrunk_active_goal_container);
            expandedContainer = itemView.findViewById(R.id.expanded_active_goal_container);
            editPanel = itemView.findViewById(R.id.edit_panel);
            btnExpandShrink = itemView.findViewById(R.id.active_goal_expand_shrink_btn);
            btnEdit = itemView.findViewById(R.id.active_goal_edit_btn);
            btnBackToParent = itemView.findViewById(R.id.active_goal_back_to_parent_btn);
            shrunkProgressBar = itemView.findViewById(R.id.shrunk_active_goal_progress_bar);
            shrunkProgressBar.enableDefaultGradient(true);
            title = itemView.findViewById(R.id.expanded_active_goal_title);
            expandedProgressBar = itemView.findViewById(R.id.expanded_active_goal_progress_bar);
            expandedProgressBar.enableDefaultGradient(true);
            description = itemView.findViewById(R.id.expanded_active_goal_description);
            subGoalsTitleContainer = itemView.findViewById(R.id.expanded_active_goal_sub_goals_title_container);
            subGoalsRecyclerViewContainer = itemView.findViewById(R.id.expanded_active_goal_sub_goals_container);
            subGoalsRecyclerView = itemView.findViewById(R.id.expanded_active_goal_sub_goals_recyclerview);
            nameET = itemView.findViewById(R.id.expanded_active_goal_edit_name_edit_text);
            descriptionET = itemView.findViewById(R.id.expanded_active_goal_edit_description_edit_text);
            btnDelete = itemView.findViewById(R.id.edit_delete_button);
            btnCancel = itemView.findViewById(R.id.edit_cancel_button);
            btnSave = itemView.findViewById(R.id.edit_save_button);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (expandedContainer.getVisibility() == View.VISIBLE) {
                        shrink();
                    } else {
                        expand();
                    }
                }
            });

        }

        private void expand(){
            TransitionManager.beginDelayedTransition((ViewGroup) itemView.getRootView(), new AutoTransition());
            expandedContainer.setVisibility(View.VISIBLE);
            shrunkProgressBar.setVisibility(View.INVISIBLE);

        }

        private void shrink(){
            TransitionManager.beginDelayedTransition((ViewGroup) itemView.getRootView(), new AutoTransition());
            expandedContainer.setVisibility(View.GONE);
            shrunkProgressBar.setVisibility(View.VISIBLE);
        }

    }

    @NonNull
    @Override
    public ActiveGoalsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.active_goal_card, parent, false);
        return new ActiveGoalsViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ActiveGoalsViewHolder holder, int position) {
        if (activeGoals.get(position) == null) {
            return;
        }
        GoalDBHelper db = new GoalDBHelper(context);

        Goal currentGoal = activeGoals.get(position);
        Cursor subGoalsCursor = db.getSubGoalsCursorOf(currentGoal);
        ArrayList<Goal> subGoalsArrayList = db.getSubGoalsArrayListOf(currentGoal);

        String name = currentGoal.getName(),
                description = currentGoal.getDescription(),
                parent = currentGoal.getParentGoal();
        int timeCounted = currentGoal.getTimeCounted(),
                timeEstimated = currentGoal.getTimeEstimated();

        for (Goal subGoal : activeGoals) {
            if (subGoal.getParentGoal().equals(name)) {
                subGoalsArrayList.add(subGoal);
            }
        }

        holder.shrunkProgressBar.setText(name);
        holder.shrunkProgressBar.setProgress((timeCounted * 100 / timeEstimated));
        holder.shrunkProgressBar.setRadius(300.0f);
        holder.expandedProgressBar.setText("");
        holder.expandedProgressBar.setProgress((timeCounted * 100 / timeEstimated));
        holder.expandedProgressBar.setRadius(300.0f);
        holder.title.setText(name);
        holder.description.setText(description);

        if (subGoalsArrayList.size() <= 0) {
            holder.subGoalsTitleContainer.setVisibility(View.GONE);
            holder.subGoalsRecyclerViewContainer.setVisibility(View.GONE);
        } else {
            holder.subGoalsTitleContainer.setVisibility(View.VISIBLE);
            holder.subGoalsRecyclerViewContainer.setVisibility(View.VISIBLE);
            initSubGoalsAdapter(holder.subGoalsRecyclerView, subGoalsArrayList, subGoalsCursor);
        }

        if (openedFromParent[0]) {
            holder.btnBackToParent.setVisibility(View.VISIBLE);
        } else {
            holder.btnBackToParent.setVisibility(View.GONE);
        }

    }

    public void initSubGoalsAdapter(RecyclerView subGoalsRecyclerView, ArrayList<Goal> subGoals, Cursor subGoalsCursor) {
        GoalsAdapter adapter = new GoalsAdapter(context, subGoals, subGoalsCursor);
        final CarouselLayoutManager layoutManager = new CarouselLayoutManager(CarouselLayoutManager.VERTICAL, false);
        layoutManager.setPostLayoutListener((CarouselLayoutManager.PostLayoutListener) new CarouselZoomPostLayoutListener());
        subGoalsRecyclerView.setLayoutManager(layoutManager);
        subGoalsRecyclerView.setHasFixedSize(true);
        subGoalsRecyclerView.setAdapter(adapter);
    }

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

    public void swapCursor(Cursor newCursor) {
        if (cursor != null) {
            cursor.close();
        }

        cursor = newCursor;

        if (newCursor != null) {
            notifyDataSetChanged();
        }
    }
}

Where is the problem?问题出在哪儿? and how should I fix it?我该如何解决?

Help would be highly appreciated帮助将不胜感激

The problem is that RecyclerView reuses ViewHolders during scrolling.问题是 RecyclerView 在滚动期间重用 ViewHolders。 For example on position 10 it can uses ViewHolder from position 2 (let's imagine this item was expanded) and if you don't bind expanded / collapsed state for ViewHolder on position 10 it will have expanded state.例如,在位置 10 上,它可以使用位置 2 的 ViewHolder(假设此项目已展开),如果您没有为位置 10 上的 ViewHolder 绑定展开/折叠状态,它将具有展开状态。 So to solve the problem you have to track ViewHolder state and update ViewHolder every onBindViewHolder method calling.因此,要解决该问题,您必须跟踪 ViewHolder 状态并在每个 onBindViewHolder 方法调用时更新 ViewHolder。

Here is a good answer related to selection in RecyclerView and you will have almost the same logic for expanded / collapsed states.这是与 RecyclerView 中的选择相关的一个很好的答案,对于展开/折叠状态,您将拥有几乎相同的逻辑。

https://stackoverflow.com/a/28838834/9169701 https://stackoverflow.com/a/28838834/9169701

I'm not familiar with the utilities you're using for animation.我不熟悉您用于动画的实用程序。 But, you can do something like this to track and update the visibility of your views:但是,您可以执行以下操作来跟踪和更新视图的可见性:

private ArrayList<MyData> dataList;
private ArrayList<boolean> itemStates; // Create a list to store the item states

public MyAdapter(ArrayList<MyData> myData){
    dataList = myData;
    itemStates = new ArrayList<>();

    // Build the default state values for each position
    for(MyData data: dataList){
        itemStates.add(false);
    }
}

@Override
public void onBindViewHolder(MyHolder holder, int position){
    // Whatever you need to do on each item position ...

    final boolean visible = itemStates.get(position);

    // Set the visibility of whichever view you want
    if(visible){
        holder.myView.setVisibility(View.VISIBLE);
    }else{
        holder.myView.setVisibility(View.GONE);
    }

    // Change the visibility after clicked
    holder.itemView.setOnClickListener(new View.OnClickListener(){
        // Use the ViewHolder's getAdapterPosition()
        // to retrieve a reliable position inside the click callback
        int pos = holder.getAdapterPosition();

        if(visible){
            // Play the hide view animation for this position ...
        }else{
            // Play the show view animation for this position ...
        }

        // Set the new item state
        itemStates.set(pos, !visible);

        // Refresh the Adapter after a delay to give your animation time to play
        // (I've used 500 milliseconds here)
        new Handler().postDelayed(new Runnable(){
            @Override
            public void run(){
                notifyDataSetChanged();
            }
        }, 500);
    });
}

You can refer to my code for the solution, maybe it'll help.你可以参考我的代码解决方案,也许会有所帮助。

final boolean isExpanded = position == currentPosition;
holder.childLayout.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
Animation slideDown = AnimationUtils.loadAnimation(context, R.anim.slide_down_animation);
holder.childLayout.startAnimation(slideDown);

if (isExpanded)
    currentPosition = position;

holder.parentLayout.setOnClickListener(v -> {
    currentPosition = isExpanded ? -1 : position;
    notifyItemChanged(currentPosition);
    notifyItemChanged(position);
});

Hope this solves your problem.希望这能解决您的问题。

Edit: currentPosition is a variable which is assigned to -1 and it stores the current position of the item in the recyclerview.编辑: currentPosition是分配给 -1 的变量,它存储项目在 recyclerview 中的当前位置。

position is the variable of the BindViewHolder position是 BindViewHolder 的变量

setActivated() is a method defined for view. setActivated()是为视图定义的方法。 You can check it here .你可以在这里查看

childLayout is the layout of the view that is shown after the expansion. childLayout是展开后显示的视图的布局。

parentLayout is the layout on which you click to expand. parentLayout是您单击以展开的布局。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM