如何在Xamarin中从Setting_Fragment修改Main_Activity的TextView文本?
解决Xamarin中Fragment修改Activity控件文本的问题
我来帮你搞定这个问题!你当前的代码里,Setting_Content_Fragment是通过重新inflateMain_Layout来获取_Title_Text的——这等于创建了一个全新的布局实例,和Main_Activity中已经加载并显示在界面上的TextView完全不是同一个对象,所以点击按钮修改文本自然不会反映到实际界面上。
下面给你几种可行的解决方案,从简单快速到规范解耦都有,你可以根据项目情况选择:
方案一:接口回调(推荐的解耦方式)
这是Fragment和Activity通信的最佳实践,不会让Fragment直接依赖Activity,后续扩展也更灵活。
- 先定义一个回调接口,用来传递标题变更的消息:
public interface ITitleChangeListener { void OnTitleChanged(string newTitle); }
- 修改
Main_Activity,让它实现这个接口,同时把_Title_Text改成类的成员变量(之前是局部变量,外部访问不到):
[Activity(Label = "Project", MainLauncher = true)] public class Main_Activity : Activity, ITitleChangeListener { // 把TextView升级为类成员,方便在接口方法中访问 private TextView _Title_Text; protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); RequestWindowFeature(WindowFeatures.NoTitle); SetContentView(Resource.Layout.Main_Layout); ImageButton _Back_Button = (ImageButton)FindViewById(Resource.Id._Back_Button); _Title_Text = (TextView)FindViewById(Resource.Id._Title_Text); ImageButton _Exit_Button = (ImageButton)FindViewById(Resource.Id._Exit_Button); _Title_Text.Text = "Setting"; FragmentTransaction _Setting_Content_Adatper = this.FragmentManager.BeginTransaction(); Project.Fragments.Setting_Content_Fragment _Setting_Content = new Project.Fragments.Setting_Content_Fragment(); // 把当前Activity传递给Fragment作为回调对象 _Setting_Content.SetTitleChangeListener(this); _Setting_Content_Adatper.Replace(Resource.Id._Adapter_Content, _Setting_Content); _Setting_Content_Adatper.Commit(); // 下面的点击事件代码保持不变 _Back_Button.Click += (s, e) => { _Title_Text.Text = "Setting"; FragmentTransaction _Main_Fragment = this.FragmentManager.BeginTransaction(); Project.Fragments.Setting_Content_Fragment Setting_Main_Fragment = new Project.Fragments.Setting_Content_Fragment(); _Main_Fragment.Replace(Resource.Id._Adapter_Content, Setting_Main_Fragment); _Main_Fragment.AddToBackStack(null); _Main_Fragment.Commit(); }; _Exit_Button.Click += (s, e) => { Finish(); }; } // 实现接口方法,负责修改标题文本 public void OnTitleChanged(string newTitle) { _Title_Text.Text = newTitle; } }
- 最后修改
Setting_Content_Fragment,添加回调引用并在点击事件中使用:
public class Setting_Content_Fragment : Fragment { private ITitleChangeListener _titleChangeListener; // 提供方法让Activity设置回调 public void SetTitleChangeListener(ITitleChangeListener listener) { _titleChangeListener = listener; } public override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); } public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View _Setting_Content = inflater.Inflate(Resource.Layout.Setting_Content_Layout, container, false); Button _General_Button = (Button)_Setting_Content.FindViewById(Resource.Id._btnGeneral); _General_Button.Click += (s, e) => { // 通过回调通知Activity修改标题,用?.避免空引用 _titleChangeListener?.OnTitleChanged("General"); // 你的Fragment切换代码保持不变 FragmentTransaction _Main_Fragment = this.FragmentManager.BeginTransaction(); Project.Fragments.General_Content.General_Main_Fragment General_Main_Fragment = new Project.Fragments.General_Content.General_Main_Fragment(); _Main_Fragment.Replace(Resource.Id._Adapter_Content, General_Main_Fragment); _Main_Fragment.Commit(); }; return _Setting_Content; } }
方案二:直接访问Activity的控件(简单但耦合度高)
如果你的项目很小,不想折腾接口,也可以直接把Activity转换成Main_Activity类型,访问它的成员控件——这种方式虽然快,但会让Fragment和Activity紧密绑定,后续如果Activity结构变化,Fragment也要跟着改,不推荐复杂项目用。
- 先修改
Main_Activity,把_Title_Text改成public或者internal的类成员:
public class Main_Activity : Activity { // 改成public让Fragment可以访问 public TextView _Title_Text; protected override void OnCreate(Bundle savedInstanceState) { // 其他代码不变,只是把_Title_Text的定义改成public _Title_Text = (TextView)FindViewById(Resource.Id._Title_Text); // ... 剩下的代码保持原样 } }
- 然后在
Setting_Content_Fragment的点击事件里直接修改:
_General_Button.Click += (s, e) => { // 获取当前关联的Main_Activity实例 var mainActivity = (Main_Activity)Activity; // 直接修改Activity里的TextView mainActivity._Title_Text.Text = "General"; // 你的Fragment切换代码不变 FragmentTransaction _Main_Fragment = this.FragmentManager.BeginTransaction(); Project.Fragments.General_Content.General_Main_Fragment General_Main_Fragment = new Project.Fragments.General_Content.General_Main_Fragment(); _Main_Fragment.Replace(Resource.Id._Adapter_Content, General_Main_Fragment); _Main_Fragment.Commit(); };
方案三:使用ViewModel(MVVM模式,适合大型项目)
如果你的项目采用MVVM架构,用ViewModel共享数据是最优雅的方式——Activity和Fragment绑定同一个ViewModel,修改ViewModel的数据,UI会自动更新,完全解耦两者。
- 创建一个共享的ViewModel,实现
INotifyPropertyChanged接口:
public class MainViewModel : INotifyPropertyChanged { private string _titleText; public string TitleText { get => _titleText; set { _titleText = value; OnPropertyChanged(nameof(TitleText)); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
- 在
Main_Activity中初始化ViewModel并绑定TextView:
public class Main_Activity : Activity { private MainViewModel _viewModel; protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); RequestWindowFeature(WindowFeatures.NoTitle); SetContentView(Resource.Layout.Main_Layout); // 初始化ViewModel并设置初始标题 _viewModel = new MainViewModel(); _viewModel.TitleText = "Setting"; TextView _Title_Text = (TextView)FindViewById(Resource.Id._Title_Text); // 监听ViewModel的属性变化,更新TextView _viewModel.PropertyChanged += (s, e) => { if(e.PropertyName == nameof(MainViewModel.TitleText)) { _Title_Text.Text = _viewModel.TitleText; } }; // 把ViewModel传递给Fragment FragmentTransaction _Setting_Content_Adatper = this.FragmentManager.BeginTransaction(); Project.Fragments.Setting_Content_Fragment _Setting_Content = new Project.Fragments.Setting_Content_Fragment(); _Setting_Content.ViewModel = _viewModel; _Setting_Content_Adatper.Replace(Resource.Id._Adapter_Content, _Setting_Content); _Setting_Content_Adatper.Commit(); // 其他点击事件代码保持不变 } }
- 在
Setting_Content_Fragment中修改ViewModel的标题:
public class Setting_Content_Fragment : Fragment { // 接收Activity传递过来的ViewModel public MainViewModel ViewModel { get; set; } public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View _Setting_Content = inflater.Inflate(Resource.Layout.Setting_Content_Layout, container, false); Button _General_Button = (Button)_Setting_Content.FindViewById(Resource.Id._btnGeneral); _General_Button.Click += (s, e) => { // 修改ViewModel的标题,Activity会自动更新TextView ViewModel.TitleText = "General"; // 你的Fragment切换代码不变 FragmentTransaction _Main_Fragment = this.FragmentManager.BeginTransaction(); Project.Fragments.General_Content.General_Main_Fragment General_Main_Fragment = new Project.Fragments.General_Content.General_Main_Fragment(); _Main_Fragment.Replace(Resource.Id._Adapter_Content, General_Main_Fragment); _Main_Fragment.Commit(); }; return _Setting_Content; } }
这三种方案里,接口回调是最通用的,ViewModel适合大型项目,直接访问控件适合快速测试或小型项目,你可以根据自己的需求选~
内容的提问来源于stack exchange,提问作者user9694167
相关产品推荐
相关产品推荐

