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 ->
获得较旧的 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 中使用 SearchableSpinner 和自定义适配器
Android 自定义ViewViewGroup和自定义属性