为什么notifyItemChanged仅在setAdapter之后在post Runnable中有效? [英] Why notifyItemChanged only works in post Runnable after setAdapter?

查看:370
本文介绍了为什么notifyItemChanged仅在setAdapter之后在post Runnable中有效?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

public class MainActivity extends AppCompatActivity {

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

    RecyclerView list = (RecyclerView) findViewById(R.id.list);
    list.setLayoutManager(new LinearLayoutManager(this));
    final RecyclerView.Adapter adapter = getAdapter();
    list.setAdapter(adapter);

    adapter.notifyItemChanged(1, new Object());//this doesn't work
    list.post(new Runnable() {
        @Override
        public void run() {
            adapter.notifyItemChanged(1, new Object());//this works
        }
    });


}

@NonNull
private RecyclerView.Adapter getAdapter() {
    return new RecyclerView.Adapter() {
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new ItemViewHolder( LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.list_view_item, parent, false));
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            ItemViewHolder item = (ItemViewHolder) holder;
            item.tv.setText("test");
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads) {
            if(payloads.isEmpty())
                onBindViewHolder(holder, position);
            else{
                ItemViewHolder item = (ItemViewHolder) holder;
                item.tv.setText("changed!!!!!! ");
            }
        }

        @Override
        public int getItemCount() {
            return 40;
        }

        class ItemViewHolder extends RecyclerView.ViewHolder{
            TextView tv;
            ItemViewHolder(View itemView) {
                super(itemView);
                tv = (TextView) itemView.findViewById(R.id.tv);
            }
        }
    };
}}

非常简单的示例,仅用于测试notifyItemChanged,它仅在发布到消息队列时才起作用,但不能通过直接在setAdapter之后调用而起作用. setAdapter触发对requestlayout()的调用,这是否意味着如果notifyItemChanged在布置项目的中间就不会发生?

very simple example here just to test notifyItemChanged, it works only when post to the Message queue, but not by invoking directly after setAdapter. setAdapter triggers a call to requestlayout(), does that mean notifyItemChanged can't happen if it is in middle of laying out items?

推荐答案

经过一番调查,结果发现notifyItemChanged仅在连接了RecyclerView且实际上已完成onLayout时起作用,而onLayout

After a bit of investigation it turns out that notifyItemChanged only works when RecyclerView is attached and actually has completed onLayout which happens after onCreate.

中,调用processAdapterUpdatesAndSetAnimationFlags()来检查要更新的项目是否具有可用的ViewHolder(在这种情况下为null),因为在onCreate()RecyclerView未附加到窗口,因此未对RecyclerView

internally during RecyclerView.onLayout() which is called by notifyItemChanged -> requestLayout(), processAdapterUpdatesAndSetAnimationFlags() is called which checks if the item that is to be updated has an available ViewHolder which in this case is null because during onCreate(), RecyclerView is not attached to the window, thus no measurement and layout has done to RecyclerView

RecyclerView的完整绘制发生在onCreate()OnResume()

Apparently complete drawing of RecyclerView happens sometime after onCreate() and OnResume()

@Override
protected void onResume() {
    super.onResume();
    boolean a = list.isAttachedToWindow();//this is false!
}

所以要使notifyItemChangedonCreate上工作

list.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
            adapter.notifyItemChanged(1, new Object());//this will update the item
        }
    });



new Thread((new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(28);//small amount of delay,below 20 doesn't seem to work
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    adapter.notifyItemChanged(1, new Object());//also works
                }
            });
        }
    })).start();

我猜这是因为ViewRootImpl.performTraversal()是由系统控制的,并且发生在DecorView附加到onCreate

I guess the reason for this is ViewRootImpl.performTraversal() is controlled by the system and it happens sometime after DecorView is attached to the window which is during onCreate

我假设这只能在onCreateonResume期间发生,如果以后再调用可能不会发生.

I'm assuming this can only happen during onCreate and onResume, it may not happen if called later.

这篇关于为什么notifyItemChanged仅在setAdapter之后在post Runnable中有效?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆