[英]RecyclerView Perform Item Click
我有一個包含可擴展項目的RecyclerView
。 單擊一個項目將其展開。 問題是它意外地還擴展了一些其他卡。 我檢查了所有內容,但找不到為什么會發生這種情況,但我確實設法發現被點擊的項目總是以某種方式與其他展開的項目具有相同的 ID。 只有當列表足夠大時才會出現錯誤,所以我認為它與RecyclerView
的功能有關。 也使用notifyDataSetChanged()
工作,但它消除了動畫,我希望布局動畫......
這個問題似乎討論了我面臨的同樣問題......但我不知道如何解決它。
我不明白為什么會發生這種情況或如何解決這個問題……下面是一些圖像和代碼,可以幫助您更好地理解,也許看看問題是否出在代碼中……
這是RecyclerView
:
展開的卡片項目如下所示:
這是我的適配器類:
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();
}
}
}
問題出在哪兒? 我該如何解決?
幫助將不勝感激
問題是 RecyclerView 在滾動期間重用 ViewHolders。 例如,在位置 10 上,它可以使用位置 2 的 ViewHolder(假設此項目已展開),如果您沒有為位置 10 上的 ViewHolder 綁定展開/折疊狀態,它將具有展開狀態。 因此,要解決該問題,您必須跟蹤 ViewHolder 狀態並在每個 onBindViewHolder 方法調用時更新 ViewHolder。
這是與 RecyclerView 中的選擇相關的一個很好的答案,對於展開/折疊狀態,您將擁有幾乎相同的邏輯。
我不熟悉您用於動畫的實用程序。 但是,您可以執行以下操作來跟蹤和更新視圖的可見性:
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);
});
}
你可以參考我的代碼解決方案,也許會有所幫助。
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);
});
希望這能解決您的問題。
編輯: currentPosition
是分配給 -1 的變量,它存儲項目在 recyclerview 中的當前位置。
position
是 BindViewHolder 的變量
setActivated()
是為視圖定義的方法。 你可以在這里查看。
childLayout
是展開后顯示的視圖的布局。
parentLayout
是您單擊以展開的布局。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.