Android选择文件按钮OnClickListener触发java.lang.NullPointerException问题求助
嘿,我帮你排查了代码里的问题,这个空指针异常主要是几个细节没处理到位,我给你逐个梳理清楚:
核心问题:Button实例为null导致监听器设置失败
你遇到的java.lang.NullPointerException出现在btnChoiseFile.setOnClickListener(listenerChoiceFile)这一行,说明btnChoiseFile是null,根本没找到对应的控件。
问题1:布局文件不匹配
你的Activity是Main2Activity,但onCreate里设置的布局是R.layout.activity_main,如果这个布局文件里没有id为button2的Button控件,findViewById(R.id.button2)就会返回null。
- 修复方案:
- 打开
activity_main.xml确认是否存在android:id="@+id/button2"的Button; - 如果
Main2Activity对应的布局是activity_main2.xml,把setContentView(R.layout.activity_main)改成setContentView(R.layout.activity_main2)。
- 打开
问题2:ImageView成员变量被局部变量覆盖
你在类里定义了成员变量ImageView imgView;,但在onCreate里又重新声明了局部变量ImageView imgView = (ImageView) findViewById(R.id.img_view);,这会导致成员变量imgView始终为null,后续onActivityResult里调用imgView.setImageBitmap()也会触发空指针。
- 修复方案:
把onCreate里的代码改成:
去掉前面的imgView = (ImageView) findViewById(R.id.img_view);ImageView声明,直接给成员变量赋值。
问题3:监听器里重复调用startActivityForResult
你的listenerChoiceFile里连续调用了两次startActivityForResult(intent, SELECT_PICTURE),而且intent.setType("*/*")放在了第一次调用之后,逻辑混乱还多余。
- 修复方案:
调整顺序,只调用一次启动方法:View.OnClickListener listenerChoiceFile = new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); intent.setType("*/*"); startActivityForResult(intent, SELECT_PICTURE); } };
问题4:过时方法的兼容性问题
managedQuery已经被废弃,建议用ContentResolver.query替代,避免潜在的兼容性问题,同时记得关闭Cursor防止内存泄漏。
- 修复方案:
public String getPath(Uri uri) { String[] projection = {MediaStore.Images.Media.DATA}; Cursor cursor = getContentResolver().query(uri, projection, null, null, null); if (cursor == null) return null; int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String path = cursor.getString(column_index); cursor.close(); return path; }
完整修复后的代码
public class Main2Activity extends AppCompatActivity { Button btnChoiseFile; ImageView imgView; private static final int SELECT_PICTURE = 0; String filePath = ""; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 确保布局文件正确对应 setContentView(R.layout.activity_main); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 300); } } btnChoiseFile = findViewById(R.id.button2); // 增加非空判断,额外防护 if (btnChoiseFile != null) { btnChoiseFile.setOnClickListener(listenerChoiceFile); } // 给成员变量赋值 imgView = findViewById(R.id.img_view); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case SELECT_PICTURE: // 增加结果状态判断 if (resultCode == RESULT_OK && data != null) { Uri selectedImage = data.getData(); filePath = getPath(selectedImage); Log.d("path", filePath); if (imgView != null && filePath != null) { imgView.setImageBitmap(BitmapFactory.decodeFile(filePath)); } } break; } } public String getPath(Uri uri) { String[] projection = {MediaStore.Images.Media.DATA}; Cursor cursor = getContentResolver().query(uri, projection, null, null, null); if (cursor == null) return null; int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String path = cursor.getString(column_index); cursor.close(); return path; } View.OnClickListener listenerChoiceFile = new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); intent.setType("*/*"); startActivityForResult(intent, SELECT_PICTURE); } }; }
另外,以后调用findViewById后最好都加个非空判断,能更稳妥地避免空指针~
内容的提问来源于stack exchange,提问作者Yassine

