ConstraintLayout 没有为 RecyclerView 上单击的项目设置 NavController

Ali*_*ira 4 navigation android android-recyclerview

当我单击某个项目时,有时可以工作,有时会崩溃。

\n\n

以下是单击某个项目后出现的致命异常。

\n\n
2020-03-31 21:59:18.087 15383-15383/com.aliton.myapp E/AndroidRuntime: FATAL EXCEPTION: main\n    Process: com.aliton.myapp, PID: 15383\n    java.lang.IllegalStateException: View androidx.constraintlayout.widget.ConstraintLayout{b3bb13b V.E...C.. .......D 0,336-720,518} does not have a NavController set\n        at androidx.navigation.Navigation.findNavController(Navigation.java:84)\n        at com.aliton.myapp.Adapter.StoreSelectAdapter$StoreSelectViewHolder$1.onChanged(StoreSelectAdapter.java:147)\n        at com.aliton.myapp.Adapter.StoreSelectAdapter$StoreSelectViewHolder$1.onChanged(StoreSelectAdapter.java:142)\n        at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)\n        at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)\n        at androidx.lifecycle.LiveData.setValue(LiveData.java:307)\n        at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)\n        at androidx.lifecycle.LiveData$1.run(LiveData.java:91)\n        at android.os.Handler.handleCallback(Handler.java:751)\n        at android.os.Handler.dispatchMessage(Handler.java:95)\n        at android.os.Looper.loop(Looper.java:154)\n        at android.app.ActivityThread.main(ActivityThread.java:6351)\n        at java.lang.reflect.Method.invoke(Native Method)\n        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:896)\n        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:786)\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这种情况下,首先我需要将一个项目 ( StoreEntity) 插入到我的房间数据库中,侦听它id,然后导航到具有在中定义的对象的其他片段direction

\n\n

下面是我的适配器:

\n\n
public class StoreSelectAdapter extends RecyclerView.Adapter<StoreSelectAdapter.StoreSelectViewHolder> {\n\n    private static final String TAG = "debinf SummaryAdapter";\n\n    private FragmentActivity fragmentActivity;\n\n    public StoreSelectAdapter(FragmentActivity fragmentActivity) {\n        this.fragmentActivity = fragmentActivity;\n    }\n\n    private static final DiffUtil.ItemCallback<StoreModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<StoreModel>() {\n        @Override\n        public boolean areItemsTheSame(@NonNull StoreModel oldItem, @NonNull StoreModel newItem) {\n            return oldItem.getKey().equals(newItem.getKey());\n        }\n\n        @Override\n        public boolean areContentsTheSame(@NonNull StoreModel oldItem, @NonNull StoreModel newItem) {\n            return oldItem.getKey().equals(newItem.getKey()) && oldItem.getName().equals(newItem.getName()) && oldItem.getAddress().equals(newItem.getAddress());\n        }\n    };\n\n    private AsyncListDiffer<StoreModel> differ = new AsyncListDiffer<StoreModel>(this, DIFF_CALLBACK);\n\n    @NonNull\n    @Override\n    public StoreSelectViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_select_store, parent, false);\n        return new StoreSelectViewHolder(view, fragmentActivity);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull StoreSelectViewHolder holder, int position) {\n        StoreModel store = differ.getCurrentList().get(position);\n        Log.i(TAG, "onBindViewHolder: "+store.getName());\n\n        holder.storeName.setText(store.getName());\n        holder.storeAddress.setText(store.getAddress());\n\n        if (store.getImage() != null && !store.getImage().isEmpty()) {\n            Picasso.get().load(store.getImage()).networkPolicy(NetworkPolicy.OFFLINE).into(holder.storeImage, new Callback() {\n                @Override\n                public void onSuccess() {\n\n                }\n\n                @Override\n                public void onError(Exception e) {\n                    Picasso.get().load(store.getImage()).into(holder.storeImage);\n                }\n            });\n        } else {\n            holder.storeImage.setImageResource(android.R.drawable.stat_sys_phone_call_on_hold);\n        }\n\n        /*holder.itemView.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Log.i(TAG, "onClick: onBindViewHolder");\n                Toast.makeText(context, "onClick: onBindViewHolder", Toast.LENGTH_SHORT).show();\n                AddstoreFragmentDirections.ActionAddstoreFragmentToBarcodeFragment direction = AddstoreFragmentDirections.actionAddstoreFragmentToBarcodeFragment(store);\n                Navigation.createNavigateOnClickListener()\n            }\n        });*/\n    }\n\n    @Override\n    public int getItemCount() {\n        return differ.getCurrentList().size();\n    }\n\n    public void submitList(List<StoreModel> stores) {\n        differ.submitList(stores);\n    }\n\n    public List<StoreModel> getCurrentItemList() {\n        return differ.getCurrentList();\n    }\n\n    public class StoreSelectViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {\n\n        //private static final String TAG = "StoreSelectViewHolder";\n\n        public ImageView storeImage;\n        public TextView storeName, storeAddress;\n\n        private FragmentActivity fragmentActivity;\n        private LocalDatabaseViewModel localDatabaseViewModel;\n\n        public StoreSelectViewHolder(@NonNull View itemView, FragmentActivity fragmentActivity) {\n            super(itemView);\n\n            storeImage = (ImageView) itemView.findViewById(R.id.item_store_image);\n            storeName = (TextView) itemView.findViewById(R.id.item_store_name);\n            storeAddress = (TextView) itemView.findViewById(R.id.item_store_address);\n\n            this.fragmentActivity = fragmentActivity;\n\n            itemView.setOnClickListener(this);\n\n        }\n\n        @Override\n        public void onClick(View v) {\n            StoreModel store = differ.getCurrentList().get(getAdapterPosition());\n            Log.i(TAG, "onClick: storeName selected: "+store.getName());\n            Log.i(TAG, "onClick: storeKey selected: "+store.getKey());\n            StoreEntity storeEntity = new StoreEntity(store.getName(), store.getImage());\n            storeEntity.setKey(store.getKey());\n            localDatabaseViewModel = ViewModelProviders.of(fragmentActivity).get(LocalDatabaseViewModel.class);\n            localDatabaseViewModel.getStoreIdFromInsertedItem().observe(fragmentActivity, new Observer<Long>() {\n                @Override\n                public void onChanged(Long itemId) {\n                    Log.i(TAG, "onChanged: itemId in adapter is "+itemId);\n                    SelectstoreFragmentDirections.ActionSelectstoreFragmentToBarcodeFragment direction = SelectstoreFragmentDirections.actionSelectstoreFragmentToBarcodeFragment(store, itemId); \n                    Navigation.findNavController(v).navigate(direction); // this is the StoreSelectAdapter.java:147\n                }\n            });\n            localDatabaseViewModel.insertStore(storeEntity);\n\n\n        }\n\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我感谢任何帮助解决这个问题的帮助。

\n\n

编辑

\n\n

下面是我的activity_main.xml声明我的片段 NavHost 的位置。

\n\n
<androidx.drawerlayout.widget.DrawerLayout\n    android:id="@+id/main_drawer"\n    xmlns:android="http://schemas.android.com/apk/res/android"\n    xmlns:app="http://schemas.android.com/apk/res-auto"\n    xmlns:tools="http://schemas.android.com/tools"\n    android:layout_width="match_parent"\n    android:layout_height="match_parent"\n    tools:context=".MainActivity">\n\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:layout_width="match_parent"\n        android:layout_height="match_parent">\n\n        <fragment\n            android:id="@+id/main_fragment"\n            android:layout_width="0dp"\n            android:layout_height="0dp"\n            android:name="androidx.navigation.fragment.NavHostFragment"\n            app:layout_constraintLeft_toLeftOf="parent"\n            app:layout_constraintRight_toRightOf="parent"\n            app:layout_constraintTop_toTopOf="parent"\n            app:layout_constraintBottom_toTopOf="@id/main_bottomnav"\n            app:defaultNavHost="true"\n            app:navGraph="@navigation/mainnav_graph"/>\n\n        <com.google.android.material.bottomnavigation.BottomNavigationView\n            android:id="@+id/main_bottomnav"\n            android:layout_width="match_parent"\n            android:layout_height="wrap_content"\n            app:layout_constraintBottom_toBottomOf="parent"\n            app:layout_constraintEnd_toEndOf="parent"\n            app:layout_constraintStart_toStartOf="parent"\n            app:menu="@menu/main_navmenu"\n            android:background="@color/colorAccent"\n            app:itemIconTint="@drawable/botton_item_color"\n            app:itemTextColor="@drawable/botton_item_color">\n\n        </com.google.android.material.bottomnavigation.BottomNavigationView>\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n\n    <com.google.android.material.navigation.NavigationView\n        android:id="@+id/main_sidebar"\n        android:layout_width="wrap_content"\n        android:layout_height="match_parent"\n        android:layout_gravity="start"\n        app:menu="@menu/main_sidebarmenu"/>\n\n</androidx.drawerlayout.widget.DrawerLayout>\n
Run Code Online (Sandbox Code Playgroud)\n\n

下面是我的mainnav_graph.xml

\n\n
...\n<fragment\n    android:id="@+id/barcodeFragment"\n    android:name="com.aliton.myapp.BarcodeFragment"\n    android:label="fragment_barcode"\n    tools:layout="@layout/fragment_barcode" >\n\n    <argument\n        android:name="store"\n        app:argType="com.aliton.myapp.Model.StoreModel" />\n    <action\n        android:id="@+id/action_barcodeFragment_to_productdetailFragment"\n        app:destination="@id/productdetailFragment" />\n    <argument\n        android:name="storeId"\n        app:argType="long" />\n</fragment>\n<fragment\n    android:id="@+id/selectstoreFragment"\n    android:name="com.aliton.myapp.SelectstoreFragment"\n    android:label="fragment_selectstore"\n    tools:layout="@layout/fragment_selectstore" >\n    <action\n        android:id="@+id/action_selectstoreFragment_to_addstoreFragment"\n        app:destination="@id/addstoreFragment" />\n    <action\n        android:id="@+id/action_selectstoreFragment_to_barcodeFragment"\n        app:destination="@id/barcodeFragment" />\n</fragment>\n...\n
Run Code Online (Sandbox Code Playgroud)\n\n

下面是我fragment_selectstore.xml的内容SelectstoreFragment.java

\n\n
<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android="http://schemas.android.com/apk/res/android"\n    xmlns:app="http://schemas.android.com/apk/res-auto"\n    xmlns:tools="http://schemas.android.com/tools"\n    android:layout_width="match_parent"\n    android:layout_height="match_parent"\n    tools:context=".SelectstoreFragment">\n\n    <androidx.cardview.widget.CardView\n        android:id="@+id/selectstore_cardview"\n        android:layout_width="match_parent"\n        android:layout_height="wrap_content"\n        android:layout_margin="8dp"\n        app:cardCornerRadius="10dp"\n        app:layout_constraintTop_toTopOf="parent"\n        app:layout_constraintEnd_toEndOf="parent"\n        app:layout_constraintStart_toStartOf="parent">\n\n        <TextView\n            android:id="@+id/selectstore_question"\n            android:layout_width="match_parent"\n            android:layout_height="wrap_content"\n            android:text="Are you at any of the store shown below?"\n            android:textSize="28sp"\n            android:gravity="center"\n            android:textStyle="bold"/>\n\n\n    </androidx.cardview.widget.CardView>\n\n    <FrameLayout\n        android:id="@+id/selectstore_framelayout"\n        android:layout_width="match_parent"\n        android:layout_height="0dp"\n        android:layout_marginTop="2dp"\n        app:layout_constraintBottom_toBottomOf="parent"\n        app:layout_constraintTop_toBottomOf="@+id/selectstore_cardview">\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id="@+id/selectstore_recyclerview"\n            android:layout_width="match_parent"\n            android:layout_height="match_parent"\n            android:scrollbars="vertical">\n\n        </androidx.recyclerview.widget.RecyclerView>\n\n        <TextView\n            android:id="@+id/selectstore_nodata"\n            android:layout_width="match_parent"\n            android:layout_height="wrap_content"\n            android:layout_gravity="center"\n            android:gravity="center"\n            android:text="No store found!"\n            android:textSize="22sp" />\n\n    </FrameLayout>\n\n    <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton\n        android:id="@+id/selectstore_fab"\n        style="@style/Widget.MaterialComponents.ExtendedFloatingActionButton"\n        android:layout_width="wrap_content"\n        android:layout_height="wrap_content"\n        android:layout_gravity="bottom|end"\n        android:layout_margin="8dp"\n        android:contentDescription="Outra loja desc"\n        android:text="Outra loja"\n        app:icon="@drawable/common_full_open_on_phone"\n        app:layout_constraintBottom_toBottomOf="parent"\n        app:layout_constraintEnd_toEndOf="parent" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n
Run Code Online (Sandbox Code Playgroud)\n\n

更新

\n\n

1)我还实现了和interface之间的通信以获取,但应用程序不断崩溃。AdapterFragmentviewonViewCreated()

\n\n

2)我还尝试使用以下命令获取片段布局中定义的 NavHostFragment 的 navController

\n\n
@Override\npublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n    super.onViewCreated(view, savedInstanceState);\n\n    Log.i(TAG, "onViewCreated: ");\n\n    // /sf/answers/3773188751/\n    // NOT WORKING\n    adapter = new StoreSelectAdapter(getActivity(), new StoreSelectAdapter.OnStoreSelectListener() {\n        @Override\n        public void onStoreSelected(StoreModel storeModel, Long itemId) {\n            SelectstoreFragmentDirections.ActionSelectstoreFragmentToBarcodeFragment direction = SelectstoreFragmentDirections.actionSelectstoreFragmentToBarcodeFragment(storeModel, itemId);\n            Navigation.findNavController(getActivity(), R.id.main_fragment).navigate(direction);\n        }\n    });\n    storeRecyclerview.setAdapter(adapter);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

但现在我得到了NullPointerException

\n\n
2020-04-02 17:11:22.163 6015-6015/com.aliton.myapp E/AndroidRuntime: FATAL EXCEPTION: main\n    Process: com.aliton.myapp, PID: 6015\n    java.lang.NullPointerException: Attempt to invoke virtual method \'android.view.View android.app.Activity.requireViewById(int)\' on a null object reference\n        at androidx.core.app.ActivityCompat.requireViewById(ActivityCompat.java:363)\n        at androidx.navigation.Navigation.findNavController(Navigation.java:58)\n        at com.aliton.myapp.SelectstoreFragment$4.onStoreSelected(SelectstoreFragment.java:221)\n        at com.aliton.myapp.Adapter.StoreSelectAdapter$StoreSelectViewHolder$1.onChanged(StoreSelectAdapter.java:149)\n        at com.aliton.myapp.Adapter.StoreSelectAdapter$StoreSelectViewHolder$1.onChanged(StoreSelectAdapter.java:145)\n        at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)\n        at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)\n        at androidx.lifecycle.LiveData.setValue(LiveData.java:307)\n        at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)\n        at androidx.lifecycle.LiveData$1.run(LiveData.java:91)\n        at android.os.Handler.handleCallback(Handler.java:873)\n        at android.os.Handler.dispatchMessage(Handler.java:99)\n        at android.os.Looper.loop(Looper.java:193)\n        at android.app.ActivityThread.main(ActivityThread.java:6669)\n        at java.lang.reflect.Method.invoke(Native Method)\n        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)\n        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)\n
Run Code Online (Sandbox Code Playgroud)\n\n

3)我已经复制了Navigation类中的方法,以便了解导致错误的原因,它们是:

\n\n
@Override\npublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n    super.onViewCreated(view, savedInstanceState);\n\n    Log.i(TAG, "onViewCreated: ");\n\n    adapter = new StoreSelectAdapter(getActivity(), new StoreSelectAdapter.OnStoreSelectListener() {\n        @Override\n        public void onStoreSelected(StoreModel storeModel, Long itemId) {\n            SelectstoreFragmentDirections.ActionSelectstoreFragmentToBarcodeFragment direction = SelectstoreFragmentDirections.actionSelectstoreFragmentToBarcodeFragment(storeModel, itemId);\n            NavController navController = myFindViewNavController(view);\n            if (navController == null) {\n                Log.i(TAG, "onStoreSelected: ISSUE NavController set");\n            }                \n\n        }\n    });\n\n    storeRecyclerview.setAdapter(adapter);\n}\n\nprivate NavController myFindViewNavController(View view) {\n    while (view != null) {\n        Log.i(TAG, "myFindViewNavController: view != null - "+view);\n        NavController controller = myGetViewNavController(view);\n        if (controller != null) {\n            Log.i(TAG, "myFindViewNavController: controller != null");\n            return controller;\n        }\n        ViewParent parent = view.getParent();\n        view = parent instanceof View ? (View) parent : null;\n        Log.i(TAG, "myFindViewNavController: view.getParent() - "+view);\n    }\n    return null;\n}\n\nprivate NavController myGetViewNavController(View view) {\n    Object tag = view.getTag(R.id.nav_controller_view_tag);\n    Log.i(TAG, "myGetViewNavController: tag is "+tag);\n    NavController controller = null;\n    if (tag instanceof WeakReference) {\n        Log.i(TAG, "myGetViewNavController: tag instanceof WeakReference");\n        controller = ((WeakReference<NavController>) tag).get();\n    } else if (tag instanceof NavController) {\n        Log.i(TAG, "myGetViewNavController: tag instanceof NavController");\n        controller = (NavController) tag;\n    }\n    return controller;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

验证logcat,似乎ViewModel在我的adapter在导航到下一个片段后再次触发了

\n\n
debinf\xc2\xa0SummaryAdapter: onClick: storeName selected: Posto Shell Convem\ndebinf\xc2\xa0SummaryAdapter: onClick: storeKey selected: M0Ysk5jRIdtiJ9zyUhXG\ndebinf\xc2\xa0SummaryAdapter: onChanged: itemId in adapter is 40, view id: -1\ndebinf\xc2\xa0SelStoreFrag: myFindViewNavController: view != null - androidx.constraintlayout.widget.ConstraintLayout{fb9835d V.E...... ........ 0,0-1080,1437}\ndebinf\xc2\xa0SelStoreFrag: myGetViewNavController: tag is null\ndebinf\xc2\xa0SelStoreFrag: myFindViewNavController: view.getParent() - android.widget.FrameLayout{4af5206 V.E...... ........ 0,0-1080,1437 #7f090137 app:id/main_fragment}\ndebinf\xc2\xa0SelStoreFrag: myFindViewNavController: view != null - android.widget.FrameLayout{4af5206 V.E...... ........ 0,0-1080,1437 #7f090137 app:id/main_fragment}\ndebinf\xc2\xa0SelStoreFrag: myGetViewNavController: tag is androidx.navigation.NavController@47f0c7\ndebinf\xc2\xa0SelStoreFrag: myGetViewNavController: tag instanceof NavController\ndebinf\xc2\xa0SelStoreFrag: myFindViewNavController: controller != null\ndebinf\xc2\xa0BarcodeFrag: onCreateView: \ndebinf\xc2\xa0BarcodeFrag: onCreateView: fragArgs is com.aliton.myapp.Model.StoreModel@77fbfcb\ndebinf\xc2\xa0Ba

Fra*_*pos 7

我在适配器的绑定方法上遇到了一些类似的问题,对我有用的是更改此行

navController = Navigation.findNavController(itemView)
Run Code Online (Sandbox Code Playgroud)

navController = Navigation.findNavController(activity, R.id.nav_host_fragment)
Run Code Online (Sandbox Code Playgroud)

  • 这个真的很有帮助,谢谢 (2认同)