我最近发现onActivityResult已弃用。我们该怎么处理呢?
有什么替代方案吗?
我最近发现onActivityResult已弃用。我们该怎么处理呢?
有什么替代方案吗?
当前回答
添加到muntashir akon和abhijeet的答案,你可以通过在意图中传递值来修改新格式,使其像旧格式一样工作,例如:
// 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)
....
这将允许您保持类的调用相同,只需要额外的一行来添加原始调用的结果代码。还允许您创建一个可重用的启动程序实例。
其他回答
Kotlin版本的@Muntashir阿肯解决方案
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())
}
}
}
我的目标是用最少的代码更改重用startActivityForResult方法的当前实现。为此,我使用onActivityResultFromLauncher方法创建了一个包装器类和接口。
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()
}
}
我在我的项目中使用匕首,我在需要的地方注入了包装器
@Inject
lateinit var activityResultLauncher: ActivityResultLauncherWrapper
但是包装器也可以直接实例化:
val activityResultLauncher = ActivityResultLauncherWrapper()
然后你必须用launchIntentForResult改变startActivityForResult方法。下面是一个从片段中调用它的例子:
activityResultLauncher.launchIntentForResult(
requireActivity(),
intent,
REQUEST_CODE_CONSTANT,
object: ActivityResultLauncherWrapper.OnActivityResultListener {
override fun onActivityResultFromLauncher(requestCode: Int, resultCode: Int, data: Intent?) {
/*do something*/
}
}
)
您将在匿名对象中收到结果。 你可以在Fragment或FragmentActivity中使用OnActivityResultListener,如果你实现了接口,并像这样重构当前的实现:
class MyFragment : Fragment(), OnActivityResultListener {
...
override fun onActivityResultFromLauncher(requestCode: Int, resultCode: Int, data: Intent?) {/*do somthing*/}
...
}
正如我们所知,Kotlin类ActivityResultLauncherWrapper也可以在java代码中使用。在我的项目中也有java类。这里有一个在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*/}
...
}
我希望这有助于为您的案例构建解决方案。
在替换已弃用的方法startActivityForResult(…)时,需要遵循4个简单的步骤。
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... } } });
对于多个自定义请求,将条件附加为
if (result.getResultCode() == 123) {
..
} else if(result.getResultCode() == 131){
..
} // so on..
Imports : import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; In place of startActivityForResult(intent, 123), use Intent intent = new Intent(this, SampleActivity.class); activityResultLaunch.launch(intent); In SampleActivity.java class, while returning back to source activity, code will remain the same like - Intent intent = new Intent(); setResult(123, intent); finish();
编码快乐!:)
我使用kotlin扩展,使它非常简单。在你的扩展中添加以下扩展功能。kt文件:
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...
}
}
}
更新 上述实现可能导致以下异常: java.lang.IllegalStateException: LifecycleOwner xxxx正在尝试注册,而当前状态为恢复。生命周期所有者必须在启动之前调用寄存器。
因此,registerForActivityResult应该提前调用,例如在onCreate之前。这是另一种解决方案。
在你的扩展中添加以下扩展功能。kt文件:
fun AppCompatActivity.registerForResult(onResult: (resultCode: Int, data: Intent?) -> Unit):
ActivityResultLauncher<Intent> {
return this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
onResult(result.resultCode, result.data)
}
}
现在,在继承AppCompatActivity的任何活动中,你可以使用下面的简单代码:
为每个需要结果的操作定义一个类成员变量
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)
您可以为Koltin使用扩展函数。例如:
//random utils file
fun Fragment.buildGetContentRequest(function: (Uri) -> Unit): ActivityResultLauncher<String> {
return this.registerForActivityResult(ActivityResultContracts.GetContent()) {
function(it)
}
}
fun Fragment.buildTakePhotoRequest(function: (Boolean) -> Unit): ActivityResultLauncher<Uri> {
return this.registerForActivityResult(ActivityResultContracts.TakePicture()) {
function(it)
}
}
fun Fragment.buildSelectMultipleContentRequest(function: (MutableList<Uri>?) -> Unit): ActivityResultLauncher<String> {
return this.registerForActivityResult(ActivityResultContracts.GetMultipleContents()) {
function(it)
}
}
然后在你的片段中像这样
//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
}
}