Kotlin 6. Kotlin Spinner 在 Activity 以及 Fragment 中的使用

厉令
2023-12-01

一起来学Kotlin:概念:6. Kotlin Spinner 在 Activity 以及 Fragment 中的使用

Kotlin Spinner 在 Activity 以及 Fragment 中的使用。



1. Spinner 在 Activity 中的使用

Spinner 在 Activity 中的案例非常普遍,这里直接列举一个例子,我们来看一下 Spinner 在 OCR (com.google.mlkit:text-recognition)中的应用。

1.1 准备

我们在android studio中新建一个项目,如下为我的AndroidManifest.xml, build_gradle (Project)以及build_gradle (app)的代码。注意,这个例子SDK Version 是32。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.zjgwarehouseocr">

    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.ZJGWarehouseOCR"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="com.google.mlkit.vision.DEPENDENCIES"
            android:value="ocr"/>

    </application>

</manifest>

build_gradle (Project)

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id 'com.android.application' version '7.2.1' apply false
    id 'com.android.library' version '7.2.1' apply false
    id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

build_gradle (:app)

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.example.zjgwarehouseocr"
        minSdk 28
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.navigation:navigation-fragment:2.5.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0'
    implementation 'androidx.navigation:navigation-ui-ktx:2.5.0'

    //MLKit
    implementation 'com.google.mlkit:text-recognition:16.0.0-beta4'
    implementation 'com.google.mlkit:text-recognition-chinese:16.0.0-beta4'
    implementation 'com.google.mlkit:text-recognition-devanagari:16.0.0-beta4'
    implementation 'com.google.mlkit:text-recognition-japanese:16.0.0-beta4'
    implementation 'com.google.mlkit:text-recognition-korean:16.0.0-beta4'
}

1.2 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:cropToPadding="true"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/test2" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="Recognized Text"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="0dp"
        android:layout_height="200dp"
        android:layout_marginTop="32dp"
        android:textAlignment="center"
        android:scrollbars="vertical"
        app:layout_constraintBottom_toTopOf="@+id/btn_start"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

    <Button
        android:id="@+id/btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start Recognition"
        app:layout_constraintBottom_toTopOf="@+id/linearLayout"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.835" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyle"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:visibility="gone"
        app:layout_constraintBottom_toTopOf="@+id/btn_start"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="409dp"
        android:layout_height="68dp"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">

        <Spinner
            android:id="@+id/spinnerOcrLanguage"
            android:layout_width="205dp"
            android:layout_height="match_parent"
            android:contentDescription="@string/app_name" />

        <Spinner
            android:id="@+id/spinnerLabel"
            android:layout_width="205dp"
            android:layout_height="match_parent"
            android:layout_marginEnd="16dp"
            android:contentDescription="@string/app_name" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

1.3 MainActivity.kt

package com.example.zjgwarehouseocr

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.method.ScrollingMovementMethod
import android.util.Log
import android.view.View
import android.widget.*
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.text.TextRecognition
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
import com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions
import com.google.mlkit.vision.text.latin.TextRecognizerOptions
import java.util.ArrayList

class MainActivity : AppCompatActivity() {

    private val TAG = "ML_kit_text_recognition"
    private val label1 = R.drawable.test1
    private val label2 = R.drawable.test2

    // View Element:
    private  lateinit var btn: Button
    private lateinit var tvContent:TextView
    private lateinit var pb:ProgressBar
    private lateinit var imageView:ImageView
    private lateinit var spinnerLabel: Spinner
    private lateinit var spinnerOcrLang: Spinner

    // Bitmap:
    private var selectedImage: Bitmap?= null
    // OCR recognizer initialization
    private var recognizer = TextRecognition.getClient(TextRecognizerOptions.Builder().build())

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Initialization
        btn = findViewById(R.id.btn_start)
        tvContent = findViewById(R.id.tv_content)
        pb = findViewById(R.id.progressBar)
        imageView = findViewById(R.id.imageView)
        spinnerLabel = findViewById(R.id.spinnerLabel)
        spinnerOcrLang = findViewById(R.id.spinnerOcrLanguage)
        // 设置滚轮,当显示的文字太长时,支持滚轮拖动。
        tvContent.setMovementMethod(ScrollingMovementMethod())

        // Button - CLickListener
        btn.setOnClickListener {
            startTextRecognition()
            pb.visibility = View.VISIBLE
        }

        imageView.setImageResource(label1)
        selectedImage = BitmapFactory.decodeResource(resources, label1)

        // Spinner
        val content:Array<String> = arrayOf(LABEL1, LABEL2)
        val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, content)
        spinnerLabel.adapter = adapter
        spinnerLabel.onItemSelectedListener = object:AdapterView.OnItemSelectedListener{
            override fun onItemSelected(
                parent: AdapterView<*>?,
                view: View?,
                position: Int,
                id: Long
            ) {
                tvContent.text = ""
                if (position == 0)
                {
                    imageView.setImageResource(label1)
                    selectedImage = BitmapFactory.decodeResource(resources,label1)
                }
                else
                {
                    imageView.setImageResource(label2)
                    selectedImage = BitmapFactory.decodeResource(resources,label2)
                }
            }

            override fun onNothingSelected(parent: AdapterView<*>?) {

            }
        }

        val options: MutableList<String> = ArrayList()
        options.add(TEXT_RECOGNITION_LATIN)
        options.add(TEXT_RECOGNITION_CHINESE)
        options.add(TEXT_RECOGNITION_DEVANAGARI)
        options.add(TEXT_RECOGNITION_JAPANESE)
        options.add(TEXT_RECOGNITION_KOREAN)
        // Creating adapter for spinner
        val dataAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, options)
        spinnerOcrLang.adapter = dataAdapter
        spinnerOcrLang.onItemSelectedListener = object:AdapterView.OnItemSelectedListener{
            override fun onItemSelected(
                parent: AdapterView<*>?,
                view: View?,
                position: Int,
                id: Long
            ) {
                if (position == 0){
                    Log.i(TAG, "Using on-device Text recognition Processor for Latin")
                    recognizer = TextRecognition.getClient(TextRecognizerOptions.Builder().build())
                }
                else if(position == 1){
                    Log.i(TAG, "Using on-device Text recognition Processor for Chinese")
                    recognizer = TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build())
                }
                else if(position == 2){
                    Log.i(TAG, "Using on-device Text recognition Processor for Devanagari")
                    recognizer = TextRecognition.getClient(DevanagariTextRecognizerOptions.Builder().build())
                }
                else if(position == 3){
                    Log.i(TAG, "Using on-device Text recognition Processor for Japanese")
                    recognizer = TextRecognition.getClient(JapaneseTextRecognizerOptions.Builder().build())
                }
                else if(position == 4){
                    Log.i(TAG, "Using on-device Text recognition Processor for Korean")
                    recognizer = TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().build())
                }
            }

            override fun onNothingSelected(parent: AdapterView<*>?) {
                TODO("Not yet implemented")
            }

        }
    }

    private fun startTextRecognition()
    {
        val inputImage = InputImage.fromBitmap(selectedImage!!, 0)
        recognizer.process(inputImage)
            .addOnSuccessListener {
                pb.visibility = View.GONE
                tvContent.text = it.text
                Log.d(TAG, "Successful Recognition!")
            }
            .addOnFailureListener {
                Log.d(TAG, "Fail Recognition!")
            }
    }

    companion object {
        private const val LABEL1 = "label 1"
        private const val LABEL2 = "label 2"
        private const val TEXT_RECOGNITION_LATIN = "Text Recognition Latin"
        private const val TEXT_RECOGNITION_CHINESE = "Text Recognition Chinese"
        private const val TEXT_RECOGNITION_DEVANAGARI = "Text Recognition Devanagari"
        private const val TEXT_RECOGNITION_JAPANESE = "Text Recognition Japanese"
        private const val TEXT_RECOGNITION_KOREAN = "Text Recognition Korean"
    }

}

Kotlin 的官网也给出了例子:

val spinner: Spinner = findViewById(R.id.spinner)
// Create an ArrayAdapter using the string array and a default spinner layout
ArrayAdapter.createFromResource(
        this,
        R.array.planets_array,
        android.R.layout.simple_spinner_item
).also { adapter ->
    // Specify the layout to use when the list of choices appears
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
    // Apply the adapter to the spinner
    spinner.adapter = adapter
}

借助 createFromResource() 方法,您可以从字符串数组创建 ArrayAdapter。此方法的第三个参数是布局资源,其定义了所选选项在微调框控件中的显示方式。simple_spinner_item 布局是平台提供的默认布局,除非您想为微调框外观定义自己的布局,否则应使用此布局。

然后,您应调用 setDropDownViewResource(int),从而指定适配器用于显示微调框选择列表的布局(simple_spinner_dropdown_item 是平台定义的另一种标准布局)。

接着,通过调用 setAdapter() 将适配器应用到 Spinner。

2. Spinner 在 Fragment 中的使用

在 fragment 中,我们无法直接使用 findViewById。需要先通过 onCreateView 函数进行初始化和调用。在 stackoverflow 中,我们找到一个类似的问题:The spinner doesn’t work in my Kotlin fragment。被选为正确的答案如下:

class Add : Fragment()
{

val types = arrayOf("simple User", "Admin")

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val t=inflater.inflate(R.layout.fragment_add, container, false)
        val spinner = t.findViewById<Spinner>(R.id.spinner2)
       spinner?.adapter = ArrayAdapter(activity?.applicationContext, R.layout.support_simple_spinner_dropdown_item, types) as SpinnerAdapter
        spinner?.onItemSelectedListener = object :AdapterView.OnItemSelectedListener{
            override fun onNothingSelected(parent: AdapterView<*>?) {
                println("erreur")
            }

            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
                val type = parent?.getItemAtPosition(position).toString()
                Toast.makeText(activity,type, Toast.LENGTH_LONG).show()
                println(type)
            }

        }
        return t
    }
}

可以借鉴。最终用于我这边的代码被修改如下:

class HomeScanFragment : Fragment(R.layout.fragment_scan_home) {
    private val binding: FragmentScanHomeBinding by viewBinding()
    private val viewModel: HomeViewModel by viewModel()
    private lateinit var spinnerOcrLang: Spinner
    // OCR recognizer initialization
    private var recognizer = TextRecognition.getClient(TextRecognizerOptions.Builder().build())
    private val TAG = "ML_kit_text_recognition"

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setupSpinner()
    }

    private fun setupSpinner() {
        val ocrOptions: MutableList<String> = ArrayList()
        ocrOptions.add(TEXT_RECOGNITION_CHINESE)
        ocrOptions.add(TEXT_RECOGNITION_LATIN)
        ocrOptions.add(TEXT_RECOGNITION_JAPANESE)
        ocrOptions.add(TEXT_RECOGNITION_KOREAN)
        val dataAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, ocrOptions)
        spinnerOcrLang.adapter = dataAdapter
        spinnerOcrLang.onItemSelectedListener = object: AdapterView.OnItemSelectedListener{
            override fun onItemSelected(
                parent: AdapterView<*>?,
                view: View?,
                position: Int,
                id: Long
            ) {
                if (position == 0){
                    Log.i(TAG, "Using on-device Text recognition Processor for Latin")
                    recognizer = TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build())
                }
                else if(position == 1){
                    Log.i(TAG, "Using on-device Text recognition Processor for Chinese")
                    recognizer = TextRecognition.getClient(TextRecognizerOptions.Builder().build())
                }
                else if(position == 2){
                    Log.i(TAG, "Using on-device Text recognition Processor for Japanese")
                    recognizer = TextRecognition.getClient(JapaneseTextRecognizerOptions.Builder().build())
                }
                else if(position == 3){
                    Log.i(TAG, "Using on-device Text recognition Processor for Korean")
                    recognizer = TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().build())
                }
                viewModel.selectOCRRecognizer(recognizer)
            }

            override fun onNothingSelected(parent: AdapterView<*>?) {
                TODO("Not yet implemented")
            }

        }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val view = inflater!!.inflate(R.layout.fragment_scan_home, container, false)
        spinnerOcrLang = view.findViewById<Spinner>(R.id.spinnerOcrLanguage)
        return view
    }

    companion object {
        private const val TEXT_RECOGNITION_LATIN = "Text Recognition Latin"
        private const val TEXT_RECOGNITION_CHINESE = "Text Recognition Chinese"
        private const val TEXT_RECOGNITION_JAPANESE = "Text Recognition Japanese"
        private const val TEXT_RECOGNITION_KOREAN = "Text Recognition Korean"
    }

Reference

 类似资料: