Android Kotlin:无法使用 registerforactivityresult

问题描述 投票:0回答:1

迁移下一个 Java 类后:

public class ImportExportActivity extends BaseActivity
{
    private String filename;
    private static Intent oData;
    private ActivityResultLauncher<Intent> activityResultLauncher;
    private ActivityImportexportBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setImportDataActivityResultHandler();
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    private void setImportDataActivityResultHandler(){
        activityResultLauncher =
                registerForActivityResult(
                        new ActivityResultContracts.StartActivityForResult(),
                        result -> {
                            if (result.getResultCode() == Activity.RESULT_OK) {
                                Intent data = result.getData();
                                if (data != null) {
                                    oData = data;
                                    boolean hasPermission =
                                            TMPermission.verifyStorageReadPermissions((Activity)AppSettings.getContext());
                                    appSettings.setRequestPermissionSource(RequestPermissionSource.IMPORTDATA);
                                    if (hasPermission) {
                                        String strResult = TMImport.importData(data);
                                        TMToast toast = new TMToast(AppSettings.getContext());
                                        toast.show(strResult, Toast.LENGTH_LONG);
                                    }
                                }
                            }
                        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(appSettings.getRequestPermissionSource()
                .equals(RequestPermissionSource.IMPORTDATA)){
            if (TMPermission.hasReadPermission()) {
                String strResult = TMImport.importData(oData);
                TMToast toast = new TMToast(this);
                toast.show(strResult, Toast.LENGTH_LONG);
            }
        }
    }

    public void setActivityLayout()
    {
        super.setActivityLayout();
        setButtonsAppearance();
    }

    public void setContentView()
    {
        binding = DataBindingUtil.setContentView(this, R.layout.activity_importexport);
    }

    public void setActivityBackground(){
        binding.llImportExportMain.setBackgroundColor(Color.parseColor(
                appSettings.mainLayoutBackgroundColor
        ));
    }

    public void createActionBar()
    {
        String activityTitle = TMLocale.getStringResourceByName("activityrecords_textview_importexport").toUpperCase();
        TMAppearance.createActionBar(this, activityTitle);
    }

    public void setFooterMenu()
    {
        FootermenuBinding footerBinding = binding.footermenu;
        TMFooter.footerBinding = new WeakReference<>(footerBinding);
        TMFooter.activity = new WeakReference<>(this);
        TMFooter.dialog = new WeakReference<>(dialog);
        TMFooter.setFooterMenu();
    }

    private void setButtonsAppearance()
    {
        configureButton(binding.btnImport);
        configureButton(binding.btnExport);

        setButtonClickListener(binding.cvImport, Enum.Action.IMPORT);
        setButtonClickListener(binding.cvExport, Enum.Action.EXPORT);
    }

    @SuppressLint("ClickableViewAccessibility")
    private void setButtonClickListener(CardView button, String target)
    {
        button.setOnTouchListener((v, event) -> {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                TMSound.playButtonSound(this);
                setButtonStyle_Pressed(button);
            } else {
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    setButtonStyle_Released(button);
                    switch (target) {
                        case (Enum.Action.IMPORT):
                            selectFile();
                            break;
                        case (Enum.Action.EXPORT):
                            onTouchExport();
                            break;
                    }
                }
            }
            return true;
        });
    }

    private void onTouchExport()
    {
        //check if there is data to export
        TMSession us = new TMSession();
        User user = us.getUserSession();
        List<RecordDTO> records = UserService.getRecords(user.getId(), "");

        if (records.size()>0){
            exportData();
        }else{
            String strError = TMLocale.getStringResourceByName("activityimportexport_nodatatoexport");
            TMToast toast = new TMToast(this);
            toast.show(strError, Toast.LENGTH_SHORT);
        }
    }

    private void configureButton(TextView button)
    {
        int dpButtonHeight = TMActivity.getButtonsHeight(3); // TMScreen.dp2px(height);
        button.setHeight(dpButtonHeight);
        button.setTextSize(TMAppearance.getPrefsByScreenDensity()[3]);
    }
    
    /**
     * Imports records data from file into records table
     */
    private void selectFile()
    {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("*/*");
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        activityResultLauncher.launch(intent);
    }

    private void exportData()
    {
        String recordsData = TMExport.getRecordsData();
        try {
            filename = getFilename();
            File filelocation = TMFile.writeFile(this, filename, recordsData);
            sendExportFileTo(filelocation);
        } catch (IOException e) {
            TMToast toast = new TMToast(this);
            toast.show(e.getMessage(), Toast.LENGTH_SHORT);
            ExceptionHandler.logException(e);
        }
    }

    private String getFilename()
    {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.ENGLISH);
        Date now = new Date();
        return "testme_exportfile_" + formatter.format(now) + ".txt";
    }

    private void sendExportFileTo(File attachment)
    {
        Uri uri = FileProvider.getUriForFile(this, this.getApplicationContext().getPackageName() + ".provider", attachment);
        Intent i = new Intent(Intent.ACTION_SEND);
        i.setType("text/plain");
        i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        i.putExtra(Intent.EXTRA_EMAIL, new String[] { "" });

        String subject = TMLocale.getStringResourceByName("importexport_exporttomail_subject") + " " + filename;
        String body = TMLocale.getStringResourceByName("importexport_exporttomail_body");

        i.putExtra(Intent.EXTRA_SUBJECT, subject);
        i.putExtra(Intent.EXTRA_TEXT, body);
        i.putExtra(Intent.EXTRA_STREAM, uri);

        try {
            this.startActivity(Intent.createChooser(i, "Send mail..."));
        } catch (android.content.ActivityNotFoundException e) {
            Toast.makeText(this,
                    TMLocale.getStringResourceByName("importexport_exporttomail_noemailclients"), Toast.LENGTH_SHORT)
                    .show();
            ExceptionHandler.logException(e);
        }
    }
}

对于 Kotlin,我以 结束。下一堂课:

class ImportExportActivity : BaseActivity() {
    private var filename: String? = null
    private var activityResultLauncher: ActivityResultLauncher<Intent>? = null
    private var binding: ActivityImportexportBinding? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setImportDataActivityResultHandler()
    }

    private fun setImportDataActivityResultHandler() {
        activityResultLauncher = registerForActivityResult<Intent, ActivityResult>(
            StartActivityForResult(),
            ActivityResultCallback { result: ActivityResult ->
                if (result.resultCode == Activity.RESULT_OK) {
                    val data = result.data
                    if (data != null) {
                        oData = data
                        val hasPermission =
                            TMPermission.verifyStorageReadPermissions(AppSettings.getContext() as Activity)
                        appSettings.requestPermissionSource = RequestPermissionSource.IMPORTDATA
                        if (hasPermission) {
                            val strResult = TMImport.importData(data)
                            val toast = TMToast(AppSettings.getContext())
                            toast.show(strResult, Toast.LENGTH_LONG)
                        }
                    }
                }
            })
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (appSettings.requestPermissionSource
            == RequestPermissionSource.IMPORTDATA
        ) {
            if (TMPermission.hasReadPermission()) {
                val strResult = TMImport.importData(oData)
                val toast = TMToast(this)
                toast.show(strResult, Toast.LENGTH_LONG)
            }
        }
    }

    override fun setActivityLayout() {
        super.setActivityLayout()
        setButtonsAppearance()
    }

    override fun setContentView() {
        binding = DataBindingUtil.setContentView(this, R.layout.activity_importexport)
    }

    override fun setActivityBackground() {
        binding!!.llImportExportMain.setBackgroundColor(
            Color.parseColor(
                appSettings.mainLayoutBackgroundColor
            )
        )
    }

    override fun createActionBar() {
        val activityTitle =
            TMLocale.getStringResourceByName("activityrecords_textview_importexport").uppercase(
                Locale.getDefault()
            )
        createActionBar(this, activityTitle)
    }

    override fun setFooterMenu() {
        val footerBinding = binding!!.footermenu
        TMFooter.footerBinding = WeakReference(footerBinding)
        TMFooter.activity = WeakReference(this)
        TMFooter.dialog = WeakReference(dialog)
        TMFooter.setFooterMenu()
    }

    private fun setButtonsAppearance() {
        configureButton(binding!!.btnImport)
        configureButton(binding!!.btnExport)
        setButtonClickListener(binding!!.cvImport, Enum.Action.IMPORT)
        setButtonClickListener(binding!!.cvExport, Enum.Action.EXPORT)
    }

    @SuppressLint("ClickableViewAccessibility")
    private fun setButtonClickListener(button: CardView, target: String) {
        button.setOnTouchListener { v: View?, event: MotionEvent ->
            if (event.action == MotionEvent.ACTION_DOWN) {
                TMSound.playButtonSound(this)
                setButtonStyle_Pressed(button)
            } else {
                if (event.action == MotionEvent.ACTION_UP) {
                    setButtonStyle_Released(button)
                    when (target) {
                        Enum.Action.IMPORT -> selectFile()
                        Enum.Action.EXPORT -> onTouchExport()
                    }
                }
            }
            true
        }
    }

    private fun onTouchExport() {
        //check if there is data to export
        val us = TMSession()
        val user = us.userSession
        val records = UserService.getRecords(user.id, "")
        if (records.size > 0) {
            exportData()
        } else {
            val strError = TMLocale.getStringResourceByName("activityimportexport_nodatatoexport")
            val toast = TMToast(this)
            toast.show(strError, Toast.LENGTH_SHORT)
        }
    }

    private fun configureButton(button: TextView) {
        val dpButtonHeight = getButtonsHeight(3) // TMScreen.dp2px(height);
        button.height = dpButtonHeight
        button.textSize = prefsByScreenDensity[3].toFloat()
    }

    /**
     * Imports records data from file into records table
     */
    private fun selectFile() {
        val intent = Intent(Intent.ACTION_GET_CONTENT)
        intent.type = "*/*"
        intent.addCategory(Intent.CATEGORY_OPENABLE)
        activityResultLauncher!!.launch(intent)
    }

    private fun exportData() {
        val recordsData = TMExport.getRecordsData()
        try {
            filename = getFilename()
            val filelocation = TMFile.writeFile(this, filename, recordsData)
            sendExportFileTo(filelocation)
        } catch (e: IOException) {
            val toast = TMToast(this)
            toast.show(e.message, Toast.LENGTH_SHORT)
            ExceptionHandler.logException(e)
        }
    }

    private fun getFilename(): String {
        val formatter = SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.ENGLISH)
        val now = Date()
        return "testme_exportfile_" + formatter.format(now) + ".txt"
    }

    private fun sendExportFileTo(attachment: File) {
        val uri = FileProvider.getUriForFile(
            this,
            this.applicationContext.packageName + ".provider",
            attachment
        )
        val i = Intent(Intent.ACTION_SEND)
        i.type = "text/plain"
        i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        i.putExtra(Intent.EXTRA_EMAIL, arrayOf(""))
        val subject =
            TMLocale.getStringResourceByName("importexport_exporttomail_subject") + " " + filename
        val body = TMLocale.getStringResourceByName("importexport_exporttomail_body")
        i.putExtra(Intent.EXTRA_SUBJECT, subject)
        i.putExtra(Intent.EXTRA_TEXT, body)
        i.putExtra(Intent.EXTRA_STREAM, uri)
        try {
            this.startActivity(Intent.createChooser(i, "Send mail..."))
        } catch (e: ActivityNotFoundException) {
            Toast.makeText(
                this,
                TMLocale.getStringResourceByName("importexport_exporttomail_noemailclients"),
                Toast.LENGTH_SHORT
            )
                .show()
            ExceptionHandler.logException(e)
        }
    }

    companion object {
        private var oData: Intent? = null
    }
}

并且“registerForActivityResult”不再被编译器识别。

正如我在这里读到的:Android ActivityResult API 未解决的参考错误 registerForActivityResult

解决方案通过在 build.gradle 中导入接下来的两个包:

api 'androidx.activity:activity-ktx:1.6.0-alpha05'
api 'androidx.fragment:fragment-ktx:1.5.0-rc01'

然后让你的 Activity 扩展 ComponentActivity。

好吧,我尝试了这个解决方案并且它有效,现在可以识别“registerforactivityresult”,但是我无法从

ComponentActivity
继承,因为我所有的活动都扩展了
BaseActivity
,并且
BaseActivity
扩展了
AppCompatActivity
,所以我无法扩展
ComponentActivity 
在 BaseActivity 中也可以。

这么说,鉴于我的场景,我陷入了困境,不知道如何在 Kotlin 中使用

registerforactivityresult

编辑1:

这是我的 build.gradle 支持部分:

api 'androidx.legacy:legacy-support-v4:1.0.0'
api 'androidx.appcompat:appcompat:1.4.2'
api 'androidx.cardview:cardview:1.0.0'
api 'androidx.recyclerview:recyclerview:1.2.1'
api 'androidx.preference:preference-ktx:1.2.0'

编辑2:

不确定这是否是最终的解决方案,但在

AppCompatActivity
类中用
ComponentActivity
替换
BaseActivity
解决了编译问题。

编辑3:

AppCompatActivity
改变
ComponentActivity
最终更多的是一种幻想而不是解决方案。如果我扩展
ComponentActivity
,registerforactivityresult 开始工作,但 FragmentActivities 停止编译,因为要使用SectionsPagerAdapter,我的类需要扩展
FragmentActivity
。这就像一个恶性循环。

编辑4:

我完整的build.gradle:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
    compileSdkVersion 32
    def code
    Properties versionProps = new Properties()
    def versionPropsFile = file('version.properties')
    if (versionPropsFile.exists())
        versionProps.load(new FileInputStream(versionPropsFile))
    code = (versionProps['VERSION_CODE'] ?: "0").toInteger()+ 1
    packagingOptions {
        resources {
            pickFirsts += ['META-INF/LICENSE.txt']
            excludes += ['META-INF/NOTICE.md', 'META-INF/LICENSE.md', 'META-INF/INDEX.LIST', 'META-INF/DEPENDENCIES', 'META-INF/io.netty.versions.properties']
        }
    }
    versionProps['VERSION_CODE'] = code.toString()
    versionProps.store(versionPropsFile.newWriter(), null)
    defaultConfig {
        applicationId 'com.xxx.xxx'
        minSdkVersion 26
        targetSdkVersion 32
        multiDexEnabled true
        versionCode code
        versionName "3.0." + code
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    }
    bundle {
        density {
            // Different APKs are generated for devices with different screen densities; true by default.
            enableSplit true
        }
        abi {
            // Different APKs are generated for devices with different CPU architectures; true by default.
            enableSplit true
        }
        language {
            // This is disabled so that the App Bundle does NOT split the APK for each language.
            // We're gonna use the same APK for all languages.
            enableSplit false
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            applicationVariants.all { variant ->
                variant.outputs.all { output ->
                    project.ext { appName = 'xxx' }
                    def newName = 'xxx.apk'
                    outputFileName = new File("./build/", newName)
                }
            }
        }
        debug {
        }
    }
    productFlavors {
    }
    dataBinding{
        enabled = true
    }
    lint {
        abortOnError false
        checkReleaseBuilds false
    }
    namespace 'com.xxx.xxx'
}

dependencies {
    api files('libs/achartengine-1.2.0.jar')
    api 'com.facebook.android:facebook-android-sdk:14.0.0'
    //mail API 16
    implementation 'com.sun.mail:android-mail:1.6.7'
    implementation 'com.sun.mail:android-activation:1.6.7'
    //apache commons text
    implementation group: 'org.apache.commons', name: 'commons-text', version: '1.9'
    //Youtube player
    api 'com.thefinestartist:ytpa:1.2.1'
    //Font Selector List Preference
    //api 'com.vanniktech:vntfontlistpreference:1.0.0'
    //Rate my app
    api 'com.github.hotchemi:android-rate:1.0.1'
    //Support
    api 'androidx.legacy:legacy-support-v4:1.0.0'
    api 'androidx.appcompat:appcompat:1.4.2'
    api 'androidx.cardview:cardview:1.0.0'
    api 'androidx.recyclerview:recyclerview:1.2.1'
    api 'androidx.preference:preference-ktx:1.2.0'
    //Annotation
    api 'androidx.annotation:annotation:1.4.0'
    //AlertDialog
    api 'com.github.d-max:spots-dialog:1.1@aar'
    //glide animated gifs
    api 'com.github.bumptech.glide:glide:4.13.2'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.13.2'
    //circular score
    api 'com.wssholmes.stark:circular-score:0.1.1'
    api 'com.google.android.material:material:1.6.1'
    api 'com.github.mejdi14:Flat-Dialog-Android:1.0.5'
    //picasso
    api 'com.squareup.picasso:picasso:2.71828'
    //Gson
    implementation 'com.google.code.gson:gson:2.9.0'
    //Stream
    api 'com.annimon:stream:1.2.2'
    api 'androidx.activity:activity-ktx:1.6.0-alpha05'
    api 'androidx.activity:activity-ktx:1.4.0'
    api 'androidx.fragment:fragment-ktx:1.5.0-rc01'
    /*implementation 'jp.wasabeef:picasso-transformations:2.4.0'*/
    //zoom
    //implementation 'com.alexvasilkov:gesture-views:2.8.2'
    //implementation 'com.otaliastudios:zoomlayout:1.8.0'
    //Multidex
    implementation "androidx.multidex:multidex:2.0.1"
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation project(':Global')
    implementation project(':DTO')
    implementation project(':Common')
    implementation project(':Background')
    implementation project(':Services')
}

奇怪的是,即使出现 lint 错误,应用程序也可以正常构建,但由于我正在迁移过程中,还无法测试 registerForActivityResult,但我想它很可能无法工作。

编辑5:

在下一个 Google 页面中: https://developer.android.com/jetpack/androidx/releases/activity

说:

"The registerForActivityResult() API has been renamed to rememberLauncherForActivityResult()..."

但同样的情况也发生在rememberLauncherForActivityResult上,它没有找到。我完全被困住了。

编辑6:

据我所读,也许这只是一个“视觉”问题,比如说 Android Studio IDE 问题,因为应用程序构建得很好(不过,我还没有测试过 registerForActivityResult),但无论如何我厌倦了谷歌。

随着时间的推移,软件包每次都会更快地被标记为过时,而令人惊奇的是“替代”并不总是有效

目前,我将保留我的“registerForActivityResult”问题(因为我别无选择),但老实说,在我看来,Google 不鼓励我们开发者保持我们的应用程序最新。

android kotlin android-activity registerforactivityresult
1个回答
3
投票

我最终为此函数创建了一个扩展:

fun <I, O> Activity.registerForActivityResult(
    contract: ActivityResultContract<I, O>,
    callback: ActivityResultCallback<O>
) = (this as ComponentActivity).registerForActivityResult(contract, callback)

AndroidStudio 似乎存在一个未解决的错误: 错误:尽管具有所需的依赖项,但无法在大型项目上找到 registerForActivityResult

© www.soinside.com 2019 - 2024. All rights reserved.