安卓闹钟App:从Add Alarm Fragment向Alarm Fragment的ListView添加条目失败求助
问题分析与修复方案
我仔细看了你的闹钟App代码,核心问题是你错误地把Activity专属的startActivityForResult/setResult机制用在了Fragment之间的通信上,这肯定没法实现数据传递。另外还有几个小细节也拖了后腿,下面一步步给你修正:
先说说关键问题点
AddFragment里调用getActivity().setResult()完全无效,因为你并没有启动新的Activity,只是切换了ViewPager里的FragmentAlarmFragment用startActivityForResult启动AddFragment是错误的,Add是Fragment不是Activity,这套返回结果的机制根本不适用MainActivity里在super.onCreate(savedInstanceState)之前读取Intent,这会导致初始化顺序错误,可能引发空指针- 同一Activity下的Fragment通信,官方推荐用接口回调或者ViewModel,而非Intent传递
具体修复步骤
1. 给Add Fragment定义回调接口
我们先在Add里定义一个回调接口,用来把选中的闹钟数据传递给宿主Activity,再由Activity转交给Alarm Fragment:
public class Add extends Fragment { // 定义回调接口,让宿主Activity实现 public interface OnAlarmAddedListener { void onAlarmAdded(Times newAlarm); } private OnAlarmAddedListener mListener; // 绑定Fragment到Activity时,检查Activity是否实现了接口 @Override public void onAttach(@NonNull Context context) { super.onAttach(context); try { mListener = (OnAlarmAddedListener) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " 必须实现OnAlarmAddedListener接口!"); } } // ... 保留你原来的onCreateView、控件初始化代码 ... public void addNewAlarm() { subject = label.getText().toString(); int hour = timePicker.getHour(); // 保留你原来的时间格式化逻辑 if (hour == 0) { hour += 12; format = "AM"; } else if (hour == 12) { format = "PM"; } else if (hour > 12) { hour -= 12; format = "PM"; } else { format = "AM"; } Times newAlarm = new Times(String.valueOf(hour), format, "test", subject); // 通过回调把数据传给MainActivity if (mListener != null) { mListener.onAlarmAdded(newAlarm); } // 可选:添加完成后自动切回Alarm Tab,提升用户体验 ((MainActivity) getActivity()).getViewPager().setCurrentItem(1); } }
2. 修改MainActivity实现回调接口并转发数据
让MainActivity实现刚才的接口,把接收到的新闹钟数据传递给Alarm Fragment:
public class MainActivity extends AppCompatActivity implements Add.OnAlarmAddedListener { private ViewPager viewPager; private SectionsPagerAdapter sectionsPagerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 一定要先调用super.onCreate! setContentView(R.layout.activity_main); // 移除原来错误的Intent读取逻辑,我们不用这种方式传数据了 sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager()); viewPager = findViewById(R.id.view_pager); viewPager.setAdapter(sectionsPagerAdapter); TabLayout tabs = findViewById(R.id.tabs); tabs.setupWithViewPager(viewPager); } // 实现回调接口,把数据转发给Alarm Fragment @Override public void onAlarmAdded(Times newAlarm) { // 获取当前的Alarm Fragment实例 Alarm alarmFragment = (Alarm) sectionsPagerAdapter.getItem(1); if (alarmFragment != null) { alarmFragment.addNewAlarm(newAlarm); } } // 给Fragment提供获取ViewPager的方法,用于切换Tab public ViewPager getViewPager() { return viewPager; } }
同时简化SectionsPagerAdapter,去掉没用的time参数:
public class SectionsPagerAdapter extends FragmentPagerAdapter { @StringRes private static final int[] TAB_TITLES = new int[]{R.string.tab_text_1, R.string.tab_text_2 , R.string.tab_text_3}; private final Context mContext; public SectionsPagerAdapter(Context context, FragmentManager fm) { super(fm); mContext = context; } @Override public Fragment getItem(int position) { Fragment fragment = new Fragment(); switch (position) { case 0: fragment = new Edit(); break; case 1: fragment = new Alarm(); // 移除原来的bundle传递逻辑 break; case 2: fragment = new Add(); break; } return fragment; } @Nullable @Override public CharSequence getPageTitle(int position) { return mContext.getResources().getString(TAB_TITLES[position]); } @Override public int getCount() { return 3; } }
3. 修改Alarm Fragment,移除错误逻辑并添加数据接收方法
因为Add是ViewPager里的Fragment,所以不用startActivityForResult,直接切换Tab即可,同时添加方法接收新数据:
public class Alarm extends Fragment { public ArrayList<Times> names = new ArrayList<>(); private ListView list; private Button goAddAlarm; private myAdapter adapter; public Alarm() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_alarm, container, false); list = v.findViewById(R.id.listview); goAddAlarm = v.findViewById(R.id.goAddAlarm); // 修改跳转逻辑:直接切到Add Tab(position=2) goAddAlarm.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ((MainActivity) getActivity()).getViewPager().setCurrentItem(2); } }); adapter = new myAdapter(getContext(), R.layout.custom_list_alarm , names); list.setAdapter(adapter); return v; } // 新增方法,接收从MainActivity传来的新闹钟 public void addNewAlarm(Times newAlarm) { names.add(newAlarm); adapter.notifyDataSetChanged(); // 通知ListView更新数据 } // 移除原来的onActivityResult方法,现在用不上了 }
4. 确保Times类实现Serializable接口
你在传递Times对象,一定要确保这个类实现Serializable接口,否则会抛出序列化错误:
public class Times implements Serializable { // 这里放你的类成员变量、构造方法、getter/setter等 }
为什么这样改?
- 同一Activity下的Fragment通信,官方推荐用接口回调(轻量场景)或ViewModel(复杂场景),Intent是给Activity之间传数据用的
- 去掉了错误的Activity级API调用,改用Fragment间标准的通信方式
- 修正了MainActivity的初始化顺序,避免了潜在的空指针问题
这样修改后,你在Add Fragment选好时间点击添加,数据就会通过回调传到Alarm Fragment,ListView也会自动更新啦!
内容的提问来源于stack exchange,提问作者Moaaz
相关产品推荐
相关产品推荐

