alibaba / vlayout

Project vlayout is a powerfull LayoutManager extension for RecyclerView, it provides a group of layouts for RecyclerView. Make it able to handle a complicate situation when grid, list and other layouts in the same recyclerview.
http://tangram.pingguohe.net/
MIT License
10.79k stars 1.79k forks source link

StaggeredGridLayoutHelper数据错乱问题 #39

Open yuwu opened 7 years ago

yuwu commented 7 years ago

版本v1.0.3 DelegateAdapter布局如下 LinearLayoutHelper (header) StaggeredGridLayoutHelper (2列瀑布流) LinearLayoutHelper (footer加载更多) ---------- - - - header - - - ---------- - 1 2 - - 3 4 - - 5 6 - ---------- - footer(加载更多) - ----------

1.加载更多数据填充到瀑布流,2.滑动到瀑布流顶部 结果第一个item与第二个item位置更换了, 检查瀑布流中的adapter中的原始数据是没有变的

---------- - - - header - - - ---------- - 2 1 - - 3 4 - - 5 6 - - 7 8 - - 9 10 - ---------- - footer(加载更多) - ----------

longerian commented 7 years ago

我刚刚发布了1.0.4版本,你更新一下试试,这个已知问题,在1.0.4里解决了。

yuwu commented 7 years ago

@longerian v1.0.4版本已测,还是一样的问题 v1.0.4版本测试过程中第一次添加footer没有显示出来

以下是测试代码

package com.alibaba.android.vlayout.example;

import android.support.v4.view.ViewCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.RecyclerView; import android.view.View;

import com.alibaba.android.vlayout.DelegateAdapter; import com.alibaba.android.vlayout.VirtualLayoutManager; import com.alibaba.android.vlayout.layout.LinearLayoutHelper;

import java.util.ArrayList; import java.util.List;

public class MainActivity extends AppCompatActivity {

RecyclerView recyclerView;

VirtualLayoutManager layoutManager;

DataAdapter adapter;

DelegateAdapter delegateAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    recyclerView = (RecyclerView)findViewById(R.id.recyclerView);

    layoutManager = new VirtualLayoutManager(this);

    adapter = new DataAdapter(this);

    VirtualLayoutManager.enableDebugging(true);
    recyclerView.setLayoutManager(layoutManager);

    delegateAdapter = new DelegateAdapter(layoutManager);

    // header
    View header = getLayoutInflater().inflate(R.layout.header, recyclerView, false);
    delegateAdapter.addAdapter(DelegateAdapter.simpleAdapter(header, new LinearLayoutHelper(0, 1)));

    //
    adapter.addAll(getDatas());
    delegateAdapter.addAdapter(adapter);

    recyclerView.setAdapter(delegateAdapter);

    // footer
    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if(newState == RecyclerView.SCROLL_STATE_IDLE && !ViewCompat.canScrollVertically(recyclerView, 1)){

                addFooter();
                recyclerView.postDelayed(new Runnable() {
                    @Override
                    public void run() {

                        adapter.addAll(getDatas());
                        adapter.notifyDataSetChanged();
                        // removeFooter();
                    }
                }, 1000);
            }
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
        }
    });
}

int count = 0;
public List<Integer> getDatas() {
    List<Integer> datas = new ArrayList<>();
    for(int i=0; i<10; i++){
        datas.add(count++);
    }
    return datas;
}

DelegateAdapter.Adapter footerAdapter;
public void addFooter(){
    if(footerAdapter == null){
        View footer = getLayoutInflater().inflate(R.layout.footer, recyclerView, false);
        footerAdapter = DelegateAdapter.simpleAdapter(footer, new LinearLayoutHelper(0, 1));
        delegateAdapter.addAdapter(footerAdapter);
    }else{
        // delegateAdapter.addAdapter(footerAdapter);
    }
}

public void removeFooter(){
    delegateAdapter.removeAdapter(footerAdapter);
}

}

package com.alibaba.android.vlayout.example;

import android.app.Activity; import android.support.v7.widget.RecyclerView; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView;

import com.alibaba.android.vlayout.DelegateAdapter; import com.alibaba.android.vlayout.LayoutHelper; import com.alibaba.android.vlayout.layout.StaggeredGridLayoutHelper;

import java.util.ArrayList; import java.util.List; public class DataAdapter extends DelegateAdapter.Adapter { private static final int ITEM_TYPE_FIX = 0; private static final int ITEM_TYPE_1 = 1; private static final int ITEM_TYPE_2 = 2; private static final int ITEM_TYPE_3 = 3; private static final int ITEM_TYPE_4 = 4; private static final int ITEM_TYPE_5 = 5; private static final int ITEM_TYPE_6 = 6;

private final int item_types[] = new int[]{ITEM_TYPE_1, ITEM_TYPE_2, ITEM_TYPE_3, ITEM_TYPE_4, ITEM_TYPE_5, ITEM_TYPE_6};

private float designWidth = 336f;
private int designHeight[] = new int[]{336, 280, 500, 400, 276, 450};

public boolean show_item_type_fix = true;

public int padding;
public int lane;

private List<Integer> datas = new ArrayList<>();

public DataAdapter(Activity activity) {
    padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, activity.getResources().getDisplayMetrics());
    lane = 2;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    RecyclerView.ViewHolder holder = null;
    switch (viewType){
        case ITEM_TYPE_FIX: {

            View itemView =  LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
            int widthPixels = parent.getWidth();
            int width = (widthPixels-padding*(lane+1))/lane;
            int height = (int)(width * (designHeight[3])/designWidth);

            ViewGroup.LayoutParams params = itemView.getLayoutParams();
            params.width = width;
            params.height = height;
            itemView.setLayoutParams(params);

            holder = new SimpleViewHolder(itemView);
            break;
        }
        case ITEM_TYPE_1:
        case ITEM_TYPE_2:
        case ITEM_TYPE_3:
        case ITEM_TYPE_4:
        case ITEM_TYPE_5:
        case ITEM_TYPE_6: {

            View itemView =  LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);

            int widthPixels = parent.getWidth();
            int width = (widthPixels-padding*(lane+1))/lane;
            int height = (int)(width * (designWidth/designHeight[viewType-1]));

            ViewGroup.LayoutParams params = itemView.getLayoutParams();
            params.width = width;
            params.height = height;
            itemView.setLayoutParams(params);

            holder = new SimpleViewHolder(itemView);
            break;
        }
        default:
            break;
    }
    return holder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    final int type = getItemViewType(position);
    switch (type){
        case ITEM_TYPE_FIX: {

            TextView tv = (TextView) holder.itemView;
            tv.setText("fix");
            break;
        }
        case ITEM_TYPE_1:
        case ITEM_TYPE_2:
        case ITEM_TYPE_3:
        case ITEM_TYPE_4:
        case ITEM_TYPE_5:
        case ITEM_TYPE_6:{

            TextView tv = (TextView) holder.itemView;
            tv.setText(String.valueOf(getItem(position)));
            break;
        }
        default:
            break;
    }
}

@Override
public int getItemCount() {
    return show_item_type_fix ? datas.size() + 1 : datas.size();
}

@Override
public int getItemViewType(int position) {
    if(show_item_type_fix){
        if(position == 0){
            return ITEM_TYPE_FIX;
        }
        position = position - 1;
    }
    int viewType = item_types[position % item_types.length];
    return viewType;
}

public Integer getItem(int position) {
    if(show_item_type_fix){
        if(position == 0){
            return null;
        }
        position = position - 1;
    }
    return datas.get(position);
}

public void addAll(List<Integer> datas) {
    this.datas.addAll(datas);
}

@Override
public LayoutHelper onCreateLayoutHelper() {
    return getLayoutHelper();
}

StaggeredGridLayoutHelper helper;
public StaggeredGridLayoutHelper getLayoutHelper(){
    if(helper == null){
        StaggeredGridLayoutHelper helper = new StaggeredGridLayoutHelper();
        helper.setLane(lane);
        helper.setBgColor(0xffffffff);
        helper.setGap(padding);
        helper.setPadding(padding, padding*lane, padding, padding);

        this.helper = helper;
    }
    return helper;
}

static class SimpleViewHolder extends RecyclerView.ViewHolder {

    public SimpleViewHolder(View view) {
        super(view);
    }
}

}

activity_main.xml

\<?xml version="1.0" encoding="utf-8"?> \<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.alibaba.android.vlayout.example.MainActivity">

<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffdcdcdc"/>

\

header.xml

\<?xml version="1.0" encoding="utf-8"?> \<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="120dp" android:text="header" android:gravity="center" android:background="#80999999" android:textColor="#333333"/>

item.xml

\<?xml version="1.0" encoding="utf-8"?> \<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text" android:layout_width="120dp" android:layout_height="120dp" android:text="1" android:gravity="center" android:background="#fff1f1f1" android:textColor="#333333"/>

footer.xml

\<?xml version="1.0" encoding="utf-8"?> \<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:text="加载中..." android:gravity="center" android:background="#80999999" android:textColor="#333333"/>

longerian commented 7 years ago

最好传个 github 库我可以直接下载

yuwu commented 7 years ago

@longerian 这是demo下载地址 https://github.com/yuwu/vlayout-v1.0.4-bug.git

yuwu commented 7 years ago

@longerian

longerian commented 7 years ago

这个比较麻烦。还在排查中

fpl1104 commented 7 years ago

解决了嘛?我也遇到这个问题了

yuwu commented 7 years ago

@longerian @fpl1104 这个bug未解决

appdev commented 7 years ago

在1.09版中 有个bug就是 #164(瀑布流头部,第一个item上面会空出一块部分)在新版本中这个bug修复后 就出现目前这个bug了

longerian commented 7 years ago

@huclengyue #164 的bug在1.0.10里解决了 当前这个bug确实还没解决

Alzzzz commented 7 years ago

我也遇到了相同的问题,产生的原因是因为manager的onItemsChanged方法直接调用了helper中的clear,将原有排列顺序进行了清空,然后滑动时发现没有了记录所以重新计算位置,导致顺序错乱,我的临时修改方式是将清空方法暴露出来,然后将其在helper的onItemsChanged方法中删除,让用户自己决定是否清空(即上拉加载时不操作,重新请求数据时清空)。

appdev commented 7 years ago

@Alzzzz 你说的是VirtualLayoutManager 这个类吗? 没有发现清空数据的代码啊

    @Override
    public void onItemsChanged(RecyclerView recyclerView) {
        for (LayoutHelper helper : mHelperFinder) {
            helper.onItemsChanged(this);
        }

        // setLayoutHelpers(mHelperFinder.getLayoutHelpers());
    }
Alzzzz commented 7 years ago

@huclengyue StaggeredGridLayoutHelper是这个类 不过好像还有别的问题

codesdt commented 6 years ago

image 我也是这样写的瀑布流,第一次进来没有问题,当我上拉加载更多数据后,第二条数据就跑到第一条上面去了。 这是没有问题的时候: image

我上拉加载后就出问题了: image

使用的版本是: image

longerian commented 6 years ago

@niehuaming 先采用 @Alzzzz 的方法试试, 瀑布流本身比较复杂,问题是比较多的

fc-dream commented 6 years ago

@longerian 现在有解决这个问题么?

fc-dream commented 6 years ago

@niehuaming 你那边是怎么解决这个bug的

davidtps commented 6 years ago

@niehuaming 先采用 @Alzzzz 的方法试试, 瀑布流本身比较复杂,问题是比较多的

这个问题有修复么? 好苦恼。。一直没有好的解决方案。。

Cedar-byte commented 5 years ago

我也遇到这个问题了,我的处理办法是继承StaggeredGridLayoutHelper自定义一个CustomStaggeredHelper,然后重写onItemsChanged方法,将里面的super.onItemsChanged(helper)注释掉后什么都不用管了,至于下拉刷新,因为我们项目的需求是里面的模块可以随意增删,所以我在下拉刷新时是先clear delegateAdapter后再重新setAdapter,正好需求这样定之后我就不用处理onItemsChanged里面的mLazySpanLookup.clear()方法了

public class CustomStaggeredHelper extends StaggeredGridLayoutHelper {

    @Override
    public void onItemsChanged(LayoutManagerHelper helper) {
             // super.onItemsChanged(helper);
    }

}

achenglike commented 5 years ago

定之后我就不用处理onItemsChanged里面的mLazySpanLookup.clear()方法了

我试了下,问题还在

15189611 commented 3 years ago

@achenglike 我现在用还是遇到了这个问题,position的顺序会错乱, 请问下你们有解决方案?

achenglike commented 3 years ago

@achenglike 我现在用还是遇到了这个问题,position的顺序会错乱, 请问下你们有解决方案?

我最后没用StaggeredGridLayoutHelper

15189611 commented 3 years ago

@achenglike 请问用了啥方式呢,能告知下嘛,我们现在工程是用了这个adapter,要做瀑布流然后位置错乱,不知道解决

achenglike commented 3 years ago

@achenglike 请问用了啥方式呢,能告知下嘛,我们现在工程是用了这个adapter,要做瀑布流然后位置错乱,不知道解决

帮不上你。我这边当时不是瀑布流,只是左侧前两个合并,所以我单做了一个1+2的布局