在自定义 ListView 适配器中执行 findViewById() 时发生 NullPointerException

Posted

技术标签:

【中文标题】在自定义 ListView 适配器中执行 findViewById() 时发生 NullPointerException【英文标题】:NullPointerException occurs while executing findViewById() in custom ListView adaptor 【发布时间】:2021-02-19 13:45:24 【问题描述】:

我想在 android 设备上创建一个文件管理器,并在我的文件管理器中创建了一个自定义适配器作为 ListView 的内部类。

    private static final class FileAdaptor extends BaseAdapter 
           // These variables below would be set in my activity class
            private final List<String> data = new ArrayList<>(); // file name
            private final List<Drawable> data1 = new ArrayList<>(); // file icon
            private final List<String> data2 = new ArrayList<>(); // file modified data
            private final AppCompatActivity activity;
    
            public FileAdaptor(final AppCompatActivity activity) 
                this.activity = activity;
            
    
            @Override
            public final int getCount() 
                return data.size();
            
    
            @Override
            public final Object getItem(final int position) 
                return data.get(position);
            
    
            public final void setData(final List<String> data) 
                if (data != null) 
                    this.data.clear();
                    if (data.size() > 0)
                        this.data.addAll(data);
                    notifyDataSetChanged();
                
            
    
            public final void setData1(final List<Drawable> data) 
                if (data != null) 
                    this.data1.clear();
                    if (data.size() > 0)
                        this.data1.addAll(data);
                    notifyDataSetChanged();
                
            
    
            public final void setData2(final List<String> data) 
                if (data != null) 
                    this.data2.clear();
                    if (data.size() > 0)
                        this.data2.addAll(data);
                    notifyDataSetChanged();
                
            
    
            public final String getData(final int position) 
                return data.get(position);
            
    
            public final Drawable getData1(final int position) 
                return data1.get(position);
            
    
            public final String getData2(final int position) 
                return data2.get(position);
            
    
            @Override
            public final long getItemId(final int position) 
                return 0;
            
    
            @Override
            public final View getView(final int position, final View convertView, final ViewGroup parent) 
                View v;
                if (convertView == null) 
                    v = LayoutInflater.from(parent.getContext()).inflate(R.layout.file_manager_items, parent, false);
                 else v = convertView;
    
                ViewHolder h = (ViewHolder) v.getTag();
                if (h == null) 
                    h = new ViewHolder(Objects.requireNonNull(activity.findViewById(R.id.itemIcon)), Objects.requireNonNull(activity.findViewById(R.id.fileName)), Objects.requireNonNull(activity.findViewById(R.id.fileInfo)));
                    v.setTag(h);
                

/**********************
**Error occurred here**
**********************/

                    h.fileIcon.setImageDrawable(getData1(position));
                    h.fileName.setText(getData(position).substring(getData(position).lastIndexOf("/")));
                    h.fileInfo.setText(getData2(position));
                return v;
            
    
            private static final class ViewHolder 
                public final AppCompatImageView fileIcon;
                public final AppCompatTextView fileName;
                public final AppCompatTextView fileInfo;
    
                public ViewHolder(@NonNull final AppCompatImageView fileIcon, @NonNull final AppCompatTextView fileName, @NonNull final AppCompatTextView fileInfo) 
                    this.fileIcon = fileIcon;
                    this.fileName = fileName;
                    this.fileInfo = fileInfo;
                
            
    
            @NonNull
            @Override
            public final String toString() 
                return "FileAdaptor" +
                        "data=" + data +
                        ", activity=" + activity +
                        '';
            
        

下面是ListView中适配器的布局文件。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_
    android:layout_
    android:layout_centerInParent="true"
    android:orientation="horizontal">

    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/itemIcon"
        android:layout_
        android:layout_
        android:layout_gravity="center_horizontal|center_vertical"
        android:layout_weight="1"
        android:gravity="center" />

    <LinearLayout
        android:layout_
        android:layout_
        android:layout_gravity="left|start"
        android:layout_weight="10"
        android:gravity="start"
        android:orientation="vertical">

        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/fileName"
            android:layout_
            android:layout_
            android:textColor="@color/contents_title"
            android:textSize="18sp" />

        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/fileInfo"
            android:layout_
            android:layout_
            android:textColor="@color/contents" />
    </LinearLayout>
</LinearLayout>

这是 Activity 的布局文件,其中包括 listView。

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/fileManagerContentView"
    android:layout_
    android:layout_>

    <RelativeLayout
        android:layout_
        android:layout_
        android:orientation="vertical">

        <LinearLayout
            android:layout_
            android:layout_
            android:layout_above="@id/fileManagerBottomBar">

            <ListView
                android:id="@+id/mainContent"
                android:layout_
                android:layout_
                android:fillViewport="true"
                app:layout_behavior="@string/appbar_scrolling_view_behavior" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/fileManagerBottomBar"
            android:layout_
            android:layout_
            android:layout_alignParentBottom="true"
            android:orientation="horizontal">

            <androidx.appcompat.widget.AppCompatImageButton
                android:id="@+id/back"
                style="@style/Widget.AppCompat.Button.Borderless"
                android:layout_
                android:layout_
                android:layout_weight="1"
                app:srcCompat="@drawable/arrow_back" />

            <androidx.appcompat.widget.AppCompatImageButton
                android:id="@+id/next"
                style="@style/Widget.AppCompat.Button.Borderless"
                android:layout_
                android:layout_
                android:layout_weight="1"
                app:srcCompat="@drawable/arrow_next" />

            <androidx.appcompat.widget.AppCompatImageButton
                android:id="@+id/create"
                style="@style/Widget.AppCompat.Button.Borderless"
                android:layout_
                android:layout_
                android:layout_weight="1"
                app:srcCompat="@drawable/create" />

            <androidx.appcompat.widget.AppCompatImageButton
                android:id="@+id/refreshContents"
                style="@style/Widget.AppCompat.Button.Borderless"
                android:layout_
                android:layout_
                android:layout_weight="1"
                app:srcCompat="@drawable/reload" />

            <androidx.appcompat.widget.AppCompatImageButton
                android:id="@+id/parentFolder"
                style="@style/Widget.AppCompat.Button.Borderless"
                android:layout_
                android:layout_
                android:layout_weight="1"
                app:srcCompat="@drawable/up_dir" />
        </LinearLayout>
    </RelativeLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/navigationView"
        android:layout_
        android:layout_
        android:layout_gravity="start"
        app:headerLayout="@layout/navigation_header"
        app:menu="@menu/navigation_menu" />
</androidx.drawerlayout.widget.DrawerLayout>

现在看起来一切正常。但是,当我尝试在适配器的 getView 方法中为我的文本视图调用 setText 时,应用程序崩溃了,并输出 java.lang.NullPointerException,但在此之前我已经调用了 findViewById。

后来我为ViewHold中的对象注解了NonNull,为findViewById注解了Objects.requireNonNull,但是仍然出现错误,错误输出如下

java.lang.NullPointerException
        at java.util.Objects.requireNonNull(Objects.java:203)
        at com.example.filemanager.MainActivity$FileAdaptor.getView(MainActivity.java:500)
        at android.widget.AbsListView.obtainView(AbsListView.java:3271)
        at android.widget.ListView.makeAndAddView(ListView.java:2238)
        at android.widget.ListView.fillDown(ListView.java:838)
        at android.widget.ListView.fillFromTop(ListView.java:900)
        at android.widget.ListView.layoutChildren(ListView.java:1974)
        at android.widget.AbsListView.onLayout(AbsListView.java:3041)
        at android.view.View.layout(View.java:23754)
        at android.view.ViewGroup.layout(ViewGroup.java:7277)
        at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
        at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1818)
        at android.widget.LinearLayout.onLayout(LinearLayout.java:1584)
        at android.view.View.layout(View.java:23754)
        at android.view.ViewGroup.layout(ViewGroup.java:7277)
        at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1103)
        at android.view.View.layout(View.java:23754)
        at android.view.ViewGroup.layout(ViewGroup.java:7277)
        at androidx.drawerlayout.widget.DrawerLayout.onLayout(DrawerLayout.java:1231)
        at android.view.View.layout(View.java:23754)
        at android.view.ViewGroup.layout(ViewGroup.java:7277)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
        at android.view.View.layout(View.java:23754)
        at android.view.ViewGroup.layout(ViewGroup.java:7277)
        at androidx.appcompat.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:530)
        at android.view.View.layout(View.java:23754)
        at android.view.ViewGroup.layout(ViewGroup.java:7277)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
        at android.view.View.layout(View.java:23754)
        at android.view.ViewGroup.layout(ViewGroup.java:7277)
        at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
        at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
        at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
        at android.view.View.layout(View.java:23754)
        at android.view.ViewGroup.layout(ViewGroup.java:7277)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
        at com.android.internal.policy.DecorView.onLayout(DecorView.java:1059)
        at android.view.View.layout(View.java:23754)
        at android.view.ViewGroup.layout(ViewGroup.java:7277)
        at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3679)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3139)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2200)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8960)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:996)
        at android.view.Choreographer.doCallbacks(Choreographer.java:794)
        at android.view.Choreographer.doFrame(Choreographer.java:729)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:981)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:237)
        at android.app.ActivityThread.main(ActivityThread.java:7814)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1068)

谁能帮我解决这个问题

【问题讨论】:

在 setText 周围使用 try-catch 可以避免 NullPointerException 崩溃,但并不能解决这个问题。 getView 方法的第 500 行究竟写了什么? getView 中写的东西在我上面给出的 FileAdaptor 中,FileAdaptor 是 MainActivity 的一个内部类 错误给出了一个特定的行号,我不知道哪个行号是什么。你给我整个方法的事实对我没有帮助,除非你告诉我哪一行在哪里 【参考方案1】:

您的代码应该是这样的,因为视图是适配器布局的一部分,而不是 Activity 布局的一部分。

h = new ViewHolder(Objects.requireNonNull(v.findViewById(R.id.itemIcon)), Objects.requireNonNull(v.findViewById(R.id.fileName)), Objects.requireNonNull(v.findViewById(R.id.fileInfo)));
       

  

【讨论】:

【参考方案2】:

替换这个:

 h = new ViewHolder(Objects.requireNonNull(activity.findViewById(R.id.itemIcon)), Objects.requireNonNull(activity.findViewById(R.id.fileName)), Objects.requireNonNull(activity.findViewById(R.id.fileInfo)));

到:

h = new ViewHolder(Objects.requireNonNull(v.findViewById(R.id.itemIcon)), Objects.requireNonNull(v.findViewById(R.id.fileName)), Objects.requireNonNull(v.findViewById(R.id.fileInfo)));

因为你必须在你的项目视图中找到子项目

【讨论】:

【参考方案3】:

findViewById 可以返回一个Nullable 对象,因此它不会将您从 NPE 中拯救出来。

您在这里得到 NPE,因为您尝试使用活动布局中的 findViewById 来扩充您的视图,而它们实际上位于由 convertView 表示的项目布局中

所以,将activity 更改为converView,如下所示

h = new ViewHolder(Objects.requireNonNull(converView.findViewById(R.id.itemIcon)), Objects.requireNonNull(converView.findViewById(R.id.fileName)), Objects.requireNonNull(converView.findViewById(R.id.fileInfo)));

【讨论】:

以上是关于在自定义 ListView 适配器中执行 findViewById() 时发生 NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章

如何在自定义 ListView 适配器中处理 RadioGroup 的 onCheckedChangeListener

listview 项目未在自定义适配器的 getview 方法中显示分配的值

在自定义适配器类中发送服务器请求后,如何更改 Listview 按钮名称?

Android - 通过 getView 函数在自定义 listView 适配器内设置 ImageView 的源无法正常工作

如何在自定义 ListView 中获取 EditText 的所有值

为 listview 定义 onlick:在 listView 级别的 Onclick 与自定义视图适配器内的 Onclick