public void openSomeActivityForResult() {
Intent intent = new Intent(this, SomeActivity.class);
startActivityForResult(intent, 123);
}
@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == 123) {
doSomeOperations();
}
}
新方法(Java):
public void openSomeActivityForResult() {
Intent intent = new Intent(this, SomeActivity.class);
someActivityResultLauncher.launch(intent);
}
// You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayed
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
// There are no request codes
Intent data = result.getData();
doSomeOperations();
}
}
});
新方法(Kotlin) :
fun openSomeActivityForResult() {
val intent = Intent(this, SomeActivity::class.java)
resultLauncher.launch(intent)
}
var resultLauncher = registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// There are no request codes
val data: Intent? = result.data
doSomeOperations()
}
}
import android.content.Intent;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCaller;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class BetterActivityResult<Input, Result> {
/**
* Register activity result using a {@link ActivityResultContract} and an in-place activity result callback like
* the default approach. You can still customise callback using {@link #launch(Object, OnActivityResult)}.
*/
@NonNull
public static <Input, Result> BetterActivityResult<Input, Result> registerForActivityResult(
@NonNull ActivityResultCaller caller,
@NonNull ActivityResultContract<Input, Result> contract,
@Nullable OnActivityResult<Result> onActivityResult) {
return new BetterActivityResult<>(caller, contract, onActivityResult);
}
/**
* Same as {@link #registerForActivityResult(ActivityResultCaller, ActivityResultContract, OnActivityResult)} except
* the last argument is set to {@code null}.
*/
@NonNull
public static <Input, Result> BetterActivityResult<Input, Result> registerForActivityResult(
@NonNull ActivityResultCaller caller,
@NonNull ActivityResultContract<Input, Result> contract) {
return registerForActivityResult(caller, contract, null);
}
/**
* Specialised method for launching new activities.
*/
@NonNull
public static BetterActivityResult<Intent, ActivityResult> registerActivityForResult(
@NonNull ActivityResultCaller caller) {
return registerForActivityResult(caller, new ActivityResultContracts.StartActivityForResult());
}
/**
* Callback interface
*/
public interface OnActivityResult<O> {
/**
* Called after receiving a result from the target activity
*/
void onActivityResult(O result);
}
private final ActivityResultLauncher<Input> launcher;
@Nullable
private OnActivityResult<Result> onActivityResult;
private BetterActivityResult(@NonNull ActivityResultCaller caller,
@NonNull ActivityResultContract<Input, Result> contract,
@Nullable OnActivityResult<Result> onActivityResult) {
this.onActivityResult = onActivityResult;
this.launcher = caller.registerForActivityResult(contract, this::callOnActivityResult);
}
public void setOnActivityResult(@Nullable OnActivityResult<Result> onActivityResult) {
this.onActivityResult = onActivityResult;
}
/**
* Launch activity, same as {@link ActivityResultLauncher#launch(Object)} except that it allows a callback
* executed after receiving a result from the target activity.
*/
public void launch(Input input, @Nullable OnActivityResult<Result> onActivityResult) {
if (onActivityResult != null) {
this.onActivityResult = onActivityResult;
}
launcher.launch(input);
}
/**
* Same as {@link #launch(Object, OnActivityResult)} with last parameter set to {@code null}.
*/
public void launch(Input input) {
launch(input, this.onActivityResult);
}
private void callOnActivityResult(Result result) {
if (onActivityResult != null) onActivityResult.onActivityResult(result);
}
}
public class BaseActivity extends AppCompatActivity {
protected final BetterActivityResult<Intent, ActivityResult> activityLauncher = BetterActivityResult.registerActivityForResult(this);
}
之后,您可以简单地从任何类似的儿童活动中启动活动:
public void openSomeActivityForResult() {
Intent intent = new Intent(this, SomeActivity.class);
activityLauncher.launch(intent, result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
// There are no request codes
Intent data = result.getData();
doSomeOperations();
}
})
}
Here is an example on how to convert the existing code with the new one:
The old way:
public void openSomeActivityForResult() {
Intent intent = new Intent(this, SomeActivity.class);
startActivityForResult(intent, 123);
}
@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == 123) {
doSomeOperations();
}
}
The new way (Java):
public void openSomeActivityForResult() {
Intent intent = new Intent(this, SomeActivity.class);
someActivityResultLauncher.launch(intent);
}
// You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayed
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
// There are no request codes
Intent data = result.getData();
doSomeOperations();
}
}
});
The new way (Kotlin):
fun openSomeActivityForResult() {
val intent = Intent(this, SomeActivity::class.java)
resultLauncher.launch(intent)
}
var resultLauncher = registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// There are no request codes
val data: Intent? = result.data
doSomeOperations()
}
}
EDIT. A better approach would be to make it more generalised so that we can reuse it. The snippet below is used in one of my projects but beware that it's not well-tested and may not cover all the cases.
BetterActivityResult.java
import android.content.Intent;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCaller;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class BetterActivityResult<Input, Result> {
/**
* Register activity result using a {@link ActivityResultContract} and an in-place activity result callback like
* the default approach. You can still customise callback using {@link #launch(Object, OnActivityResult)}.
*/
@NonNull
public static <Input, Result> BetterActivityResult<Input, Result> registerForActivityResult(
@NonNull ActivityResultCaller caller,
@NonNull ActivityResultContract<Input, Result> contract,
@Nullable OnActivityResult<Result> onActivityResult) {
return new BetterActivityResult<>(caller, contract, onActivityResult);
}
/**
* Same as {@link #registerForActivityResult(ActivityResultCaller, ActivityResultContract, OnActivityResult)} except
* the last argument is set to {@code null}.
*/
@NonNull
public static <Input, Result> BetterActivityResult<Input, Result> registerForActivityResult(
@NonNull ActivityResultCaller caller,
@NonNull ActivityResultContract<Input, Result> contract) {
return registerForActivityResult(caller, contract, null);
}
/**
* Specialised method for launching new activities.
*/
@NonNull
public static BetterActivityResult<Intent, ActivityResult> registerActivityForResult(
@NonNull ActivityResultCaller caller) {
return registerForActivityResult(caller, new ActivityResultContracts.StartActivityForResult());
}
/**
* Callback interface
*/
public interface OnActivityResult<O> {
/**
* Called after receiving a result from the target activity
*/
void onActivityResult(O result);
}
private final ActivityResultLauncher<Input> launcher;
@Nullable
private OnActivityResult<Result> onActivityResult;
private BetterActivityResult(@NonNull ActivityResultCaller caller,
@NonNull ActivityResultContract<Input, Result> contract,
@Nullable OnActivityResult<Result> onActivityResult) {
this.onActivityResult = onActivityResult;
this.launcher = caller.registerForActivityResult(contract, this::callOnActivityResult);
}
public void setOnActivityResult(@Nullable OnActivityResult<Result> onActivityResult) {
this.onActivityResult = onActivityResult;
}
/**
* Launch activity, same as {@link ActivityResultLauncher#launch(Object)} except that it allows a callback
* executed after receiving a result from the target activity.
*/
public void launch(Input input, @Nullable OnActivityResult<Result> onActivityResult) {
if (onActivityResult != null) {
this.onActivityResult = onActivityResult;
}
launcher.launch(input);
}
/**
* Same as {@link #launch(Object, OnActivityResult)} with last parameter set to {@code null}.
*/
public void launch(Input input) {
launch(input, this.onActivityResult);
}
private void callOnActivityResult(Result result) {
if (onActivityResult != null) onActivityResult.onActivityResult(result);
}
}
With the above approach, you still have to register it before or during launching the activity or fragment attachment. Once defined, it can be reused within the activity or fragment. For example, if you need to start new activities in most of the activity, you can define a BaseActivity and register a new BetterActivityResult like this:
BaseActivity.java
public class BaseActivity extends AppCompatActivity {
protected final BetterActivityResult<Intent, ActivityResult> activityLauncher = BetterActivityResult.registerActivityForResult(this);
}
After that, you can simply launch an activity from any child activities like this:
public void openSomeActivityForResult() {
Intent intent = new Intent(this, SomeActivity.class);
activityLauncher.launch(intent, result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
// There are no request codes
Intent data = result.getData();
doSomeOperations();
}
})
}
Since you can set the callback function along with the Intent, you can reuse it for any activities.
Similarly, you can also use other activity contracts using the other two constructors.
There are 4 simple steps to follow while replacing the deprecated method startActivityForResult(...).
In place of overridden method onActivityResult(..) -
ActivityResultLauncher<Intent> activityResultLaunch = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == 123) {
// ToDo : Do your stuff...
} else if(result.getResultCode() == 321) {
// ToDo : Do your stuff...
}
}
});
For multiple custom requests, append the condition as
if (result.getResultCode() == 123) {
..
} else if(result.getResultCode() == 131){
..
} // so on..
val takePhotoForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
val intent = result.data
// handle photo from camera
}
}
val pickImageFromGalleryForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
val intent = result.data
// handle image from gallery
}
}
启动这样的活动/意图:
private fun startTakePhotoActivity() {
takePhotoForResult.launch(Intent(requireActivity(), TakePhotoActivity::class.java))
}
private fun pickImageFromGallery() {
val pickIntent = Intent(Intent.ACTION_PICK)
pickIntent.setDataAndType(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
"image/*"
)
pickImageFromGalleryForResult.launch(pickIntent)
}
然后,您需要 这,您可以摆脱项目中数百个 const val请求_ 值。
For those whose fragments have more than one requestCode, and if you are not sure how to handle multiple results by those requestCodes, you need to understand that requestCode is useless in the new approach.
I imagine the old way you code like this:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_CODE) {
when (requestCode) {
REQUEST_TAKE_PHOTO -> {
// handle photo from camera
}
REQUEST_PICK_IMAGE_FROM_GALLERY -> {
// handle image from gallery
}
}
}
}
In the new API, you need to implement the result of every requests in a separate ActivityResultContract:
val takePhotoForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
val intent = result.data
// handle photo from camera
}
}
val pickImageFromGalleryForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
val intent = result.data
// handle image from gallery
}
}
Then you need to start those activities/intents like this:
private fun startTakePhotoActivity() {
takePhotoForResult.launch(Intent(requireActivity(), TakePhotoActivity::class.java))
}
private fun pickImageFromGallery() {
val pickIntent = Intent(Intent.ACTION_PICK)
pickIntent.setDataAndType(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
"image/*"
)
pickImageFromGalleryForResult.launch(pickIntent)
}
By doing this, you can get rid of hundreds of const val REQUEST_ values in your project.
onActivityResult, startActivityForResult, requestPermissions, and onRequestPermissionsResult are deprecated on androidx.fragment from 1.3.0-alpha04, not on android.app.Activity.
Instead, you can use Activity Result APIs with registerForActivityResult.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.id.activity_main)
var ivPhoto = findViewById<ImageView>(R.id.ivPhoto)
var btnChoosePhoto = findViewById<Button>(R.id.btnChoosePhoto)
val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
ivPhoto.setImageURI(uri) // Handle the returned Uri
}
btnChoose.setOnClickListener {
getContent.launch("image/*")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.id.activity_main)
var ivPhoto = findViewById<ImageView>(R.id.ivPhoto)
var btnChoosePhoto = findViewById<Button>(R.id.btnChoosePhoto)
val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
ivPhoto.setImageURI(uri) // Handle the returned Uri
}
btnChoose.setOnClickListener {
getContent.launch("image/*")
}
}
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// There are no request codes
val data: Intent? = result.data
bluetoothAdapter.enable()
Toast.makeText(context, "Permission Granted: ", Toast.LENGTH_SHORT).show()
dynamicButton()
}
else{Toast.makeText(context, "You have to enable bluetooth to use this app.", Toast.LENGTH_SHORT).show()}
}.launch(intent)
The below code works in the Kotlin fragment to check the Bluetooth permission.
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// There are no request codes
val data: Intent? = result.data
bluetoothAdapter.enable()
Toast.makeText(context, "Permission Granted: ", Toast.LENGTH_SHORT).show()
dynamicButton()
}
else{Toast.makeText(context, "You have to enable bluetooth to use this app.", Toast.LENGTH_SHORT).show()}
}.launch(intent)
abstract class BaseActivity : AppCompatActivity()
{
private var requestCode: Int = -1
private var resultHandler: ActivityResultLauncher<Intent>? = null
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
registerForActivityResult()
}
private fun registerForActivityResult()
{
if (shouldRegisterForActivityResult())
{
resultHandler = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
onActivityResult(result.data, requestCode, result.resultCode)
this.requestCode = -1
}
}
}
fun startActivityForResult(requestCode: Int, intent: Intent)
{
this.requestCode = requestCode
resultHandler?.launch(intent)
}
protected open fun onActivityResult(data: Intent?, requestCode: Int, resultCode: Int)
{
// For sub activities
}
protected open fun shouldRegisterForActivityResult(): Boolean
{
// Sub activities that need the onActivityResult "mechanism", should override this and return true
return false
}
}
这是亚活动:
class SubActivity : BaseActivity()
{
companion object
{
private const val SOME_REQUEST_CODE = 300
}
private fun testActivityResult()
{
val intent = Intent(this, OtherActivity::class.java)
startActivityForResult(SOME_REQUEST_CODE, intent)
}
override fun shouldRegisterForActivityResult(): Boolean
{
return true
}
override fun onActivityResult(data: Intent?, requestCode: Int, resultCode: Int)
{
if (requestCode == SOME_REQUEST_CODE)
{
// Yes!
}
}
}
希望它能帮助某人
Here's my solution:
In our project, we had 20+ occurrences of startActivityForResult (and onActivityResult).
We wanted to change the code as little as possible (and keep using request codes), while introducing an elegant solution for future use.
Since lots of us, developers, use BaseActivity concept - why not take advantage of it?
Here is BaseActivity:
abstract class BaseActivity : AppCompatActivity()
{
private var requestCode: Int = -1
private var resultHandler: ActivityResultLauncher<Intent>? = null
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
registerForActivityResult()
}
private fun registerForActivityResult()
{
if (shouldRegisterForActivityResult())
{
resultHandler = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
onActivityResult(result.data, requestCode, result.resultCode)
this.requestCode = -1
}
}
}
fun startActivityForResult(requestCode: Int, intent: Intent)
{
this.requestCode = requestCode
resultHandler?.launch(intent)
}
protected open fun onActivityResult(data: Intent?, requestCode: Int, resultCode: Int)
{
// For sub activities
}
protected open fun shouldRegisterForActivityResult(): Boolean
{
// Sub activities that need the onActivityResult "mechanism", should override this and return true
return false
}
}
Here is SubActivity:
class SubActivity : BaseActivity()
{
companion object
{
private const val SOME_REQUEST_CODE = 300
}
private fun testActivityResult()
{
val intent = Intent(this, OtherActivity::class.java)
startActivityForResult(SOME_REQUEST_CODE, intent)
}
override fun shouldRegisterForActivityResult(): Boolean
{
return true
}
override fun onActivityResult(data: Intent?, requestCode: Int, resultCode: Int)
{
if (requestCode == SOME_REQUEST_CODE)
{
// Yes!
}
}
}
class DummyFragment : Fragment() {
//registering fragment for camera listener
private val takePhoto = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
val imageBitmap = it.data?.extras?.get("data") as Bitmap
// do your thing with the obtained bitmap
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
然后,按照通常的方式调用摄像机意图。并使用此上面创建的变量来启动意图。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
someRandomButton.setOnClickListener {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
takePhoto.launch(takePictureIntent)
}
}
I figured how to do it properly from a Fragment in Kotlin, to capture an image and handle returned bitmap. It is pretty much the same in other cases too.
First, you have to register the fragment to listen for the activity results. This has to be done before initiating the fragment, which means creating a member variable instead of initiating within onCreate function.
class DummyFragment : Fragment() {
//registering fragment for camera listener
private val takePhoto = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
val imageBitmap = it.data?.extras?.get("data") as Bitmap
// do your thing with the obtained bitmap
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
Then, call the camera intent as you would normally do. And use this above-created variable to launch the intent.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
someRandomButton.setOnClickListener {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
takePhoto.launch(takePictureIntent)
}
}
public class MyFragment extends Fragment implements OnActivityResultListener {
...
@Inject
ActivityResultLauncherWrapper activityResultLauncher;
//ActivityResultLauncherWrapper activityResultLauncher = new ActivityResultLauncherWrapper()
...
public void launnchActivity(@NotNull Intent intent) {
activityResultLauncher.launchIntentForResult(requireActivity(), intent, REQUEST_CODE_CONSTANT, this);
}
...
@Override
public void onActivityResultFromLauncher(int requestCode, int resultCode, Intent data) {/*do somthing*/}
...
}
我希望这有助于为您的情况构建解决方案。
My goal was to reuse the current implementation of the startActivityForResult method with minimum code changes. For that purpose, I made a wrapper class and interface with an onActivityResultFromLauncher method.
interface ActivityResultLauncherWrapper {
fun launchIntentForResult(activity: FragmentActivity, intent: Intent, requestCode: Int, callBack: OnActivityResultListener)
fun unregister()
interface OnActivityResultListener {
fun onActivityResultFromLauncher(requestCode: Int, resultCode: Int, data: Intent?)
}
}
class ActivityResultLauncherWrapperImpl : ActivityResultLauncherWrapper {
private var weakLauncher: WeakReference<ActivityResultLauncher<Intent>>? = null
override fun launchIntentForResult(
activity: FragmentActivity,
intent: Intent,
requestCode: Int,
callBack: ActivityResultLauncherWrapper.OnActivityResultListener
) {
weakLauncher = WeakReference(
activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
callBack.onActivityResultFromLauncher(requestCode, result.resultCode, result.data)
}
)
weakLauncher?.get()?.launch(intent)
}
override fun unregister() {
weakLauncher?.get()?.unregister()
}
}
I am using Dagger in my project and I injected the wrapper where it is needed
@Inject
lateinit var activityResultLauncher: ActivityResultLauncherWrapper
But the wrapper also can be instantiated directly:
val activityResultLauncher = ActivityResultLauncherWrapper()
then you have to change the startActivityForResult method with launchIntentForResult. Here is example where it is called from a fragment:
You will receive the result in the anonymous object.
You could use OnActivityResultListener in a Fragment or an FragmentActivity if you implement the Interface and refactor the current implementation like this:
class MyFragment : Fragment(), OnActivityResultListener {
...
override fun onActivityResultFromLauncher(requestCode: Int, resultCode: Int, data: Intent?) {/*do somthing*/}
...
}
As we know, the Kotlin class ActivityResultLauncherWrapper could be used in java code as well. There are java classes in my project as well. There is an example with implementation of the callback interface in a Fragment:
public class MyFragment extends Fragment implements OnActivityResultListener {
...
@Inject
ActivityResultLauncherWrapper activityResultLauncher;
//ActivityResultLauncherWrapper activityResultLauncher = new ActivityResultLauncherWrapper()
...
public void launnchActivity(@NotNull Intent intent) {
activityResultLauncher.launchIntentForResult(requireActivity(), intent, REQUEST_CODE_CONSTANT, this);
}
...
@Override
public void onActivityResultFromLauncher(int requestCode, int resultCode, Intent data) {/*do somthing*/}
...
}
I hope this helps to build the solution for your case.
//your actual fragment logic
class YourFragment : Fragment() {
//we can assign our request in init process
private val mRequestSelectFiles = buildSelectMultipleContentRequest {
onFilesSelected(it)
}
fun onSelectFiles() {
val mime = "*/*"
mRequestSelectFiles.launch(mime)
}
fun onFilesSelected(list: MutableList<Uri>?) {
//your logic
}
}
You can use extension functions for Koltin. For example:
//your actual fragment logic
class YourFragment : Fragment() {
//we can assign our request in init process
private val mRequestSelectFiles = buildSelectMultipleContentRequest {
onFilesSelected(it)
}
fun onSelectFiles() {
val mime = "*/*"
mRequestSelectFiles.launch(mime)
}
fun onFilesSelected(list: MutableList<Uri>?) {
//your logic
}
}
private val getResult =
registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
val value = it.data?.getStringExtra("input")
}
}
现在我们想在哪里启动活动以进行结果,我们可以使用 getResult.launch(Intent)
Sharing solution that I've found
First, register this activity for result using registerForActivityResult
This will return an object of type ActivityResultLauncher<Intent!>
Like this,
private val getResult =
registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
val value = it.data?.getStringExtra("input")
}
}
Now where ever we want to launch activity for result we can use getResult.launch(intent)
var mainActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result ->
if(result.resultCode == Activity.RESULT_OK){
val data = result.data
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
try {
// Google Sign In was successful, authenticate with Firebase
val account = task.getResult(ApiException::class.java)!!
Log.d(TAG, "firebaseAuthWithGoogle:" + account.id)
firebaseAuthWithGoogle(account.idToken!!)
} catch (e: ApiException) {
// Google Sign In failed, update UI appropriately
Log.w(TAG, "Google sign in failed", e)
}
}
}
In my case I was trying to use the intent I was moving directly to the next activity without using the Google Sign In.
What worked for me :
Inside OnCreate set the onClickListener for the sign-in button :
btnSignIn.setOnClickListener {
signIn()
}
private fun signIn() {
val intent = client.signInIntent
mainActivityResultLauncher.launch(intent)
}
In the above code I was writing the intent to go to the next activity but I had to write client.signInIntent
var mainActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ result ->
if(result.resultCode == Activity.RESULT_OK){
val data = result.data
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
try {
// Google Sign In was successful, authenticate with Firebase
val account = task.getResult(ApiException::class.java)!!
Log.d(TAG, "firebaseAuthWithGoogle:" + account.id)
firebaseAuthWithGoogle(account.idToken!!)
} catch (e: ApiException) {
// Google Sign In failed, update UI appropriately
Log.w(TAG, "Google sign in failed", e)
}
}
}
public class ActivityLauncher {
private final ActivityResultLauncher<Intent> launcher;
private ActivityResultCallback<ActivityResult> activityResultCallback;
private ActivityLauncher(@NonNull ActivityResultCaller caller,
@NonNull ActivityResultContract<Intent, ActivityResult> contract,
@Nullable ActivityResultCallback<ActivityResult> activityResultCallback) {
this.activityResultCallback = activityResultCallback;
this.launcher = caller.registerForActivityResult(contract, this::onActivityResult);
}
public static ActivityLauncher registerActivityForResult(
@NonNull ActivityResultCaller caller) {
return new ActivityLauncher(caller, new ActivityResultContracts.StartActivityForResult(), null);
}
public void launch(Intent intent, @Nullable ActivityResultCallback<ActivityResult> activityResultCallback) {
if (activityResultCallback != null) {
this.activityResultCallback = activityResultCallback;
}
launcher.launch(intent);
}
private void onActivityResult(ActivityResult result) {
if (activityResultCallback != null) activityResultCallback.onActivityResult(result);
}
public interface OnActivityResult {
void onActivityResultCallback(int requestCode, int resultCode, Intent data);
}
}
baseeactivity.java 中的代码
private final ActivityLauncher activityLauncher = ActivityLauncher.registerActivityForResult(this);
public void startActivityForResult(Intent intent, int requestCode, ActivityLauncher.OnActivityResult onActivityResult) {
activityLauncher.launch(intent, result -> onActivityResult.onActivityResultCallback(requestCode, result.getResultCode(), result.getData()));
}
,最后在每个活动中扩展了 baseeactivity ,实现 Activitylauncher.onactivity.onactivityResult and strong>和更改“ on ActivityResult”覆盖函数的名称为“ OnActivityResultCallback”。还可以删除 super.onactivityResult()
Combine with the above answer, I have a approach that compatible with the old way startActivityForResult() keep using requestCode without changing old code structure:
ActivityLauncher.class
public class ActivityLauncher {
private final ActivityResultLauncher<Intent> launcher;
private ActivityResultCallback<ActivityResult> activityResultCallback;
private ActivityLauncher(@NonNull ActivityResultCaller caller,
@NonNull ActivityResultContract<Intent, ActivityResult> contract,
@Nullable ActivityResultCallback<ActivityResult> activityResultCallback) {
this.activityResultCallback = activityResultCallback;
this.launcher = caller.registerForActivityResult(contract, this::onActivityResult);
}
public static ActivityLauncher registerActivityForResult(
@NonNull ActivityResultCaller caller) {
return new ActivityLauncher(caller, new ActivityResultContracts.StartActivityForResult(), null);
}
public void launch(Intent intent, @Nullable ActivityResultCallback<ActivityResult> activityResultCallback) {
if (activityResultCallback != null) {
this.activityResultCallback = activityResultCallback;
}
launcher.launch(intent);
}
private void onActivityResult(ActivityResult result) {
if (activityResultCallback != null) activityResultCallback.onActivityResult(result);
}
public interface OnActivityResult {
void onActivityResultCallback(int requestCode, int resultCode, Intent data);
}
}
Code in BaseActivity.java
private final ActivityLauncher activityLauncher = ActivityLauncher.registerActivityForResult(this);
public void startActivityForResult(Intent intent, int requestCode, ActivityLauncher.OnActivityResult onActivityResult) {
activityLauncher.launch(intent, result -> onActivityResult.onActivityResultCallback(requestCode, result.getResultCode(), result.getData()));
}
And finally in each Activity that extends BaseActivity, implements ActivityLauncher.OnActivityResult and change the name of override function "onActivityResult" to "onActivityResultCallback". Also rember to remove super.onActivityResult()
How to use: startActivityForResult(intent, requestCode, this)
It seems that onActivityResult is deprecated in the super class but you did not mention the super class name and compileSdkVersion here in your question.
In Java and Kotlin every class or method could be marked as deprecated simply by adding @Deprecated to it so check your super class you may extend a wrong class.
When a class is deprecated all of its methods are deprecated too.
To see a quick solution click on deprecated method and press Ctrl+Q in Android studio to see documentation of method there should be a solution for it.
In my project using androidx and API 29 as compileSdkVersion, this method is NOT deprecated in activities and fragments
class BetterActivityResult<Input, Result> private constructor(
caller : ActivityResultCaller,
contract : ActivityResultContract<Input, Result>,
var onActivityResult : ((Result) -> Unit)?,
) {
private val launcher : ActivityResultLauncher<Input> =
caller.registerForActivityResult(contract) { onActivityResult?.invoke(it) }
/**
* Launch activity, same as [ActivityResultLauncher.launch] except that it
* allows a callback
* executed after receiving a result from the target activity.
*/
/**
* Same as [.launch] with last parameter set to `null`.
*/
@JvmOverloads
fun launch(
input : Input,
onActivityResult : ((Result) -> Unit)? = this.onActivityResult,
) {
this.onActivityResult = onActivityResult
launcher.launch(input)
}
companion object {
/**
* Register activity result using a [ActivityResultContract] and an in-place
* activity result callback like
* the default approach. You can still customise callback using [.launch].
*/
fun <Input, Result> registerForActivityResult(
caller : ActivityResultCaller,
contract : ActivityResultContract<Input, Result>,
onActivityResult : ((Result) -> Unit)?,
) : BetterActivityResult<Input, Result> {
return BetterActivityResult(caller, contract, onActivityResult)
}
/**
* Same as [.registerForActivityResult] except
* the last argument is set to `null`.
*/
fun <Input, Result> registerForActivityResult(
caller : ActivityResultCaller,
contract : ActivityResultContract<Input, Result>,
) : BetterActivityResult<Input, Result> {
return registerForActivityResult(caller, contract, null)
}
/**
* Specialised method for launching new activities.
*/
fun registerActivityForResult(
caller : ActivityResultCaller,
) : BetterActivityResult<Intent, ActivityResult> {
return registerForActivityResult(caller, StartActivityForResult())
}
}
}
class BetterActivityResult<Input, Result> private constructor(
caller : ActivityResultCaller,
contract : ActivityResultContract<Input, Result>,
var onActivityResult : ((Result) -> Unit)?,
) {
private val launcher : ActivityResultLauncher<Input> =
caller.registerForActivityResult(contract) { onActivityResult?.invoke(it) }
/**
* Launch activity, same as [ActivityResultLauncher.launch] except that it
* allows a callback
* executed after receiving a result from the target activity.
*/
/**
* Same as [.launch] with last parameter set to `null`.
*/
@JvmOverloads
fun launch(
input : Input,
onActivityResult : ((Result) -> Unit)? = this.onActivityResult,
) {
this.onActivityResult = onActivityResult
launcher.launch(input)
}
companion object {
/**
* Register activity result using a [ActivityResultContract] and an in-place
* activity result callback like
* the default approach. You can still customise callback using [.launch].
*/
fun <Input, Result> registerForActivityResult(
caller : ActivityResultCaller,
contract : ActivityResultContract<Input, Result>,
onActivityResult : ((Result) -> Unit)?,
) : BetterActivityResult<Input, Result> {
return BetterActivityResult(caller, contract, onActivityResult)
}
/**
* Same as [.registerForActivityResult] except
* the last argument is set to `null`.
*/
fun <Input, Result> registerForActivityResult(
caller : ActivityResultCaller,
contract : ActivityResultContract<Input, Result>,
) : BetterActivityResult<Input, Result> {
return registerForActivityResult(caller, contract, null)
}
/**
* Specialised method for launching new activities.
*/
fun registerActivityForResult(
caller : ActivityResultCaller,
) : BetterActivityResult<Intent, ActivityResult> {
return registerForActivityResult(caller, StartActivityForResult())
}
}
}
在form var resultlauncher中创建一个变量:activityResultLauncher&lt; intent&gt;
创建一个私人函数,在其中您以这种基本格式开始初始化resultlauncher,
resultLauncher=registerForActivityResult(ActivityResultContracts.StartActivityForResult()){result ->
// copy paste the code from the onActivityResult replacing resultcode to result.resultCode
if(result.resultcode==Activity.Result_OK){
val data=result.data // this data variable is of type intent and you can use it
}else{
//code if you do not get the data
}
}
An alternate way to do this is in 3 steps. (Considering you have a startActivityForResult(0 and onActivityResult())
create a variable in the form var resultLauncher:ActivityResultLauncher<Intent>
create a private function where you initialize the resultLauncher in this basic format
resultLauncher=registerForActivityResult(ActivityResultContracts.StartActivityForResult()){result ->
// copy paste the code from the onActivityResult replacing resultcode to result.resultCode
if(result.resultcode==Activity.Result_OK){
val data=result.data // this data variable is of type intent and you can use it
}else{
//code if you do not get the data
}
}
Go to the line with startActivityForResult() and replace it with the line resultLauncher.launch(intent)
private int requestCode = -1;
private ActivityResultLauncher<Intent> resultHandler = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
registerForActivityResult();
}
private final void registerForActivityResult() {
if (shouldRegisterForActivityResult()) {
this.resultHandler = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback() {
public void onActivityResult(Object var1) {
this.onActivityResult((ActivityResult)var1);
}
public final void onActivityResult(ActivityResult result) {
Intrinsics.checkNotNullExpressionValue(result, "result");
AppActivityClass.onActivityResult(result.getData(), AppActivityClass.this.requestCode, result.getResultCode());
AppActivityClass.this.requestCode = -1;
}
});
}
}
public final void startActivityForResult(int requestCode, Intent intent) {
this.requestCode = requestCode;
if (resultHandler != null) {
resultHandler.launch(intent);
}
}
protected static void onActivityResult(Intent intent, int requestCode, int resultCode) {
}
protected Boolean shouldRegisterForActivityResult() {
return false;
}
现在在任何活动中使用此代码:
@Override
protected Boolean shouldRegisterForActivityResult() {
return true; // this will override the baseactivity method and we can use onactivityresult
}
private void someMethod(){
Intent i = new Intent(mContext,SomeOtherClassActivity.class);
startActivityForResult(101,i);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 101) {
if (resultCode == RESULT_OK) {
//revert from called class
}
}
}
dor506 answer worked for me as i use BaseActivity in most of my projects so it is easier for me to change the code in single file rather than all my activites. I have written the java version of this code.
BaseActivity code :
private int requestCode = -1;
private ActivityResultLauncher<Intent> resultHandler = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
registerForActivityResult();
}
private final void registerForActivityResult() {
if (shouldRegisterForActivityResult()) {
this.resultHandler = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback() {
public void onActivityResult(Object var1) {
this.onActivityResult((ActivityResult)var1);
}
public final void onActivityResult(ActivityResult result) {
Intrinsics.checkNotNullExpressionValue(result, "result");
AppActivityClass.onActivityResult(result.getData(), AppActivityClass.this.requestCode, result.getResultCode());
AppActivityClass.this.requestCode = -1;
}
});
}
}
public final void startActivityForResult(int requestCode, Intent intent) {
this.requestCode = requestCode;
if (resultHandler != null) {
resultHandler.launch(intent);
}
}
protected static void onActivityResult(Intent intent, int requestCode, int resultCode) {
}
protected Boolean shouldRegisterForActivityResult() {
return false;
}
Now in any activity use this code like this:
@Override
protected Boolean shouldRegisterForActivityResult() {
return true; // this will override the baseactivity method and we can use onactivityresult
}
private void someMethod(){
Intent i = new Intent(mContext,SomeOtherClassActivity.class);
startActivityForResult(101,i);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 101) {
if (resultCode == RESULT_OK) {
//revert from called class
}
}
}
resultContract =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// There are no request codes
val country = result.data?.getParcelableExtra<Country>("Country")
showLiveDemoDialogue(country)
}
}
startActivityForresult和OnActivityResult在Android 10 API 30中被弃用,现在我们有一种新的方法来使用registerforactivityResult并启动活动来
val intent = Intent(this, CountriesListActivity::class.java)
resultContract.launch(intent)
但是您应该在您想要的任何地方拨打启动和启动之前注册。
否则,您将获得此例外
attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
startActivityForResult and onActivityResult is deprecated in android 10 API 30 now we have a new way to get the result using registerForActivityResult
resultContract =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// There are no request codes
val country = result.data?.getParcelableExtra<Country>("Country")
showLiveDemoDialogue(country)
}
}
and to launch activity
val intent = Intent(this, CountriesListActivity::class.java)
resultContract.launch(intent)
but you should register before you call launch And launch wherever you want.
otherwise, you will get this exception
attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
public class MyBaseActivity extends AppCompatActivity {
private ActivityResultLauncher<Intent> activityLauncher;
protected static String ACTIVITY_REQUEST_CODE = "my.activity.request.code";
protected _originalIntent;
public void launchActivityForResult(Intent intent, int requestCode){
intent.putExtra(UGM_ACTIVITY_REQUEST_CODE, requestCode);
activityLauncher.launch(intent);
}
//
//In order to be signature compatible for the rest of derived activities,
//we will override the deprecated method with our own implementation!
//
@SuppressWarnings( "deprecation" )
public void startActivityForResult(Intent intent, int requestCode){
launchActivityForResult(intent, requestCode);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_originalIntent = getIntent();
//set the default result
setResult(Activity.RESULT_OK, _originalIntent);
activityLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
Intent intent = result.getData();
int requestCode = intent.getIntExtra(ACTIVITY_REQUEST_CODE, -1);
MyBaseActivity.this.onActivityResult(requestCode, result.getResultCode(), intent);
}
});
}
}
If you implement your base Activity like this, you may continure using startActivityForResult in old fashion.
The only limitation is you will have to use setResult(result, intent) to set the result within your activity.
The key is to let the result carry the request code back to the result consumer.
public class MyBaseActivity extends AppCompatActivity {
private ActivityResultLauncher<Intent> activityLauncher;
protected static String ACTIVITY_REQUEST_CODE = "my.activity.request.code";
protected _originalIntent;
public void launchActivityForResult(Intent intent, int requestCode){
intent.putExtra(UGM_ACTIVITY_REQUEST_CODE, requestCode);
activityLauncher.launch(intent);
}
//
//In order to be signature compatible for the rest of derived activities,
//we will override the deprecated method with our own implementation!
//
@SuppressWarnings( "deprecation" )
public void startActivityForResult(Intent intent, int requestCode){
launchActivityForResult(intent, requestCode);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_originalIntent = getIntent();
//set the default result
setResult(Activity.RESULT_OK, _originalIntent);
activityLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
Intent intent = result.getData();
int requestCode = intent.getIntExtra(ACTIVITY_REQUEST_CODE, -1);
MyBaseActivity.this.onActivityResult(requestCode, result.getResultCode(), intent);
}
});
}
}
resultLauncher.launch( consentIntent
)
var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// There are no request codes
// val data: Intent? = result.data
val message = result.data?.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
getOtpFromMessage(message)
}
}
In case you are using SMS consent API then use the following code (Kotlin):
resultLauncher.launch( consentIntent
)
var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// There are no request codes
// val data: Intent? = result.data
val message = result.data?.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
getOtpFromMessage(message)
}
}
fun AppCompatActivity.startForResult(intent: Intent,
onResult: (resultCode: Int, data: Intent?) -> Unit
) {
this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {result ->
onResult(result.resultCode, result.data)
}.launch(intent)
}
现在,在任何继承AppCompatactivity的活动中,您可以使用以下简单代码:
val i = Intent(this, TargetActivity::class.java)
startForResult(i) { resultCode, data ->
//put your code here like:
if (resultCode == RESULT_OK) {
//your code here...
}
}
}
fun AppCompatActivity.registerForResult(onResult: (resultCode: Int, data: Intent?) -> Unit):
ActivityResultLauncher<Intent> {
return this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
onResult(result.resultCode, result.data)
}
}
现在,在任何继承AppCompatactivity的活动内部,您可以在下面使用以下简单代码:
定义一个class Memeber变量为每个动作都需要结果
private val myActionResult = registerForResult { resultCode, data ->
//put your code here like:
if (resultCode == RESULT_OK) {
//your code here...
}
}
}
启动动作
val i = Intent(this, TargetActivity::class.java)
myActionResult.launch(i)
I am using kotlin extension to make it very simple. Add below extensiton fucntion in your Extenstions.kt file:
fun AppCompatActivity.startForResult(intent: Intent,
onResult: (resultCode: Int, data: Intent?) -> Unit
) {
this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {result ->
onResult(result.resultCode, result.data)
}.launch(intent)
}
Now, inside any activity that inherits AppCompatActivity, you can use below simple code:
val i = Intent(this, TargetActivity::class.java)
startForResult(i) { resultCode, data ->
//put your code here like:
if (resultCode == RESULT_OK) {
//your code here...
}
}
}
Update
Above implementaion may cause below exception: java.lang.IllegalStateException: LifecycleOwner xxxx is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
So registerForActivityResult should be called in advance for example before onCreate. Here is the alternative solution.
Add below extensiton fucntion in your Extenstions.kt file:
fun AppCompatActivity.registerForResult(onResult: (resultCode: Int, data: Intent?) -> Unit):
ActivityResultLauncher<Intent> {
return this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
onResult(result.resultCode, result.data)
}
}
Now, inside any activity that inherits AppCompatActivity, you can use below simple code:
Define a class memeber variable for every action requires result
private val myActionResult = registerForResult { resultCode, data ->
//put your code here like:
if (resultCode == RESULT_OK) {
//your code here...
}
}
}
Launch the action
val i = Intent(this, TargetActivity::class.java)
myActionResult.launch(i)
Adding on to the answers by muntashir akon and abhijeet, you can modify the new format to work like the old format by passing values in the intent, for example:
// calling class
....
val i = Intent(this@GEBShopActivity, BarcodeScannerActivity::class.java)
when(loadedFragment){
is ShopHomeFragment -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_SCAN_LIST_MAINT) }
is ShopListFragment -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_SCAN_LIST_MAINT) }
is ShopItemMaintFragment -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_SCAN_ITEM_MAINT) }
is ShopPriceFragment -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_PRICE_CAPTURE) }
is ShopCompareFragment -> { i.putExtra("myapp.result.code", CODE_ACTIVITY_PRICE_CAPTURE) }
}
shopFragmentLauncher.launch(i)
....
// called class
....
val resultIntent = Intent()
val bundle = Bundle()
bundle.putStringArrayList("scanned_barcodes", scanned_barcodes)
bundle.putInt("scan_count", scan_count)
resultIntent.putExtras(bundle)
resultIntent.putExtra("myapp.result.code", intent.getIntExtra("myapp.result.code", 0))
setResult(Activity.RESULT_OK, resultIntent)
....
This will allow you to keep the class called the same with just the one extra line to add your original called result code. Also allows you to create a reusable launcher instance.
val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
// Handle Uri
}
仅供参考,您应该能够使用它来获取任何 uri ,而不仅仅是用于图像。在这里使用下面。
import androidx.activity.result.contract.ActivityResultContracts
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
// ...
binding.selectPhotoButton.setOnClickListener {
selectPhoto()
}
}
fun selectPhoto() {
getContent.launch("image/*") // *** What Google said to do to launch the image library ***
}
// *** This is what Google posted to handle the Uri ***
val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
// Handle the returned Uri
binding.myImageView.setImageURI(uri)
}
}
2023 Kotlin Answer to get an Uri.
An example for those who are searching for a way to fetch an image from the image library and handle the Uri because super.onActivityResult(requestCode, resultCode, data) is crossed out/deprecated
val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
// Handle Uri
}
FYI you should be able to use this to get any Uri, not just for images. Here it is used below.
import androidx.activity.result.contract.ActivityResultContracts
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
// ...
binding.selectPhotoButton.setOnClickListener {
selectPhoto()
}
}
fun selectPhoto() {
getContent.launch("image/*") // *** What Google said to do to launch the image library ***
}
// *** This is what Google posted to handle the Uri ***
val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
// Handle the returned Uri
binding.myImageView.setImageURI(uri)
}
}
发布评论
评论(30)
可以在 developer.android.com )上获得基本培训。
这是如何使用新代码转换现有代码的一个示例:
旧方法:
新方法(Java):
新方法(Kotlin) :
编辑。一种更好的方法是使其更具概括性,以便我们可以重复使用它。下面的摘要用于我的一个项目中,但请注意,它没有经过充分测试,可能无法涵盖所有情况。
PrettractivityResult.java
采用上述方法,您仍必须在启动活动或片段附件之前或在启动片段附件之前进行注册。定义后,可以在活动或碎片中重复使用。例如,如果您需要在大多数活动中启动新活动,则可以定义
baseeactivity
并注册新的BetterActivityResult
like:baseactivity.java < /strong>
之后,您可以简单地从任何类似的儿童活动中启动活动:
由于您可以将回调函数与
Intent
一起设置,因此您可以将其重复使用任何活动。同样,您也可以使用其他两个构造函数使用其他活动合同。
A basic training is available at developer.android.com.
Here is an example on how to convert the existing code with the new one:
The old way:
The new way (Java):
The new way (Kotlin):
EDIT. A better approach would be to make it more generalised so that we can reuse it. The snippet below is used in one of my projects but beware that it's not well-tested and may not cover all the cases.
BetterActivityResult.java
With the above approach, you still have to register it before or during launching the activity or fragment attachment. Once defined, it can be reused within the activity or fragment. For example, if you need to start new activities in most of the activity, you can define a
BaseActivity
and register a newBetterActivityResult
like this:BaseActivity.java
After that, you can simply launch an activity from any child activities like this:
Since you can set the callback function along with the
Intent
, you can reuse it for any activities.Similarly, you can also use other activity contracts using the other two constructors.
从现在开始,
startActivityForresult()
已被弃用,因此请使用新方法。kotlin示例
From now,
startActivityForResult()
has been deprecated so use new method instead of that.Kotlin Example
替换弃用方法
startActivityForresult(...)
时,有4个简单的步骤需要遵循。代替覆盖方法
onActivityResult(..)
-对于多个自定义请求,将条件附加为
导入:
代替startActivityForresult(Intent,123),使用
,在返回源活动时,代码将保持与 -
的相同
快乐编码! :)
There are 4 simple steps to follow while replacing the deprecated method
startActivityForResult(...)
.In place of overridden method
onActivityResult(..)
-For multiple custom requests, append the condition as
Imports :
In place of startActivityForResult(intent, 123), use
In SampleActivity.java class, while returning back to source activity, code will remain the same like -
Happy Coding! :)
在Java 8中,可以写下以下内容:
In Java 8 it can be written alike this:
在Kotlin中
代码更改
为
我将
,
希望它对您有用。 :d
In KOTLIN
I changed my code
and
to
and
I hope it works for you. :D
对于那些片段具有多个
requestCode
的人,如果您不确定如何处理这些request> requestCode
s的多个结果,则需要了解request> request> requestcode < /代码>在新方法中是没有用的。
我想像您这样的旧方式进行编码:
在新的API中,您需要在单独的
Active> ActiveResultContract
中实现每个请求的结果:启动这样的活动/意图:
然后,您需要 这,您可以摆脱项目中数百个
const val请求_
值。For those whose fragments have more than one
requestCode
, and if you are not sure how to handle multiple results by thoserequestCode
s, you need to understand thatrequestCode
is useless in the new approach.I imagine the old way you code like this:
In the new API, you need to implement the result of every requests in a separate
ActivityResultContract
:Then you need to start those activities/intents like this:
By doing this, you can get rid of hundreds of
const val REQUEST_
values in your project.onActivityResult
,startActivityForresult
,requestpermissions
和onrequestpermissionsResult
均为 reteprecated 在androidx.fragment.fragment
from1.3.0-alpha04
>,而不是android.app.Activity
。相反,您可以使用
活动结果APIS
a href =“ https://developer.android.com/reference/androidx/activitive/result/activitivitive resuttiveResultcaller#registerforactivityRactivityResultractivityRactivity( ActivityResultCallback%3CO%3E)“ rel =“ noreferrer”>registerForactivityResult
。onActivityResult
,startActivityForResult
,requestPermissions
, andonRequestPermissionsResult
are deprecated onandroidx.fragment
from1.3.0-alpha04
, not onandroid.app.Activity
.Instead, you can use
Activity Result APIs
withregisterForActivityResult
.参考: kotlin-从画廊中选择图像
我发现的最简单的陈词滥调迄今为止
Reference : Kotlin - Choose Image from gallery
The Simplest Alernative I've found so far
在这里我解释了新的方式
从活动或碎片中称呼此方法
Here i explain the new way
call this from activity or fragment
以下代码在Kotlin片段中起作用,以检查蓝牙权限。
The below code works in the Kotlin fragment to check the Bluetooth permission.
这是我的解决方案:
在我们的项目中,我们发生了20多次起始攻击(和OnActivityResult)。
我们想尽可能少地更改代码(并继续使用请求代码),同时引入一个优雅的解决方案供将来使用。
自从我们很多人,开发人员,使用基础性概念 - 为什么不利用它呢?
这是碱性:
这是亚活动:
希望它能帮助某人
Here's my solution:
In our project, we had 20+ occurrences of startActivityForResult (and onActivityResult).
We wanted to change the code as little as possible (and keep using request codes), while introducing an elegant solution for future use.
Since lots of us, developers, use BaseActivity concept - why not take advantage of it?
Here is BaseActivity:
Here is SubActivity:
Hope it helps someone
我想出了如何从Kotlin中的片段正确地进行操作,以捕获图像并处理返回的位图。在其他情况下也几乎相同。
首先,您必须注册片段才能聆听活动结果。这必须在启动片段之前完成,这意味着创建成员变量,而不是在OnCreate函数中启动。
然后,按照通常的方式调用摄像机意图。并使用此上面创建的变量来启动意图。
I figured how to do it properly from a Fragment in Kotlin, to capture an image and handle returned bitmap. It is pretty much the same in other cases too.
First, you have to register the fragment to listen for the activity results. This has to be done before initiating the fragment, which means creating a member variable instead of initiating within onCreate function.
Then, call the camera intent as you would normally do. And use this above-created variable to launch the intent.
我的目标是重复使用具有最小代码更改的
StartActivityForresult
方法的当前实现。为此,我用onActivityResultFromlauncher方法进行了包装类和接口。我在项目中使用了匕首,并向包装器注入了需要的包装器
,但也可以直接对包装器进行实例化:
然后,您必须使用
lastion> lastion> lashingIntentForresult
更改startActivityForresult
方法。这是从片段中调用的示例:您将在匿名对象中收到结果。
如果您实现接口并重构当前实现,则可以在片段或片段中使用
onActivityResultListener
:众所周知,Kotlin类ActivityResultLauncherWrapper也可以在Java代码中使用。我的项目中也有Java课程。在片段中实现了回调界面的示例:
我希望这有助于为您的情况构建解决方案。
My goal was to reuse the current implementation of the
startActivityForResult
method with minimum code changes. For that purpose, I made a wrapper class and interface with an onActivityResultFromLauncher method.I am using Dagger in my project and I injected the wrapper where it is needed
But the wrapper also can be instantiated directly:
then you have to change the
startActivityForResult
method withlaunchIntentForResult
. Here is example where it is called from a fragment:You will receive the result in the anonymous object.
You could use
OnActivityResultListener
in a Fragment or an FragmentActivity if you implement the Interface and refactor the current implementation like this:As we know, the Kotlin class ActivityResultLauncherWrapper could be used in java code as well. There are java classes in my project as well. There is an example with implementation of the callback interface in a Fragment:
I hope this helps to build the solution for your case.
您可以为Koltin使用扩展功能。例如:
然后在你的碎片中
You can use extension functions for Koltin. For example:
And then in your fragment something like this
这就是我替换多个请求码的方式(将此代码放入您的活动中):
我启动这样的活动:
This was what how I replaced multiple requestCodes (put this code in your Activity):
I launch the activities like this:
共享我找到的解决方案
首先,使用
register foractivityResult
注册此活动这将返回类型
activityResultLauncher的对象!&gt;
像这样,
现在我们想在哪里启动活动以进行结果,我们可以使用
getResult.launch(Intent)
Sharing solution that I've found
First, register this activity for result using
registerForActivityResult
This will return an object of type
ActivityResultLauncher<Intent!>
Like this,
Now where ever we want to launch activity for result we can use
getResult.launch(intent)
就我而言,我试图使用我直接移动到下一个活动的意图而无需使用Google登录。
对我有用的是:
increate内部设置登录按钮的onclicklistener:
在上面的代码中,我写的是要转到下一个活动的意图,但我不得不编写
client.signinintent.signinintent。
In my case I was trying to use the intent I was moving directly to the next activity without using the Google Sign In.
What worked for me :
Inside OnCreate set the onClickListener for the sign-in button :
In the above code I was writing the intent to go to the next activity but I had to write
client.signInIntent
结合上述答案,我有一种与旧方法 startActivityForresult()继续使用 requestCode 而无需更改旧代码结构的方法:
activitylauncher.class strong>
}
baseeactivity.java 中的代码
,最后在每个活动中扩展了 baseeactivity ,实现 Activitylauncher.onactivity.onactivityResult and strong>和更改“ on ActivityResult”覆盖函数的名称为“ OnActivityResultCallback”。还可以删除 super.onactivityResult()
如何使用:startActivityForresult(Intent,requestCode,this)
Combine with the above answer, I have a approach that compatible with the old way startActivityForResult() keep using requestCode without changing old code structure:
ActivityLauncher.class
}
Code in BaseActivity.java
And finally in each Activity that extends BaseActivity, implements ActivityLauncher.OnActivityResult and change the name of override function "onActivityResult" to "onActivityResultCallback". Also rember to remove super.onActivityResult()
How to use: startActivityForResult(intent, requestCode, this)
似乎
OnActivityResult
在超级类中弃用,但您没有在您的问题中提及超级类名称和compilesdkversion
。在Java和Kotlin中,只需通过将
@deprecated
添加到它,就可以将每个类或方法标记为弃用,以便检查您的超级类,您可以扩展错误的类。当一个班级弃用时,其所有方法也被弃用。
要查看快速解决方案,请单击“不建议的方法”,然后按Android Studio中的
Ctrl+Q
查看方法文档,应该有一个解决方案。在我的项目中,使用
androidx
和API 29作为compilesdkversion
,此方法在活动和片段中不弃用It seems that
onActivityResult
is deprecated in the super class but you did not mention the super class name andcompileSdkVersion
here in your question.In Java and Kotlin every class or method could be marked as deprecated simply by adding
@Deprecated
to it so check your super class you may extend a wrong class.When a class is deprecated all of its methods are deprecated too.
To see a quick solution click on deprecated method and press
Ctrl+Q
in Android studio to see documentation of method there should be a solution for it.In my project using
androidx
and API 29 ascompileSdkVersion
, this method is NOT deprecated in activities and fragmentsKotlin version of @Muntashir Akon solution
做到这一点的另一种方法是3个步骤。 (考虑到您具有startActivityForresult(0和OnActivitivityResult())
var resultlauncher中创建一个变量:activityResultLauncher&lt; intent&gt;
startActivityForresult()
,然后将其替换为resultlauncher.launch(intent)
An alternate way to do this is in 3 steps. (Considering you have a startActivityForResult(0 and onActivityResult())
var resultLauncher:ActivityResultLauncher<Intent>
startActivityForResult()
and replace it with the lineresultLauncher.launch(intent)
dor506 答案对我有用,因为我在大多数项目中都使用baseactivity,因此我更容易更轻松地更改代码。文件而不是我所有的活动。我写了此代码的Java版本。
基础性代码:
现在在任何活动中使用此代码:
dor506 answer worked for me as i use BaseActivity in most of my projects so it is easier for me to change the code in single file rather than all my activites. I have written the java version of this code.
BaseActivity code :
Now in any activity use this code like this:
获得结果,
startActivityForresult和OnActivityResult在Android 10 API 30中被弃用,现在我们有一种新的方法来使用registerforactivityResult并启动活动来
但是您应该在您想要的任何地方拨打启动和启动之前注册。
否则,您将获得此例外
startActivityForResult and onActivityResult is deprecated in android 10 API 30 now we have a new way to get the result using registerForActivityResult
and to launch activity
but you should register before you call launch And launch wherever you want.
otherwise, you will get this exception
如果您这样的基本活动,则可以以旧方式使用StartActivityForresult来继续进行。
唯一的限制是,您将必须使用SetResult(结果,意图)将结果设置在您的活动中。
关键是让结果将请求代码带回结果消费者。
If you implement your base Activity like this, you may continure using startActivityForResult in old fashion.
The only limitation is you will have to use setResult(result, intent) to set the result within your activity.
The key is to let the result carry the request code back to the result consumer.
startActivityForresult&amp; amp; amp; amp;的简单示例来自活动和碎片的requestMultiplePermissions [in Kotlin]
请求活动以进行活动
检查
请求活动许可?
从碎片?
使用相同的方法,但请确保将这些实现列入
初始化,onattach()或onCreate()
Simple Example of registerForActivityResult for both StartActivityForResult & RequestMultiplePermissions from Activity and Fragment [in Kotlin]
Requesting activity for result from Activity
Check out ActivityResult
Requesting for permissions from Activity?
From Fragment?
Use same methods but make sure you put these implementations in
initialization, onAttach(), or onCreate()
如果您使用SMS同意API,则使用以下代码(Kotlin):
In case you are using SMS consent API then use the following code (Kotlin):
我正在使用Kotlin扩展名使其非常简单。在 ExtenStions.kt 文件中添加下面的Extensiton fucntion:
现在,在任何继承AppCompatactivity的活动中,您可以使用以下简单代码:
Update
以上实施可能会导致例外:
java.lang.illegalstateException:LifeCycleowner XXXX在恢复当前状态时尝试注册。生命周期者必须在启动之前调用登记册。
,因此应在发行之前提前调用registerforactivityResult。这是替代解决方案。
在 Extenstions.kt 文件中添加下面的Extensiton fucntion:
现在,在任何继承AppCompatactivity的活动内部,您可以在下面使用以下简单代码:
I am using kotlin extension to make it very simple. Add below extensiton fucntion in your Extenstions.kt file:
Now, inside any activity that inherits AppCompatActivity, you can use below simple code:
Update
Above implementaion may cause below exception:
java.lang.IllegalStateException: LifecycleOwner xxxx is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
So registerForActivityResult should be called in advance for example before onCreate. Here is the alternative solution.
Add below extensiton fucntion in your Extenstions.kt file:
Now, inside any activity that inherits AppCompatActivity, you can use below simple code:
添加 muntashir akon 和 abhijeet ,您可以通过在意图中传递值来修改新格式以像旧格式一样工作,例如:
这将允许您仅使用一条额外的行来将类称为相同添加您的原始称为结果代码。还允许您创建一个可重复使用的启动器实例。
Adding on to the answers by muntashir akon and abhijeet, you can modify the new format to work like the old format by passing values in the intent, for example:
This will allow you to keep the class called the same with just the one extra line to add your original called result code. Also allows you to create a reusable launcher instance.
2023 kotlin答案以获取
uri
。为那些正在寻找一种从从图像库中获取图像并处理
URI
的人的示例(请求代码,结果代码,数据) 划掉/弃用我从 google文档 并且有效。
仅供参考,您应该能够使用它来获取任何
uri
,而不仅仅是用于图像。在这里使用下面。2023 Kotlin Answer to get an
Uri
.An example for those who are searching for a way to fetch an image from the image library and handle the
Uri
becausesuper.onActivityResult(requestCode, resultCode, data)
is crossed out/deprecatedI got this answer from google docs and it works.
FYI you should be able to use this to get any
Uri
, not just for images. Here it is used below.