将AppCompatActivity转为Fragment时遇Intent构造错误,求技术帮助
Hey David,别着急,我帮你拆解一下你遇到的问题~
首先看你抛出的错误信息:
Error:(63, 33) error: no suitable constructor found for Intent(Events,Class
)
constructor Intent.Intent(String,Uri) is not applicable
(argument mismatch; Events cannot be converted to String)
constructor Intent.Intent(Context,Class<?>) is not applicable
(argument mismatch; Events cannot be converted to Context)
这个错误的核心很明确:你的Events是Fragment类,它本身不是Context类型,但Intent的Intent(Context, Class<?>)构造方法第一个参数必须传入Context对象,所以才会出现类型不匹配的报错。
接下来给你一步步的修复方案:
1. 修复Intent的Context参数
在Fragment中获取Context有几种安全的方式:
- 使用
requireActivity():获取当前Fragment依附的Activity(也就是你的MainActivity),它是Context的子类,而且如果Activity为空会直接抛出异常,避免空指针(推荐) - 使用
getActivity():效果和上面类似,但Activity为空时会返回null,需要额外判空 - 在View的点击事件里用
v.getContext()(你在FAB的点击监听里已经用了这个,是正确的)
所以把ListView Item点击里的Intent代码改成:
Intent intent = new Intent(requireActivity(), AddEvent.class); Uri currentVehicleUri = ContentUris.withAppendedId(EventContract.EventEntry.CONTENT_URI, id); intent.setData(currentVehicleUri); startActivity(intent); // Fragment可以直接调用startActivity,不需要额外包裹getActivity()
2. 修复onCreateView里的致命逻辑错误
你当前的onCreateView方法第一行就执行了return eventListView;,这会导致后面所有的代码(设置Toolbar、监听事件、初始化Loader等)完全不会执行!这是新手很容易犯的错误,必须调整代码顺序:
@Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { // 第一步:先加载整个布局的根View,而不是直接返回ListView View rootView = inflater.inflate(R.layout.nav_events, container, false); // 第二步:从根View中查找所有需要的控件 mToolbar = rootView.findViewById(R.id.toolbar); ((AppCompatActivity) requireActivity()).setSupportActionBar(mToolbar); mToolbar.setTitle("Events"); eventListView = rootView.findViewById(R.id.list); View emptyView = rootView.findViewById(R.id.empty_view); eventListView.setEmptyView(emptyView); mCursorAdapter = new EventCursorAdapter(requireActivity(), null); eventListView.setAdapter(mCursorAdapter); // 设置ListView Item点击监听 eventListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { Intent intent = new Intent(requireActivity(), AddEvent.class); Uri currentVehicleUri = ContentUris.withAppendedId(EventContract.EventEntry.CONTENT_URI, id); intent.setData(currentVehicleUri); startActivity(intent); } }); // 设置FAB点击监听 mAddEventButton = rootView.findViewById(R.id.fab); mAddEventButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(v.getContext(), AddEvent.class); startActivity(intent); } }); // 初始化Loader getLoaderManager().initLoader(VEHICLE_LOADER, null, this); // 最后返回根View return rootView; }
这里要注意:
- 不要直接返回ListView,要返回整个布局的根View(因为你的布局里包含Toolbar、FAB等其他控件)
- 所有控件的查找都要从inflate出来的
rootView里获取,而不是调用getView()——getView()只有在onCreateView执行完成后才会返回当前Fragment的View,在onCreateView内部调用会得到null。
3. 其他小优化建议
- 初始化
eventDbHelper的时候,不要在成员变量里直接调用getActivity()(此时Fragment还未依附到Activity,会得到null),可以转移到onViewCreated方法中:@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); eventDbHelper = new EventDbHelper(requireActivity()); } - 如果你使用的是AndroidX库,推荐用
getSupportLoaderManager()替代getLoaderManager(),兼容性更好。
修改完以上几个点之后,你的Intent错误和后续的逻辑失效问题应该就能解决了~
内容的提问来源于stack exchange,提问作者DavidCoder

