Android RecyclerView 和自定义 Adapter 位置元素的变化

Posted

技术标签:

【中文标题】Android RecyclerView 和自定义 Adapter 位置元素的变化【英文标题】:Android RecyclerView and custom Adapter position elements changes 【发布时间】:2021-07-18 03:32:42 【问题描述】:

我有一个 RecyclerView 填充了一个自定义适配器 recylerView.setAdapter(myAdapter),而适配器填充了一个 ArrayList 的不同元素。在 ViewHolder 我覆盖了 public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) 方法。在自定义 ViewHolder 我放了一个按钮。该按钮上有一个点击监听器。当我单击按钮时,我读取了变量position,并且每次单击同一个按钮时,我都面临着变量的变化。 为什么会这样?

我的适配器是这样的:

import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.amplifyframework.core.Amplify;
import com.amplifyframework.core.model.query.Where;
import com.amplifyframework.datastore.generated.model.Comments;
import com.amplifyframework.datastore.generated.model.Likes;
import com.amplifyframework.datastore.generated.model.Posts;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.RequestOptions;
import com.google.firebase.auth.FirebaseAuth;

import java.net.URL;
import java.util.LinkedList;

import de.hdodenhof.circleimageview.CircleImageView;

import static com.amazonaws.mobile.auth.core.internal.util.ThreadUtils.runOnUiThread;

public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHolder>

    private LinkedList<Posts> usersPostsList;
    private FirebaseAuth mAuth;
    private Context context;
    //private Boolean likeChecker;
    private String currentUserId;
    private MainActivity mainActivity;
    private int currentPosition;

    public PostsAdapter(Context context, String currentUserId, MainActivity mainActivity)
    
        this.context = context;
        this.currentUserId = currentUserId;
        this.mainActivity = mainActivity;
    

    public PostsAdapter(LinkedList<Posts> usersPostsList, Context context)
    
        this.usersPostsList = usersPostsList;
        this.context = context;
    

    public void setMessages(LinkedList<Posts> usersPostsList) 
        this.usersPostsList = usersPostsList;
    

    public static class PostViewHolder extends RecyclerView.ViewHolder
    
        View mView;

        public static ImageButton LikePostButton, CommentPostButton;
        public TextView DisplayNoOfLikes, modifyPost;
        private TextView DisplayNoOfComments;
        String currentUserID;
        ExpandableTextView PostDescription;
        private Context context;

        public PostViewHolder(@NonNull View itemView, Context context)
        
            super(itemView);
            mView = itemView;
            this.context = context;

            LikePostButton = (ImageButton) mView.findViewById(R.id.like_button);
            CommentPostButton = (ImageButton) mView.findViewById(R.id.comment_button);
            DisplayNoOfLikes = (TextView) mView.findViewById(R.id.display_no_of_likes);
            DisplayNoOfComments = (TextView) mView.findViewById(R.id.display_no_of_comments);
            currentUserID = FirebaseAuth.getInstance().getCurrentUser().getUid();
            modifyPost = mView.findViewById(R.id.modify_post);
        



        public void setLikeButtonStatus(final String PostKey) 

            Amplify.DataStore.query(
                    Likes.class, Where.matches(Likes.POST_ID.eq(PostKey.trim()).and(Likes.SENDER.eq(currentUserID.trim()))),
                    items -> 
                        int countLikes = 0;
                        if (!items.hasNext()) 
                            LikePostButton.setImageResource(R.drawable.ic_star_border);
                        
                        while (items.hasNext()) 
                            Likes item = items.next();
                            countLikes++;
                            LikePostButton.setImageResource(R.drawable.ic_star_fill);
                            Log.i("Amplify", "Id " + item.getId());
                        
                        int finalCountLikes = countLikes;
                        DisplayNoOfLikes.setText(finalCountLikes + (" Likes"));
                    ,
                    failure -> Log.e("Amplify", "Could not query DataStore", failure)
            );

        


        public void setFullname(String fullname)
        
            TextView username = (TextView) mView.findViewById(R.id.post_user_name);
            username.setText(fullname);
        

        public void setProfileimage(Context ctx, String profileimage)
        
            CircleImageView image = (CircleImageView) mView.findViewById(R.id.post_profile_image);
            Amplify.Storage.getUrl(profileimage,
                    result -> 
                        URL url = result.getUrl();
                        runOnUiThread(() -> Glide.with(ctx)
                                .load(url)
                                .apply(new RequestOptions()
                                        .diskCacheStrategy(DiskCacheStrategy.NONE)
                                        .skipMemoryCache(true))
                                .into(image));
                    ,
                    error -> Log.i("Amplify", "error while retrieving url"));
        

        public void setTime(String time)
        
            TextView PostTime = (TextView) mView.findViewById(R.id.post_time);
            PostTime.setText("   " + time);
        
        public void setDate(String date)
        
            TextView PostDate = (TextView) mView.findViewById(R.id.post_date);
            PostDate.setText("   " + date);
        
        public void setDescription(String description)
        
            PostDescription = mView.findViewById(R.id.post_description);
            PostDescription.setText(description);
        


        public void setPostimage(Context ctx, String postimage, String id)
        
            ImageView postImageInner = mView.findViewById(R.id.post_image);

             Amplify.Storage.getUrl(postimage,
                    result -> 
                        URL url = result.getUrl();
                        runOnUiThread(() -> Glide.with(ctx)
                                .load(url)
                                .apply(new RequestOptions()
                                        .diskCacheStrategy(DiskCacheStrategy.NONE)
                                        .skipMemoryCache(true))
                                .into(postImageInner));
                        Log.i("Amplify---------", "url ok----");
                    ,
                    error -> Log.e("Amplify---------", "error while retrieving url: " + error.getCause().toString()));

            postImageInner.setOnClickListener(v -> 
                Intent clickPostIntent = new Intent(context, ClickPostActivity.class);
                clickPostIntent.putExtra("PostKey", id);
                clickPostIntent.putExtra("postImagePath", postimage);
                context.startActivity (clickPostIntent);
            );

        
        public void setCountry(String country)
        
            TextView CountryName = (TextView) mView.findViewById(R.id.post_country_name);
            CountryName.setText(country);
        

        public void setCity(String city)
        
            TextView City = (TextView) mView.findViewById(R.id.post_city_name);
            City.setText("- " + city);
        


        public void setCommentStatus(final String PostKey) 

            Amplify.DataStore.query(
                    Comments.class, Where.matches(Comments.POST_ID.eq(PostKey.trim())),
                    items -> 
                        int i = 0;
                        while (items.hasNext()) 
                            Comments comments = items.next();
                            i++;
                            int finalI = i;
                            runOnUiThread(() -> DisplayNoOfComments.setText(finalI + " comments"));
                            Log.i("amplify?", "comment id: " + comments.getId());
                        
                    ,
                    failure -> Log.e("Amplify", "Could not query DataStore", failure)
            );
        
    



    @NonNull
    @Override
    public PostViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
    
        View V = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.all_posts_layout, parent,false);

        mAuth = FirebaseAuth.getInstance();
        return new PostViewHolder(V, context);

    

    @Override
    public int getItemViewType(int position) 
        currentPosition = position;
        return position;
    

    @Override
    public void onBindViewHolder(@NonNull PostViewHolder viewHolder, int position) 
        viewHolder.setFullname(usersPostsList.get(position).getFullname());
        viewHolder.setTime(usersPostsList.get(position).getTime());
        viewHolder.setDate(usersPostsList.get(position).getDate());
        viewHolder.setDescription(usersPostsList.get(position).getDescription());
        viewHolder.setProfileimage(context, usersPostsList.get(position).getProfileimage());
        viewHolder.setPostimage(context, usersPostsList.get(position).getPostimage(), usersPostsList.get(position).getId());
        viewHolder.setCountry(usersPostsList.get(position).getCountry());
        viewHolder.setCity(usersPostsList.get(position).getCity());


        if( viewHolder.PostDescription.originalText.length() > 100 ) 
            viewHolder.PostDescription.setOnClickListener(new View.OnClickListener() 
                @Override
                public void onClick(View v) 
                    if (!((ExpandableTextView)v).read) 
                        ((ExpandableTextView)v).expandText();
                        ((ExpandableTextView)v).read = true;
                     else 
                        ((ExpandableTextView)v).truncateText();
                        ((ExpandableTextView)v).read = false;
                    

                
            );
        


        viewHolder.CommentPostButton.setOnClickListener(v -> 
            Intent commentsIntent = new Intent(context, CommentActivity.class);
            commentsIntent.putExtra("PostKey",usersPostsList.get(currentPosition).getId());
            context.startActivity(commentsIntent);
        );


        viewHolder.LikePostButton.setOnClickListener(v -> 

            Amplify.DataStore.query(
                    Likes.class, Where.matches(Likes.POST_ID.eq(usersPostsList.get(currentPosition).getId().trim())
                            .and(Likes.SENDER.eq(currentUserId.trim()))
                            .and((Likes.RECEIVER.eq(usersPostsList.get(currentPosition).getUid())))
                    ),
                    items -> 
                        if (items.hasNext()) 
                            Likes item = items.next();
                            Amplify.DataStore.delete(item,
                                    deleted -> Log.i("Amplify", "Deleted item."),
                                    failure -> Log.e("Amplify", "Delete failed.", failure)
                            );

                            Log.i("Amplify", "Id " + item.getId());
                         else 
                            Likes likes = Likes.builder()
                                    .receiver(usersPostsList.get(currentPosition).getUid().trim())
                                    .sender(currentUserId.trim())
                                    .postId(usersPostsList.get(currentPosition).getId().trim())
                                    .value("true")
                                    .build();
                            Amplify.DataStore.save(
                                    likes,
                                    success -> Log.i("Amplify", "Item updated: " + success.item().getId()),
                                    error -> Log.e("Amplify", "Could not save item to DataStore", error)
                            );
                        
                    ,
                    failure -> 
                        Log.e("Amplify", "Could not query DataStore", failure);
                    
            );
            notifyDataSetChanged();
        );

        viewHolder.modifyPost.setOnClickListener(v -> 
            Intent intent = new Intent(context, ClickPostActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
            intent.putExtra("PostKey", usersPostsList.get(currentPosition).getId());
            intent.putExtra("postImagePath", usersPostsList.get(currentPosition).getPostimage());
            context.startActivity(intent);
        );
    


    @Override
    public int getItemCount() 
        return usersPostsList.size() ;
    




似乎在单击 LikePostButton 后调用方法 getItemViewType 以便:viewHolder.LikePostButton.setOnClickListener(v -&gt; 获得较旧的 currentPosition 变量值

感谢大家帮助我理解

【问题讨论】:

请阅读onBindViewHolder的文档以获得答案。 我阅读了文档,感谢它很有用,我发现导入实现 'androidx.recyclerview:recyclerview:1.2.0' 有一个方法:viewHolder.getBindingAdapterPosition() 效果很好......谢谢@SteveM 但我使用 viewHolder.getAbsoluteAdapterPosition() 做得更好,并在将其放入适配器之前反转项目的数组列表,因为 getAbsoluteAdapterPositions 遍历适配器元素的位置相反。再次感谢 【参考方案1】:

您可以覆盖 getItemViewType(position:Int) 方法并替换 super 的行以仅在 Kotlin 中使用这样的位置返回它:

override fun getItemViewType(position: Int): Int 
        return position
    

在 Java 中:

@Override
public int getItemViewType(int position) 
    return position;

【讨论】:

不幸的是,这对我没有帮助 您能分享您的适配器代码以了解问题所在吗? 在我的适配器中,我在 currentPosition 中设置了一个全局变量 private; .....在我得到变量后:@Override public int getItemViewType(int position) currentPosition = position;返回位置;然后在 onBindViewHolder 方法中的单击侦听器中: viewHolder.Button.setOnClickListener(v -> list.get( currentPosition)..... 但似乎方法 getItemViewType 在 onBindViewHolder 之后被调用,因此 currentPosition 变量没有更新 如果我理解正确..你的问题是当你保存当前位置时,你不应该使用这个变量 (currentPosition) 你可以使用 onBindViewHolder 里面的默认位置所以你不应该t 使用全局变量 我不能使用 onBindViewHolder 的默认位置,文档说如果你有一个 onClickListener 位置可能不准确,我真的面对它。我尝试了文档中的建议,因此使用 getAbsoluteAdapterPosition() 但它仍然失败,并且在大约 5 或 6 次单击后,getAbsoluteAdapterPosition() 方法返回 -1,所以这对我来说是个大问题......【参考方案2】:

谢谢,我解决了它在 ViewHolder 中的按钮上设置侦听器,然后调用 getAbsoluteAdapterPosition() 以获得正确的位置

【讨论】:

以上是关于Android RecyclerView 和自定义 Adapter 位置元素的变化的主要内容,如果未能解决你的问题,请参考以下文章

Android 上的 HttpClient 和自定义 TrustManager

Android:如何添加按钮和自定义视图

在 Android 中使用 SearchableSpinner 和自定义适配器

Android 自定义ViewViewGroup和自定义属性

带有 Intent 和自定义 Listview 的 Android

Android中Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现)