我尝试使用recyclerview和改造来构建应用程序,但是当从JSON文件加载数据时,我的recyclerview会无限重复一次相同的结果。
对于样本加载,所有可用数据,然后无限重复结果,方法如下:
正常:
--------------------
| Name 1 |
--------------------
| Name 2 |
--------------------
| Name 3 |
--------------------
错误
--------------------
| Name 1 |
--------------------
| Name 2 |
--------------------
| Name 3 |
--------------------
| Name 1 |
--------------------
| Name 2 |
--------------------
| Name 3 |
--------------------
...
在附件onScrollListener.java
下
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
private int visibleThreshold = 5;
private int currentPage = 0;
private int previousTotalItemCount = 0;
private boolean loading = true;
private int startingPageIndex = 0;
RecyclerView.LayoutManager mLayoutManager;
public EndlessRecyclerOnScrollListener(LinearLayoutManager layoutManager) {
this.mLayoutManager = layoutManager;
}
public EndlessRecyclerOnScrollListener(GridLayoutManager layoutManager) {
this.mLayoutManager = layoutManager;
visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
}
public EndlessRecyclerOnScrollListener(StaggeredGridLayoutManager layoutManager) {
this.mLayoutManager = layoutManager;
visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
}
public int getLastVisibleItem(int[] lastVisibleItemPositions) {
int maxSize = 0;
for (int i = 0; i < lastVisibleItemPositions.length; i++) {
if (i == 0) {
maxSize = lastVisibleItemPositions[i];
}
else if (lastVisibleItemPositions[i] > maxSize) {
maxSize = lastVisibleItemPositions[i];
}
}
return maxSize;
}
@Override
public void onScrolled(RecyclerView view, int dx, int dy) {
int lastVisibleItemPosition = 0;
int totalItemCount = mLayoutManager.getItemCount();
if (mLayoutManager instanceof StaggeredGridLayoutManager) {
int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null);
// get maximum element within the list
lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions);
} else if (mLayoutManager instanceof GridLayoutManager) {
lastVisibleItemPosition = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition();
} else if (mLayoutManager instanceof LinearLayoutManager) {
lastVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
}
if (totalItemCount < previousTotalItemCount) {
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = totalItemCount;
if (totalItemCount == 0) {
this.loading = true;
}
}
if (loading && (totalItemCount > previousTotalItemCount)) {
loading = false;
previousTotalItemCount = totalItemCount;
}
if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) {
currentPage++;
onLoadMore(currentPage, totalItemCount, view);
loading = true;
}
}
public void resetState() {
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = 0;
this.loading = true;
}
// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int page, int totalItemsCount, RecyclerView view);
}
这是我在MainActivity中放入OnCreate
的代码
recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener((LinearLayoutManager) mLayoutManager) {
@Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
getInbox();
}
});
在加载数据并将消息列表getInbox
构建到 MainActivity
private void getInbox() {
swipeRefreshLayout.setRefreshing(true);
Map<String, String> datos = new HashMap<>();
datos.put("ruta", user.getRuta());
datos.put("jornada", user.getJornada());
datos.put("id_ppl", user.getId_ppl());
datos.put("placa", user.getPlaca());
//datos.put("page", String.valueOf(index));
Call<List<Message>> call = apiService.getInbox(datos) ;
call.enqueue(new Callback<List<Message>>() {
@Override
public void onResponse(Call<List<Message>> call, retrofit2.Response<List<Message>> response) {
// TODO - avoid looping
for ( Message message : response.body()) {
// generate a random color
message.setColor(getRandomMaterialColor("400"));
messages.add(message);
}
mAdapter.notifyDataSetChanged();
}
@Override
public void onFailure(Call<List<Message>> call, Throwable t) {
Toast.makeText(getApplicationContext(), "Unable to fetch json: " + t.getMessage(), Toast.LENGTH_LONG).show();
swipeRefreshLayout.setRefreshing(false);
}
});
}
这是我的adapter.java
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;
public LinearLayout messageContainer;
public RelativeLayout iconContainer, iconBack, iconFront;
public MyViewHolder(View view) {
super(view);
from = view.findViewById(R.id.from);
subject = view.findViewById(R.id.txt_primary);
message = view.findViewById(R.id.txt_secondary);
iconText = view.findViewById(R.id.icon_text);
timestamp = view.findViewById(R.id.timestamp);
iconBack = view.findViewById(R.id.icon_back);
iconFront = view.findViewById(R.id.icon_front);
iconImp = view.findViewById(R.id.icon_star);
imgProfile = view.findViewById(R.id.icon_profile);
messageContainer = view.findViewById(R.id.message_container);
iconContainer = view.findViewById(R.id.icon_container);
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;
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());
holder.subject.setText(message.getSubject());
holder.message.setText(message.getMessage());
holder.timestamp.setText(message.getTimestamp());
// displaying the first letter of From in icon text
holder.iconText.setText(message.getFrom().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 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) {
if (!TextUtils.isEmpty(message.getPicture())) {
Glide.with(mContext).load(message.getPicture())
.thumbnail(0.5f)
// .crossFade()
// .transform(new CircleTransform(mContext))
//.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(holder.imgProfile);
holder.imgProfile.setColorFilter(null);
holder.iconText.setVisibility(View.GONE);
} else {
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).getId();
}
private void applyImportant(MyViewHolder holder, Message message) {
if (message.isImportant()) {
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.isRead()) {
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 onStatusChanged(String s, int i, Bundle bundle);
void onProviderEnabled(String s);
void onProviderDisabled(String s);
void onIconClicked(int position);
void onIconImportantClicked(int position);
void onMessageRowClicked(int position);
void onRowLongClicked(int position);
}
我的用于构建Json的PHP文件
$url = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$parts = parse_url($url);
$flag = 0;
$page = 1;
if(!empty($_GET)){
parse_str($parts['query'],$query);
$page = $query['page'];
if($page > 1){
$flag = 1;
}else{
$flag = 0;
}
}
$per_page = 5;
if($flag == 0){
$qry = "SELECT * FROM registros_not WHERE ruta = '$ruta' AND jornada = '$jornada' AND placa='$placa' AND (id_ppl = '$id_ppl' OR id_ppl = '') ORDER BY fecha_r DESC LIMIT {$per_page}";
}else{
$page = $page - 1;
$offset_page = $per_page * $page;
$qry = "SELECT * FROM registros_not WHERE ruta = '$ruta' AND jornada = '$jornada' AND placa='$placa' AND (id_ppl = '$id_ppl' OR id_ppl = '') ORDER BY fecha_r DESC LIMIT {$per_page} OFFSET {$offset_page}";
}
$res = mysqli_query($con,$qry);
$response = array();
while($row = mysqli_fetch_array($res)){
array_push($response,array('id'=>$row['idr'],'subject'=>$row['fecha_r'],'isImportant'=>$row['impor_mj'],'from'=>$row['titulo'],'message'=>$row['msj'],'timestamp'=>$row['cel'],'picture'=>$row['picture'],'isRead'=>$row['read_mj']));
}
echo stripslashes(json_encode($response, JSON_UNESCAPED_UNICODE));
我是学生,现在仍然是菜鸟,需要任何帮助。
答案 0 :(得分:0)
每次滚动时,都会执行方法getInbox()
。
这意味着将调用API。
因此,在onResponse()
方法内部,数据是第一次添加到您现有的数组中,然后显示在以下位置:mAdapter.notifyDataSetChanged();
然后,当您滚动时,它会再次执行,然后到达的数据,将被添加到此处messages.add(message);
的现有消息列表中,然后再次在此处显示(您现在拥有的所有数据即现有项目,再加上新项目):mAdapter.notifyDataSetChanged();
。
要对其进行检查,您可以进行简单的测试,为此,请更改您的onResponse()
方法:
@Override
public void onResponse(Call<List<Message>> call, retrofit2.Response<List<Message>> response) {
messages.clear;
// TODO - avoid looping
for ( Message message : response.body()) {
// generate a random color
message.setColor(getRandomMaterialColor("400"));
messages.add(message);
}
mAdapter.notifyDataSetChanged();
}
每次滚动时,它应该只显示您下载的数据,因此您知道一直在添加相同的数据。
为了实现加载额外的不同数据,每次滚动时,都应向api请求数据的“部分”或页面。并非所有数据。