【问题标题】:How to replace startActivityForResult with Activity Result APIs?如何用活动结果 API 替换 startActivityForResult?
【发布时间】:2020-08-10 19:14:11
【问题描述】:

我有一个主要活动作为调用不同活动的入口点,具体取决于条件。其中,我使用 Firebase Auth 来管理用户登录:

startActivityForResult(
            AuthUI.getInstance().createSignInIntentBuilder()
                    .setAvailableProviders(providers)
                    .build(),
            RC_SIGN_IN)

我覆盖onActivityResult()来区分返回的intent/data,例如:

 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {

        REQUEST_CODE_1 -> {
          // update UI and stuff
        }

        RC_SIGN_IN -> {
          // check Firebase log in
        }
        // ...
    }
}

使用documentation 强烈推荐的活动结果API,我知道我应该在ActivityResultLauncher 之前创建prepareCall(),并确保在我启动时活动处于创建状态,但我仍然没有不了解如何优雅地处理多个活动结果(至少在一个地方),例如 onActivityResult()

看着this article,看来我需要实现多个 ActivityResultContract 类型的子内部类(因此多个prepareCall() 的?),因为它们应该是不同的合同,我说的对吗?有人可以给我看一些反映上述onActivityResult()逻辑的骨架示例吗?

【问题讨论】:

    标签: android android-activity androidx onactivityresult


    【解决方案1】:

    您可以根据需要为结果调用任意数量的活动,并且每个活动都有单独的回调:

        val startForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult())
        { result: ActivityResult ->
            if (result.resultCode == Activity.RESULT_OK) {
                //  you will get result here in result.data
                }
    
            }
        }
    
        startForResult.launch(Intent(activity, CameraCaptureActivity::class.java))
    

    您只需要指定 Activity 类 - CameraCaptureActivity::class.java

    更新:

    The prepareCall() method has been renamed to registerForActivityResult() in Activity 1.2.0-alpha04 and Fragment 1.3.0-alpha04. And it should be startForResult.launch(...) in the last line

    感谢 Rafael Tavares 更新

    【讨论】:

    • prepareCall() 方法已在 Activity 1.2.0-alpha04Fragment 1.3.0-alpha04 中重命名为 registerForActivityResult()。最后一行应该是startForResult.launch(...)
    • 如果将'android.content.Intent intent, int requestCode' 作为参数传递呢?
    • 但是,为了从Activity2发回结果,和往常一样吗?:setResult(Bundle)???
    • 它返回result.data为null
    【解决方案2】:

    从现在开始,startActivityForResult() 已被弃用,因此请改用新方法。

    示例

    public void openActivityForResult() {
        
     //Instead of startActivityForResult use this one
            Intent intent = new Intent(this,OtherActivity.class);
            someActivityResultLauncher.launch(intent);
        }
    
    
    //Instead of onActivityResult() method use this one
    
        ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
                new ActivityResultContracts.StartActivityForResult(),
                new ActivityResultCallback<ActivityResult>() {
                    @Override
                    public void onActivityResult(ActivityResult result) {
                        if (result.getResultCode() == Activity.RESULT_OK) {
                            // Here, no request code
                            Intent data = result.getData();
                            doSomeOperations();
                        }
                    }
                });

    【讨论】:

    • 用谷歌登录 onActivityForResult 试试这个代码,没有运气,有什么建议吗?
    • 这段代码对我有用;不过不得不翻译成kotlin。谢谢
    • 感谢您不使用“var”和 lambdas :)
    • @paulsm4 不客气!我没有写它是因为它很容易理解。
    • 更简洁明了的描述✓
    【解决方案3】:

    首先,不要忘记将它添加到您的 Gradle 依赖项中

    implementation 'androidx.activity:activity-ktx:1.2.0-alpha05'
    implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha05'
    

    其次,通过扩展一个名为 ActivityResultContract&lt;I, O&gt; 的抽象类来创建结果协定。我的意思是输入的类型,O 的意思是输出的类型。然后你只需要重写2个方法

    class PostActivityContract : ActivityResultContract<Int, String?>() {
    
        override fun createIntent(context: Context, input: Int): Intent {
            return Intent(context, PostActivity::class.java).apply {
                putExtra(PostActivity.ID, postId)
            }
        }
    
        override fun parseResult(resultCode: Int, intent: Intent?): String? {
            val data = intent?.getStringExtra(PostActivity.TITLE)
            return if (resultCode == Activity.RESULT_OK && data != null) data
            else null
        }
    }
    

    最后,最后一步是将合约注册到Activity。您需要将自定义合约和回调传递给registerForActivityResult

    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
          
            start_activity_contract.setOnClickListener {
                openPostActivityCustom.launch(1)
            }
        }
      
        // Custom activity result contract
        private val openPostActivityCustom =
            registerForActivityResult(PostActivityContract()) { result ->
                // parseResult will return this as string?                                              
                if (result != null) toast("Result : $result")
                else toast("No Result")
            }
    }
    

    更多信息请查看Post

    【讨论】:

    • 正在寻找依赖项,感谢添加。获得支持:)
    • 很高兴听到它对你有帮助 :)
    • 指定请求Intent的好方法!谢谢!
    【解决方案4】:

    在这种情况下,AuthUI 返回的已经是一个 Intent,所以,我们像下面的例子一样使用它。

    private val startForResult =
            registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
                when(result.resultCode){
                    RESULT_OK -> {
                        val intent = result.data
                        // Handle the Intent...
                        mUser = FirebaseAuth.getInstance().currentUser
                    }
                    RESULT_CANCELED -> {
    
                    } else -> {
                } }
            }
    

    使用以下方式从任何地方(例如单击按钮)启动活动:

     AuthUI.getInstance().createSignInIntentBuilder().setAvailableProviders(providers)
                .build().apply {
                    startForResult.launch(this)
                }
    

    【讨论】:

      【解决方案5】:
      List<AuthUI.IdpConfig> providers = Arrays.asList(
                          new AuthUI.IdpConfig.EmailBuilder().build(),
                          new AuthUI.IdpConfig.GoogleBuilder().build());
      
                  ActivityResultLauncher<Intent> launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
                      if (result.getResultCode() == Activity.RESULT_OK) {
                          Log.v("LOGIN OK", "OK Result for Login");
                      }
                  });
                  
                  launcher.launch(AuthUI.getInstance()
                          .createSignInIntentBuilder()
                          .setIsSmartLockEnabled(false)
                          .setAvailableProviders(providers)
                          .build());
      

      查看更多详情: https://githubmemory.com/repo/firebase/FirebaseUI-Android/issues?cursor=Y3Vyc29yOnYyOpK5MjAyMS0wMy0wNVQyMjoxNzozMyswODowMM4xEAQZ&pagination=next&page=2

      【讨论】:

        【解决方案6】:

        将此用于 Firebase AuthUI;

        final ActivityResultLauncher<Intent> launcher = registerForActivityResult(
                    new FirebaseAuthUIActivityResultContract(), this::onSignInResult);
        
            binding.loginSignup.setOnClickListener(view -> {
                List<AuthUI.IdpConfig> provider = Arrays.asList(new AuthUI.IdpConfig.EmailBuilder().build(),
                        new AuthUI.IdpConfig.GoogleBuilder().build(),
                        new AuthUI.IdpConfig.PhoneBuilder().build());
                Intent intent = AuthUI.getInstance()
                        .createSignInIntentBuilder()
                        .setIsSmartLockEnabled(false)
                        .setAlwaysShowSignInMethodScreen(true)
                        .setAvailableProviders(provider)
                        .build();
                launcher.launch(intent);
            });
        
        
        private void onSignInResult(FirebaseAuthUIAuthenticationResult result) {
        
            if (result.getResultCode() == RESULT_OK) {
                FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
                if (user != null) {
                    if (user.getMetadata() != null) {
                        if (user.getMetadata().getCreationTimestamp() != user.getMetadata().getLastSignInTimestamp()) {
                            Toast.makeText(this, "Welcome Back", Toast.LENGTH_SHORT).show();
                        } else {
                            Toast.makeText(this, "Welcome", Toast.LENGTH_SHORT).show();
                        }
                        startMainActivity();
                    }
                }
            } else {
                IdpResponse response = result.getIdpResponse();
                if (response == null)
                    Toast.makeText(this, "Canceled By You", Toast.LENGTH_SHORT).show();
                else Log.d(TAG, "onCreate: ActivityResult" + response.getError());
            }
        }
        
        private void startMainActivity() {
            Intent intent = new Intent(LoginActivity.this, MainActivity.class);
            startActivity(intent);
            finish();
        }
        

        像这样。

        【讨论】:

          【解决方案7】:

          如果您从片段开始活动并将结果返回给片段,请执行此操作。

          在片段中:

          private lateinit var activityResult: ActivityResultLauncher<Intent>
          
          activityResult = registerForActivityResult(
              ActivityResultContracts.StartActivityForResult()) { result ->
              if (result.resultCode == RESULT_OK) {
                  val data = result.data
                  doSomeOperations(data)
              }
          }
          
          SomeActivity.showScreen(activityResult, requireContext())
          

          活动中:

          // activity?.setResult(Activity.RESULT_OK) doesn't change.
          
          companion object {
          
              fun showScreen(activityResult: ActivityResultLauncher<Intent>, context: Context) {
                  val intent = Intent(context, SomeActivity::class.java)
                  activityResult.launch(intent)
              }
          }
          

          【讨论】:

          • 什么是 SomeActivity.showScreen(activityResult, requireContext()) 我的意思是什么是 showScreen ???你能再描述一下吗?
          • @YogiArifWidodo, fun showScreen(activityResult: ActivityResultLauncher&lt;Intent&gt;, context: Context) 在答案的第二部分。您可以从片段中打开活动SomeActivity,执行一些操作并将该活动的结果返回给片段。
          猜你喜欢
          • 1970-01-01
          • 2017-03-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-24
          相关资源
          最近更新 更多