Android:底部导航栏片段中填充有 SQLite 的 Recyclerview 不会更新

Posted

技术标签:

【中文标题】Android:底部导航栏片段中填充有 SQLite 的 Recyclerview 不会更新【英文标题】:Android: Recyclerview in Bottom Navigation Bar Fragment populated with SQLite will not update 【发布时间】:2021-03-27 19:50:37 【问题描述】:

我有一个带有 3 个片段的底部导航栏。在第一个片段中,我尝试将 SQLite 数据放入一个 recyclerview 中。它工作正常,除了我需要在导航栏项目之间切换才能看到刷新的回收站视图。但是,当我使用带有 postDelayed 的处理程序时,如果我设置了大约 1 秒的延迟,它确实会显示刷新的 recyclerview。 0.2 秒已经不行了。

尽管这仍然非常通用:是否有任何最佳实践?在我看来,我需要使用已被弃用的 AsyncTask。

谢谢!

西蒙

HomeFragment

public class HomeFragment extends Fragment 


    private HomeViewModel homeViewModel;
    private Context context;
    private CardView cardview;
    private LinearLayout.LayoutParams layoutparams;
    private TextView textview;
    private RelativeLayout relativeLayout;
    private myDbAdapter helper;
    RecyclerView myView;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) 
        homeViewModel =
                new ViewModelProvider(this).get(HomeViewModel.class);
        View root = inflater.inflate(R.layout.fragment_home, container, false);
        
        helper  = new myDbAdapter(getContext());
        myView = (RecyclerView) root.findViewById(R.id.recyclerview_home);
        RecyclerViewAdapter3 adapter = new RecyclerViewAdapter3(new ArrayList<String>(Arrays.asList(helper.classes())));
        myView.setHasFixedSize(true);
        myView.setAdapter(adapter);
        LinearLayoutManager llm = new LinearLayoutManager(getContext());
        llm.setOrientation(LinearLayoutManager.VERTICAL);
        myView.setLayoutManager(llm);
    

        homeViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() 
            @Override
            public void onChanged(@Nullable String s) 
                textView.setText(s);
            
        );
        return root;
    

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) 
        super.onViewCreated(view, savedInstanceState);



    

    public void refresh(View v)
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() 
            public void run() 
                myView = (RecyclerView) v.findViewById(R.id.recyclerview_home);
                helper  = new myDbAdapter(v.getContext());
                ArrayList<String> classes = new ArrayList(Arrays.asList(helper.classes()));
                ArrayList<String> subClasses = new ArrayList(Arrays.asList(helper.subClasses()));
                RecyclerViewAdapter3 adapter = new RecyclerViewAdapter3(classes);
                myView.setHasFixedSize(true);
                myView.setAdapter(adapter);
                LinearLayoutManager llm = new LinearLayoutManager(v.getContext());
                llm.setOrientation(LinearLayoutManager.VERTICAL);
                myView.setLayoutManager(llm);
            
        , 1000); //time in millis
    

RecyclerViewAdapter3

public class RecyclerViewAdapter3 extends RecyclerView.Adapter<RecyclerViewAdapter3.MyViewHolder> 
    public ArrayList<String> classArrayList;
    public ArrayList<String> subClassArrayList;
    myDbAdapter helper;


    public RecyclerViewAdapter3(ArrayList<String> classArrayList)
        this.classArrayList = classArrayList;
    

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        View listItem = LayoutInflater.from(parent.getContext()).inflate(R.layout.cardview, parent, false);
        return new MyViewHolder(listItem);

    

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) 
        holder.class.setText(classArrayList.get(position));

        holder.delete.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                helper = new myDbAdapter(v.getContext());
                helper.delete(classArrayList.get(position));
                HomeFragment homeFragment = new HomeFragment();
                homeFragment.refresh(v.getRootView());
            
        );
        holder.selectButton.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 


        );

    @Override
    public int getItemCount() 

        return classArrayList.size();
    

    public static class MyViewHolder extends RecyclerView.ViewHolder 
        private TextView class;
        private Button selectButton;
        private ImageView delete;
        public MyViewHolder(View itemView) 
            super(itemView);
            class = (TextView)itemView.findViewById(R.id.name);
            selectButton = (Button) itemView.findViewById(R.id.selectButton);
            delete = (ImageView) itemView.findViewById(R.id.delete);
        
    

【问题讨论】:

如何填充您的回收站视图?在哪个片段生命周期方法中?还是您观察 LiveData?请发布您的代码,以便我们更好地帮助您。 非常感谢!刚刚编辑过。 【参考方案1】:

感谢您发布您的代码 :)

您的代码中可能会出现一些问题,就像现在一样,我无法确定在您使用postDelay 时导致它工作的原因。我将列出一些,您可以查看:

来自 ViewHolder 中的 onClick()
   HomeFragment homeFragment = new HomeFragment();
   homeFragment.refresh(v.getRootView());

你真的不应该像这样实例化你的片段。您可以将片段中的回调传递给适配器(例如:View.OnClickListener

你不断地重新实例化你的适配器和你的助手。您应该只创建一次适配器,将其设置为您的回收器视图适配器,并将其保存在成员变量中。

建议的解决方案

我看到您已经在使用ViewModel,因此您正走在一条不易出错的屏幕上,因此我建议您将 db 查询逻辑移动到您的视图模型中。如果您使用的是原始 SQLite(而不是 Room),您可以扩展 androidViewModel,这样您就可以立即访问上下文。正如您对homeViewModel.getText() 所做的那样,您应该将classes 数组公开为实时数据,观察它,并将新列表提交给您的适配器。

为了将您的列表提交给您的适配器,我建议使用ListAdapter,这将为您提供一个在片段中提交列表的submitList 方法,在适配器内部,您将有一个getItem(int position) 方法,您可以使用它可以在onBindViewHolder方法里面查询。

在您的片段中,它看起来像这样:

ClassAdapter adapter = null;

View onCreateView(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState)
    View root = inflater.inflate(R.layout.fragment_home, container, false);
    
    adapter = new ClassAdapter(
        new ClassDeleteCallback() 
            @Override
            void onClassDeleted(Class class) 
                // inside view model we can modify db state, than refresh live data
                viewModel.deleteClass(class);
            
        ,
        new ClassSelectedCallback() 
            // follows same pattern of above
        
    );
    RecyclerView rv = root.findViewById(R.id.my_rv);
    rv.setAdapter(adapter);
    rv.setLayoutManager(new LinearLayoutManager(getContext());
    
        
    homeViewModel.getClasses().observe(getViewLifecycleOwner(), new Observer<List<Class>>() 
        @Override
        public void onChanged(@Nullable List<Class> classes) 
            adapter.submitList(classes);
         
     );

    homeViewModel.refreshClasses();
    return root;

我强烈建议您稍微研究一下这个项目,因为它涵盖了许多可以使应用程序更加稳定的基础知识:Sunflower sample app

我认为您应该阅读更多有关架构组件的信息,然后查看一些代码实验室和其他内容,然后从第一个方框开始使用此屏幕,因为它比修复当前状态更容易: )

我希望这是有帮助的,而不是太令人沮丧!

【讨论】:

一点也不令人沮丧。谢谢,我去看看:-) @SimonKay 如果有帮助,您能否将我的答案标记为解决方案(我正在尝试获得更多帽子:D)

以上是关于Android:底部导航栏片段中填充有 SQLite 的 Recyclerview 不会更新的主要内容,如果未能解决你的问题,请参考以下文章

在底部导航栏中保存片段状态

Android Jetpack导航,另一个主机片段内的主机片段

底部导航栏的 Oncreate 视图问题

使用底部导航栏防止片段刷新

Android (Kotlin) - 导航操作取决于可见的片段视图

底部导航片段应用程序不断崩溃