Android 在Recyclerview中滚动后,Imageview不在正确的行上
我有一个通过microsoft graph api显示电子邮件的应用程序。 到目前为止,除了1件事情外,其他一切都很好。当列表第一次加载时,所有信息都会正确显示,但当我向下滚动时,会返回。附件的imageview位于错误的行上。它只显示在没有附件的行上。在适配器中,我有一个if子句,它说如果hasAttachement值为“true”,则只显示行中的图像。。我真的不明白为什么它在错误的行中被重新绘制 我设置附件的方法称为: MessagesAdapter中的setBijlage() 编辑:如果我在我的应用程序中单击该行,该行将再次正确显示(如果有附件,则会获得一个图标,如果没有附件,则会删除该图标) MailActivity.javaAndroid 在Recyclerview中滚动后,Imageview不在正确的行上,android,android-recyclerview,imageview,android-viewholder,Android,Android Recyclerview,Imageview,Android Viewholder,我有一个通过microsoft graph api显示电子邮件的应用程序。 到目前为止,除了1件事情外,其他一切都很好。当列表第一次加载时,所有信息都会正确显示,但当我向下滚动时,会返回。附件的imageview位于错误的行上。它只显示在没有附件的行上。在适配器中,我有一个if子句,它说如果hasAttachement值为“true”,则只显示行中的图像。。我真的不明白为什么它在错误的行中被重新绘制 我设置附件的方法称为: MessagesAdapter中的setBijlage() 编辑:如果我
public class MailActivity extends AppCompatActivityRest implements SwipeRefreshLayout.OnRefreshListener, MessagesAdapter.MessageAdapterListener {
private String currentFolder;
private String currentUser;
private List<Message> messages = new ArrayList<>();
private RecyclerView recyclerView;
private MessagesAdapter mAdapter;
private SwipeRefreshLayout swipeRefreshLayout;
private ActionModeCallback actionModeCallback;
private ActionMode actionMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_mail);
super.onCreate(savedInstanceState);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
currentFolder = getString(R.string.inbox);
currentUser = getIntent().getStringExtra("USER_EMAIL");
setActionBarMail(currentFolder, currentUser);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
swipeRefreshLayout.setOnRefreshListener(this);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
actionModeCallback = new ActionModeCallback();
// show loader and fetch messages
swipeRefreshLayout.post(
new Runnable() {
@Override
public void run() {
getAllMails(15);
}
}
);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_search) {
Toast.makeText(getApplicationContext(), "Search...", Toast.LENGTH_SHORT).show();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void processResponse(OutlookObjectCall outlookObjectCall, JSONObject response) {
switch (outlookObjectCall) {
case READUSER: {
System.out.println("reading user");
} break;
case READMAIL: {
messages.clear();
JSONObject list = response;
try {
JSONArray mails = list.getJSONArray("value");
Type listType = new TypeToken<List<Message>>() {
}.getType();
messages = new Gson().fromJson(String.valueOf(mails), listType);
for (Message message : messages) {
message.setColor(getRandomMaterialColor("400"));
}
System.out.println(messages.get(2).getFrom().getEmailAddress().getName());
mAdapter = new MessagesAdapter(this, messages, this);
recyclerView.setAdapter(mAdapter);
} catch (JSONException e) {
e.printStackTrace();
}
mAdapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
break;
case SENDMAIL: {
System.out.println("Just send a mail." );
}
}
}
@Override
public void onRefresh() {
// swipe refresh is performed, fetch the messages again
getAllMails(15);
}
@Override
public void onIconClicked(int position) {
if (actionMode == null) {
actionMode = startSupportActionMode(actionModeCallback);
}
toggleSelection(position);
}
@Override
public void onIconImportantClicked(int position) {
// Star icon is clicked,
// mark the message as important
Message message = messages.get(position);
message.setImportance("normal");
messages.set(position, message);
mAdapter.notifyDataSetChanged();
}
@Override
public void onMessageRowClicked(int position) {
// verify whether action mode is enabled or not
// if enabled, change the row state to activated
if (mAdapter.getSelectedItemCount() > 0) {
enableActionMode(position);
} else {
// read the message which removes bold from the row
Message message = messages.get(position);
message.setIsRead("true");
messages.set(position, message);
mAdapter.notifyDataSetChanged();
Toast.makeText(getApplicationContext(), "Read: " + message.getBodyPreview(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onRowLongClicked(int position) {
// long press is performed, enable action mode
enableActionMode(position);
}
private void enableActionMode(int position) {
if (actionMode == null) {
actionMode = startSupportActionMode(actionModeCallback);
}
toggleSelection(position);
}
private void toggleSelection(int position) {
mAdapter.toggleSelection(position);
int count = mAdapter.getSelectedItemCount();
if (count == 0) {
actionMode.finish();
} else {
actionMode.setTitle(String.valueOf(count));
actionMode.invalidate();
}
}
private class ActionModeCallback implements ActionMode.Callback {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.menu_action_mode, menu);
// disable swipe refresh if action mode is enabled
swipeRefreshLayout.setEnabled(false);
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete:
// delete all the selected messages
deleteMessages();
mode.finish();
return true;
default:
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode mode) {
mAdapter.clearSelections();
swipeRefreshLayout.setEnabled(true);
actionMode = null;
recyclerView.post(new Runnable() {
@Override
public void run() {
mAdapter.resetAnimationIndex();
// mAdapter.notifyDataSetChanged();
}
});
}
}
// deleting the messages from recycler view
private void deleteMessages() {
mAdapter.resetAnimationIndex();
List<Integer> selectedItemPositions =
mAdapter.getSelectedItems();
for (int i = selectedItemPositions.size() - 1; i >= 0; i--) {
mAdapter.removeData(selectedItemPositions.get(i));
}
mAdapter.notifyDataSetChanged();
}
private void setActionBarMail(String title, String subtitle) {
getSupportActionBar().setTitle(title);
getSupportActionBar().setSubtitle(subtitle);
}
private void getAllMails(int aantalMails) {
swipeRefreshLayout.setRefreshing(true);
try {
new GraphAPI().getRequest(OutlookObjectCall.READMAIL, this, "/inbox/messages?$top=" + aantalMails);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private int getRandomMaterialColor(String typeColor) {
int returnColor = Color.GRAY;
int arrayId = getResources().getIdentifier("mdcolor_" + typeColor, "array", getPackageName());
if (arrayId != 0) {
TypedArray colors = getResources().obtainTypedArray(arrayId);
int index = (int) (Math.random() * colors.length());
returnColor = colors.getColor(index, Color.GRAY);
colors.recycle();
}
return returnColor;
}
公共类MailActivity扩展了AppCompatActivityRest,实现了SwiperFreshLayout.OnRefreshListener、MessagesAdapter.MessageAdapterListener{
私有字符串文件夹;
私有字符串用户;
私有列表消息=新的ArrayList();
私人回收站;
私人信息;
私人SwipeRefreshLayout SwipeRefreshLayout;
私有ActionModeCallback ActionModeCallback;
私人行动模式;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
setContentView(R.layout.activity\u-mail);
super.onCreate(savedInstanceState);
Toolbar Toolbar=(Toolbar)findViewById(R.id.Toolbar);
设置支持操作栏(工具栏);
currentFolder=getString(R.string.inbox);
currentUser=getIntent().getStringExtra(“用户电子邮件”);
setActionBarMail(currentFolder,currentUser);
FloatingActionButton fab=(FloatingActionButton)findViewById(R.id.fab);
fab.setOnClickListener(新视图.OnClickListener(){
@凌驾
公共void onClick(视图){
Snackbar.make(查看“替换为您自己的操作”,Snackbar.LENGTH\u LONG)
.setAction(“Action”,null).show();
}
});
recyclerView=(recyclerView)findViewById(R.id.recycler\u视图);
swipeRefreshLayout=(swipeRefreshLayout)findViewById(R.id.swipe\u refresh\u layout);
swipeRefreshLayout.setOnRefreshListener(此);
RecyclerView.LayoutManager mLayoutManager=新的LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
setItemAnimator(新的DefaultItemAnimator());
recyclerView.addItemDecoration(新的DividerItemDecoration(这是LinearLayoutManager.VERTICAL));
actionModeCallback=新建actionModeCallback();
//显示加载程序并获取消息
swipeRefreshLayout.post(
新的Runnable(){
@凌驾
公开募捐{
获取所有邮件(15);
}
}
);
}
@凌驾
公共布尔onCreateOptions菜单(菜单){
//为菜单充气;这会将项目添加到操作栏(如果存在)。
getMenuInflater().充气(右菜单菜单菜单主菜单);
返回true;
}
@凌驾
公共布尔值onOptionsItemSelected(菜单项项){
//处理操作栏项目单击此处。操作栏将
//自动处理Home/Up按钮上的点击,只要
//在AndroidManifest.xml中指定父活动时。
int id=item.getItemId();
//noinspection SimplifiableIf语句
if(id==R.id.action\u search){
Toast.makeText(getApplicationContext(),“搜索…”,Toast.LENGTH\u SHORT.show();
返回true;
}
返回super.onOptionsItemSelected(项目);
}
@凌驾
public void processResponse(OutlookObjectCall OutlookObjectCall、JSONObject响应){
开关(outlookObjectCall){
案例阅读用户:{
System.out.println(“读取用户”);
}中断;
案例阅读邮件:{
messages.clear();
JSONObject list=响应;
试一试{
JSONArray mails=list.getJSONArray(“值”);
类型listType=新类型令牌(){
}.getType();
messages=new Gson().fromJson(String.valueOf(mails),listType);
用于(消息:消息){
message.setColor(getRandomMaterialColor(“400”));
}
System.out.println(messages.get(2.getFrom().getEmailAddress().getName());
mAdapter=newmessagesadapter(this,messages,this);
recyclerView.setAdapter(mAdapter);
}捕获(JSONException e){
e、 printStackTrace();
}
mAdapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
打破
案例发送邮件:{
System.out.println(“只需发送邮件即可”);
}
}
}
@凌驾
公共void onRefresh(){
//执行刷卡刷新后,再次获取消息
获取所有邮件(15);
}
@凌驾
点击公共位置(内部位置){
if(actionMode==null){
actionMode=startSupportActionMode(actionModeCallback);
}
切换选择(位置);
}
@凌驾
公共无效onIconImportantClicked(内部位置){
//单击星号图标,
//将信息标记为重要信息
Message Message=messages.get(位置);
消息设置重要性(“正常”);
消息。设置(位置、消息);
mAdapter.notifyDataSetChanged();
}
@凌驾
已单击MessageRowClicked上的public void(int位置){
//验证是否启用了操作模式
//如果启用,请更改行st
public class MessagesAdapter extends RecyclerView.Adapter<MessagesAdapter.MyViewHolder> {
private Context mContext;
private List<Message> messages;
private MessageAdapterListener listener;
private SparseBooleanArray selectedItems;
// array used to perform multiple animation at once
private SparseBooleanArray animationItemsIndex;
private boolean reverseAllAnimations = false;
// index is used to animate only the selected row
// dirty fix, find a better solution
private static int currentSelectedIndex = -1;
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener {
public TextView from, subject, message, iconText, timestamp;
public ImageView iconImp, imgProfile, imgBijlage;
public LinearLayout messageContainer;
public RelativeLayout iconContainer, iconBack, iconFront;
public MyViewHolder(View view) {
super(view);
from = (TextView) view.findViewById(R.id.from);
subject = (TextView) view.findViewById(R.id.txt_primary);
message = (TextView) view.findViewById(R.id.txt_secondary);
iconText = (TextView) view.findViewById(R.id.icon_text);
timestamp = (TextView) view.findViewById(R.id.timestamp);
iconBack = (RelativeLayout) view.findViewById(R.id.icon_back);
iconFront = (RelativeLayout) view.findViewById(R.id.icon_front);
iconImp = (ImageView) view.findViewById(R.id.icon_star);
imgProfile = (ImageView) view.findViewById(R.id.icon_profile);
messageContainer = (LinearLayout) view.findViewById(R.id.message_container);
iconContainer = (RelativeLayout) view.findViewById(R.id.icon_container);
imgBijlage = (ImageView) view.findViewById(R.id.icon_attachement);
view.setOnLongClickListener(this);
}
@Override
public boolean onLongClick(View view) {
listener.onRowLongClicked(getAdapterPosition());
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
return true;
}
}
public MessagesAdapter(Context mContext, List<Message> messages, MessageAdapterListener listener) {
this.mContext = mContext;
this.messages = messages;
this.listener = listener;
selectedItems = new SparseBooleanArray();
animationItemsIndex = new SparseBooleanArray();
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.message_list_row, parent, false);
return new MyViewHolder(itemView);
}
@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
Message message = messages.get(position);
// displaying text view data
holder.from.setText(message.getFrom().getEmailAddress().getName());
holder.subject.setText(message.getSubject());
holder.message.setText(message.getBodyPreview());
System.out.println("EMAIL: " + position + " HAS ATTACHEMENT: " + message.getHasAttachments());
setBijlage(message, holder);
try {
setDate(message, holder);
} catch (ParseException e) {
e.printStackTrace();
}
// displaying the first letter of From in icon text
holder.iconText.setText(message.getFrom().getEmailAddress().getName().substring(0, 1));
// change the row state to activated
holder.itemView.setActivated(selectedItems.get(position, false));
// change the font style depending on message read status
applyReadStatus(holder, message);
// handle message star
applyImportant(holder, message);
// handle icon animation
applyIconAnimation(holder, position);
// display profile image
applyProfilePicture(holder, message);
// apply click events
applyClickEvents(holder, position);
}
private void setDate(Message message, MyViewHolder holder) throws ParseException {
String stringDate = message.getReceivedDateTime();
String COMPARE_FORMAT = "yyyy/MM/dd";
String OUTPUT_FORMAT_NOT_TODAY = "dd MMM";
String JSON_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
SimpleDateFormat dateFormat = new SimpleDateFormat(COMPARE_FORMAT);
SimpleDateFormat formatter = new SimpleDateFormat(JSON_FORMAT);
SimpleDateFormat defaultFormat = new SimpleDateFormat(OUTPUT_FORMAT_NOT_TODAY);
//today date (check if today)
Date today = new Date();
String currentDate = dateFormat.format(today);
//hours (if today
Date date = formatter.parse(stringDate);
formatter.applyPattern(COMPARE_FORMAT);
String mailDate = formatter.format(date);
//dd/month (if not today)
boolean is24 = DateFormat.is24HourFormat(mContext);
if (mailDate.equals(currentDate)) {
if (is24) {
SimpleDateFormat outputFormat = new SimpleDateFormat("HH:mm");
holder.timestamp.setText(outputFormat.format(date));
} else {
SimpleDateFormat outputFormat = new SimpleDateFormat("hh:mm a");
holder.timestamp.setText(outputFormat.format(date));
}
} else {
holder.timestamp.setText(defaultFormat.format(date));
}
}
private void setBijlage(Message message, MyViewHolder holder){
//set bijlage
if (message.getHasAttachments().toLowerCase().equals("true")){
holder.imgBijlage.setImageResource(R.drawable.ic_bijlage);
}
}
private void applyClickEvents(MyViewHolder holder, final int position) {
holder.iconContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
listener.onIconClicked(position);
}
});
holder.iconImp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
listener.onIconImportantClicked(position);
}
});
holder.messageContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
listener.onMessageRowClicked(position);
}
});
holder.messageContainer.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
listener.onRowLongClicked(position);
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
return true;
}
});
}
private void applyProfilePicture(MyViewHolder holder, Message message) {
holder.imgProfile.setImageResource(R.drawable.bg_circle);
holder.imgProfile.setColorFilter(message.getColor());
holder.iconText.setVisibility(View.VISIBLE);
}
private void applyIconAnimation(MyViewHolder holder, int position) {
if (selectedItems.get(position, false)) {
holder.iconFront.setVisibility(View.GONE);
resetIconYAxis(holder.iconBack);
holder.iconBack.setVisibility(View.VISIBLE);
holder.iconBack.setAlpha(1);
if (currentSelectedIndex == position) {
FlipAnimator.flipView(mContext, holder.iconBack, holder.iconFront, true);
resetCurrentIndex();
}
} else {
holder.iconBack.setVisibility(View.GONE);
resetIconYAxis(holder.iconFront);
holder.iconFront.setVisibility(View.VISIBLE);
holder.iconFront.setAlpha(1);
if ((reverseAllAnimations && animationItemsIndex.get(position, false)) || currentSelectedIndex == position) {
FlipAnimator.flipView(mContext, holder.iconBack, holder.iconFront, false);
resetCurrentIndex();
}
}
}
// As the views will be reused, sometimes the icon appears as
// flipped because older view is reused. Reset the Y-axis to 0
private void resetIconYAxis(View view) {
if (view.getRotationY() != 0) {
view.setRotationY(0);
}
}
public void resetAnimationIndex() {
reverseAllAnimations = false;
animationItemsIndex.clear();
}
@Override
public long getItemId(int position) {
return messages.get(position).getAutoId();
}
private void applyImportant(MyViewHolder holder, Message message) {
if (message.getImportance().toLowerCase().equals("high")) {
holder.iconImp.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_star_black_24dp));
holder.iconImp.setColorFilter(ContextCompat.getColor(mContext, R.color.icon_tint_selected));
} else {
holder.iconImp.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_star_border_black_24dp));
holder.iconImp.setColorFilter(ContextCompat.getColor(mContext, R.color.icon_tint_normal));
}
}
private void applyReadStatus(MyViewHolder holder, Message message) {
if (message.getIsRead().toLowerCase().equals("true")) {
holder.from.setTypeface(null, Typeface.NORMAL);
holder.subject.setTypeface(null, Typeface.NORMAL);
holder.from.setTextColor(ContextCompat.getColor(mContext, R.color.subject));
holder.subject.setTextColor(ContextCompat.getColor(mContext, R.color.message));
} else {
holder.from.setTypeface(null, Typeface.BOLD);
holder.subject.setTypeface(null, Typeface.BOLD);
holder.from.setTextColor(ContextCompat.getColor(mContext, R.color.from));
holder.subject.setTextColor(ContextCompat.getColor(mContext, R.color.subject));
}
}
@Override
public int getItemCount() {
return messages.size();
}
public void toggleSelection(int pos) {
currentSelectedIndex = pos;
if (selectedItems.get(pos, false)) {
selectedItems.delete(pos);
animationItemsIndex.delete(pos);
} else {
selectedItems.put(pos, true);
animationItemsIndex.put(pos, true);
}
notifyItemChanged(pos);
}
public void clearSelections() {
reverseAllAnimations = true;
selectedItems.clear();
notifyDataSetChanged();
}
public int getSelectedItemCount() {
return selectedItems.size();
}
public List<Integer> getSelectedItems() {
List<Integer> items =
new ArrayList<>(selectedItems.size());
for (int i = 0; i < selectedItems.size(); i++) {
items.add(selectedItems.keyAt(i));
}
return items;
}
public void removeData(int position) {
messages.remove(position);
resetCurrentIndex();
}
private void resetCurrentIndex() {
currentSelectedIndex = -1;
}
public interface MessageAdapterListener {
void onIconClicked(int position);
void onIconImportantClicked(int position);
void onMessageRowClicked(int position);
void onRowLongClicked(int position);
}
}
private void setBijlage(Message message, MyViewHolder holder){
//set bijlage
if (message.getHasAttachments().toLowerCase().equals("true")){
holder.imgBijlage.setVisibility(View.VISIBLE);
holder.imgBijlage.setImageResource(R.drawable.ic_bijlage);
}else{
holder.imgBijlage.setVisibility(View.GONE);
}
}
if (message.getHasAttachments().toLowerCase().equals("true")){
holder.imgBijlage.setVisibility(View.VISIBLE);
}else {
holder.imgBijlage.setVisibility(View.INVISIBLE);
}