【问题标题】:How to update a TextView of an Activity from another class如何从另一个类更新 Activity 的 TextView
【发布时间】:2020-08-17 00:07:38
【问题描述】:

我是 Android/Java 编程新手。我有两个班,一个是Activity,另一个是普通班。我的活动类包含TextView。我可以从普通类更新活动类的TextView 吗?我尝试了随机代码,但失败了。

// activity class
public class MainMenu extends Activity {
    public TextView txtView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView txtView = (TextView)findViewById(R.id.text);   
    }
}

// Other class
public class ClassB {
    public ClassB() {
        public void Update() {
            TextView txtView = (TextView)findViewById(R.id.text);
            txtView.setText("Hello");
        }
    }
}

【问题讨论】:

    标签: java android


    【解决方案1】:

    您必须通过构造函数传递上下文引用。

    public class ClassB {
       Context context;
       public ClassB(Context context){
         this.context=context;
       }
    
       public void Update(){
            TextView txtView = (TextView) ((Activity)context).findViewById(R.id.text);
            txtView.setText("Hello");
       }
    

    【讨论】:

    • findViewById 是上下文的方法吗?
    • txtViewnull
    • 这是一个令人惊讶的不干净的解决方案。我很惊讶看到它有这么多的支持和接受。
    • 最好使用stackoverflow.com/a/27939196/5484290所描述的接口
    【解决方案2】:

    前面的两个示例要求在另一个类中直接使用TextView。但是,有些情况下TextView 不应该出现在其他类中,例如,您的ClassB 用于更新各种活动,其中一些活动更新TextViews,而其他活动可能更新EditTexts。

    因此,以下解决方案可以指导您如何将您的 TextView 与其他类分离,但您仍然可以实现您想要的。它使用的是接口方法。

    首先,声明一个可以让ClassB 与Activity 通信的接口,并将其命名为MyCallback

    public interface MyCallback {
        // Declaration of the template function for the interface
        public void updateMyText(String myString);
    }
    

    接下来在您的 Activity 中,实现 MyCallback,从而实现它的函数定义。在这个函数中,你会收到来自ClassB的字符串,你可以做任何你想做的事情,例如,更新TextView(或EditText等):

    public class MyActivity extends AppCompatActivity implements MyCallback {
        // ... whatever code of your activity
    
        @Override
        public void updateMyText(String myString) {
            ((TextView)findViewById(R.id.text)).setText(myString);
        }
    }
    

    最后,您可以声明 ClassB 接受 MyCallback(即,您的 Activity 类对象也是 MyCallback)。从那里您可以使用ClassB 与Activity 进行通信,并通过updateMyText 函数让它更新其TextView

    public class ClassB {
        MyCallback myCallback = null;
    
        public ClassB(MyCallback callback) {
            this.myCallback = callback;
        }
    
        public void doSomething() {
            // Do something to get String
            String myString = str;
    
            if (myCallback != null) {
                myCallback.updateMyText(myString);
            }
        }
    }
    

    希望这有助于更好地展示将 Activity 与 ClassB 正确解耦的架构结构。

    【讨论】:

    • 你把接口放在了哪个类?谢谢
    • 你可以把它作为一个顶级接口,也可以放在 ClassB 中
    • 这个解决方案在模拟器上完美运行,但是一旦myString 被触发到接口函数中,它就不会在我的物理设备上更新。
    • 你是从哪个班级来的? public ClassB(MyCallback callback) { this.myCallback = callback; }'
    • @RobinHood,你可以从MyActivity实例化ClassB
    【解决方案3】:

    这实际上是一个看似“简单”的问题,但实际上是 Android 开发环境中的一个复杂问题。

    Activity 是“进程入口点”,这意味着您看到的任何 Activity 都可以充当“启动时应用程序的第一个入口点”。人们认为只有带有MAIN/LAUNCHER意图过滤器的Activity才能在启动时启动,但这是错误的。

    任何 Activity 都可以充当“第一个 Activity”,因为 Android 可以使用当前活动的导航堆栈从任意点重新启动它。

    无论如何,考虑到这一点,一个 Activity 可以显示一个View,人们经常使用 Activity 来保存他们应用程序的每个屏幕(而不是将其用作入口点,并在其中交换视图控制器~碎片)。

    因此,如果您有多个活动,那么您需要在它们之间共享数据,以便您考虑到两个活动都可以随时启动 first 应用的Activity


    为此,您需要做的不是“直接从另一个类设置文本视图的文本”,而是需要修改可观察的共享数据

    新发布的官方Android Architecture Components提供了LiveData<T>类,该类有一个子类MutableLiveData<T>

    要将数据从一个类更新到另一个 Activity,您必须将全局数据公开为 LiveData

    public class MyApplication extends Application {
        private static MyApplication INSTANCE;
    
        DataRepository dataRepository; // this is YOUR class
    
        @Override
        public void onCreate() {
            super.onCreate();
            INSTANCE = this;
            dataRepository = new DataRepository();
        }
    
        public static MyApplication get() {
            return INSTANCE;
        }
    }
    

    DataRepository 应该暴露 LiveData:

    public class DataRepository {
        private final MutableLiveData<MyData> data = new MutableLiveData<>();
    
        public LiveData<MyData> getMyData() {
            return data;
        }
    
        public void updateText(String text) {
            MyData newData = data.getValue()
                                 .toBuilder() // immutable new copy
                                 .setText(text)
                                 .build();
            data.setValue(newData);
        }
    }
    

    Activity 订阅的地方:

    public class MyActivity extends BaseActivity {
        DataRepository dataRepository;
    
        TextView textView;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            MyApplication app = (MyApplication)getApplicationContext();
            dataRepository = app.getDataRepository();
    
            setContentView(R.layout.main_activity);
            textView = findViewById(R.id.textview);
    
            dataRepository.getMyData().observe(this, new Observer() {
                @Override
                public void onChange(MyObject myObject) {
                    textView.setText(myObject.getText());
                }
            }
        }
    

    因此,要更新此文本,您需要获取 DataRepository 类,并在其上调用 updateText

    DataRepository dataRepository = MyApplication.get().dataRepository();
    dataRepository.updateText("my new text");
    

    这将正确更新您的活动文本视图。

    请注意,您还应该将数据持久化到onSaveInstanceState(Bundle,以免丢失(假设数据不是来自磁盘)。

    为此,您需要执行以下操作:

    public class BaseActivity extends AppCompatActivity {
        DataRepository dataRepository;
    
        private static boolean didRestoreGlobals = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            MyApplication app = (MyApplication)getApplicationContext();
            dataRepository = app.getDataRepository();
    
            super.onCreate(savedInstanceState);
            if(!didRestoreGlobals) {
                didRestoreGlobals = true;
                if(savedInstanceState != null) {
                    dataRepository.restoreState(savedInstanceState.getBundle("dataRepository"));
                }
            }
        }
    
        @Override
        protected void onSaveInstanceState(Bundle bundle) {
            super.onSaveInstanceState(bundle);
            bundle.putBundle("dataRepository", dataRepository.saveState());
        }
    }
    

    然后将saveState/restoreState方法相应地添加到DataRepository

    【讨论】:

    • 您能否举例说明 MyData 的外观?
    • 任何 Kotlin 数据类,或自动值生成的值类。或者只是一些带有 getter/setter 字段的随机 POJO。
    • @XO。我认为 LocalBroadcastManager 在发送事件时没有人订阅时可能会丢失事件,不是吗? LiveData 保留最新的。我已经很久没有使用 LocalBroadcastManager 了,因为你需要一个 Intent,而你可以只使用 EventBus(虽然我不能告诉你 LBM 是否可以跨进程工作,但事件总线肯定不会)
    • @XO。实际上,LBM 已被弃用,因为它似乎可以跨进程工作,但事实并非如此。所以是的,与 LocalBroadcastManager 相比,这是现在的首选替代方案,它似乎“更好”只是因为您可以从 anywhere 的上下文中获取 LBM 的实例
    【解决方案4】:

    您可以在您的Activity 中创建一个getter 方法。

    在您的Activity 班级中:

    public TextView getTextView()
    {
    
    TextView txtView = (TextView)findViewById(R.id.text);
    return txtView;
    }
    

    在您的ClassB 班级中:

    public void Update()
    {
              MainMenu obj = new MainMenu();
              TextView tv = obj.getTextView();
              tv.setText("hello");
    
    }
    

    【讨论】:

    • new MainMenu() MainMenu 是一个活动?这个答案再正确不过了。
    • 诀窍在于,如果 MainActivity 实际上是一个活动,那么它应该以一个意图启动。但是,如果它只是普通课程,那么如果您相应地编辑帖子,我也会删除反对票 - 就目前而言,它可能宣传非常糟糕的做法
    【解决方案5】:

    如果您在活动类中创建其他类的对象(ClassB),最简单的解决方案是通过构造函数传递TextView如果您没有在活动类中创建对象这个答案不会有帮助)。因此,您的示例应如下所示:

    // activity class
    public class MainMenu extends Activity {
        public TextView txtView;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            txtView = (TextView)findViewById(R.id.text);
            //instantiating a object of the ClassB and passing tv
            ClassB obj = new ClassB(txtView);
        }    
    
    }
    
    // other class
    public class ClassB {
       //declarre tv
       TextView txtView;
    
       //get the tv as arg
       public ClassB(TextView tv){
            txtView = tv;
       }
    
       public void Update(){
            txtView.setText("Hello");
       }
    }
    

    【讨论】:

      【解决方案6】:

      我有一个 XML 页面 (Bert.XML),其中包含四个 TextView,ID 分别为 TextView1id、TextView2id、TextView3id 和 TextView4id

       <GridLayout
          android:id = "@+id/gridLayout"
          android:layout_width = "match_parent"
          android:layout_height = "wrap_content"
          android:paddingTop="10dp">
      
          <TextView
              android:id = "@+id/TextView1id"
              android:layout_gravity="end"
              android:hint = "@+id/Risico"
              android:textSize="@dimen/edit_size"
              android:layout_height = "wrap_content"
              android:layout_width = "fill_parent"
              android:layout_column = "0"
              android:layout_row = "1"
              android:layout_columnSpan = "3"
              />
          <TextView
              android:id = "@+id/TextView2id"
              android:layout_gravity="end"
              android:hint = "@+id/Risico"
              android:textSize="@dimen/edit_size"
              android:layout_height = "wrap_content"
              android:layout_width = "fill_parent"
              android:layout_column = "0"
              android:layout_row = "2"
              android:layout_columnSpan = "3"
              />
          <TextView
              android:id = "@+id/TextView3id"
              android:layout_gravity="end"
              android:hint = "@+id/Risico"
              android:textSize="@dimen/edit_size"
              android:layout_height = "wrap_content"
              android:layout_width = "fill_parent"
              android:layout_column = "0"
              android:layout_row = "3"
              android:layout_columnSpan = "3"
              />
          <TextView
              android:id = "@+id/TextView4id"
              android:layout_gravity="end"
              android:hint = "@+id/Risico"
              android:textSize="@dimen/edit_size"
              android:layout_height = "wrap_content"
              android:layout_width = "fill_parent"
              android:layout_column = "0"
              android:layout_row = "4"
              android:layout_columnSpan = "3"
              />
      
      </GridLayout>
      

      此视图的代码如下所示。在这里,我通过邮件类更改 TextViews 的文本。 Activity 已作为 Mail Class 的参数给出

      package nl.yentel.finekinney;
      import android.app.Activity;
      import android.os.Bundle;
      import androidx.appcompat.app.AppCompatActivity;
      import android.widget.TextView;
      
      public class Bert extends AppCompatActivity {
      private TextView theObject;
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_bert);
      
              //both findViewByID work
          theObject = this.findViewById(R.id.TextView2id);
          theObject = findViewById(R.id.TextView2id);
      
          Mail theMail=new Mail();
          theMail.activity=this;
          theMail.NameOfObject="TextView2id";
          theMail.KindOfObject="TextView";
          theMail.Mail();
      
          CalculateFromClass(this);
          Calculate(this);
      }
      //Calculate(dezeActiviteit);
      public void Calculate(Activity dezeActiviteit) {
      //here you should include dezeActiviteit which can be called from the Class
          theObject = dezeActiviteit.findViewById(R.id.TextView1id);
          theObject.setText("text from method");
      }
      public void CalculateFromClass(Activity dezeActiviteit) {
          //here you should include dezeActiviteit which can be called from the Class
          theObject = dezeActiviteit.findViewById(R.id.TextView4id);
          theObject.setText("text from Class");
          }
      }
      

      我的邮件类看起来像这样

      package nl.yentel.finekinney;
      import android.app.Activity;
      import android.widget.EditText;
      import android.widget.TextView;
      import androidx.appcompat.app.AppCompatActivity;
      
      public class Mail extends AppCompatActivity {
          public String NameOfObject;
          public String KindOfObject;
          public Activity activity;
      
      void Mail() {
          //if the name and kind has been given as an input parameter
          int ressourceId = activity.getResources().getIdentifier(NameOfObject, "id", activity.getPackageName());
          if (KindOfObject.equals("TextView")) {
              TextView TextViewObject = activity.findViewById(ressourceId); //VISArB 14
              TextViewObject.setText("this is a TextView");
          }
          if (KindOfObject.equals("EditText")) {
              EditText EditTextObject = activity.findViewById(ressourceId); //VISArB 14
              EditTextObject.setText("this is an EditText");
          }
          //if the name is hard coded
          TextView TextViewObject;
          TextViewObject = activity.findViewById(R.id.TextView3id);
          TextViewObject.setText("Hard coded ID");
      
          //if I want to run a method from my main Class
          Bert dezeBert = new Bert();
          dezeBert.CalculateFromClass(activity);
          }
      }
      

      【讨论】:

        【解决方案7】:

        这是访问另一个布局中的视图的 kotlin 代码:

        //inflate layout
          val view = inflate(this, R.layout.ly_custom_menu_item, null)
        //access view inside the inflated    
        val tv = view.findViewById<AppCompatTextView>(R.id.txtV_WalletBalance_SideMenu)
        //set value to view
         tv.text = "Value"
        //Add inflated Layout to something
        

        【讨论】:

          【解决方案8】:

          您可以执行以下操作。我试过了,它奏效了。 只需在调用另一个类的方法时传入对 TextView 的引用。 您的版本中的问题是 TextView 之间存在冲突,因为您要声明它两次。 而是只声明一次并将其作为参数传递,同时在另一个类中调用该方法。 干杯!!

              // activity class
          
          
          public class MainMenu extends Activity {
              public TextView txtView;
          
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
                  TextView txtView = (TextView)findViewById(R.id.text);
                  ClassB.update(txtView);
              }    
          
          }
          
              // other class
          
          
          
              public class ClassB {
                 public ClassB(){
                 }
          
                 public void update(TextView tv){
                      tv.setText("Hello");
                 }
              }
          

          【讨论】:

          • 那行不通,因为update 不是静态方法。
          【解决方案9】:

          这可以通过简单的步骤轻松管理。

          =================================

          1) 多个片段的活动

          下面一行可以通过 FindViewById 写在 Fragment 类中

          ((TextView) ((Activity) getActivity()).findViewById(R.id.textview)).setText("");

          【讨论】:

            猜你喜欢
            • 2019-07-25
            • 2023-03-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-15
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多