Android+kotlinでPOIを使ってExcelのデータを読み取る

# AndroidでExcelのデータを読み取る
JavaでExcelやWord等のOfficeのフォーマットを扱うライブラリには[POI](https://poi.apache.org/)があります。Android+kotlinでも同様にPOIが使えます

# 何に使うの？使いみちは？
何で、今更AndroidでPOIを使ってExcelを読み込む必要がある?と思うかもしれません。google spredsheetあるんじゃない？と思うかもしれません。
Androidには色々なデバイスが付いていますが、そのデバイスがエミュレータには付いていない場合があります。Android端末実機ではなくて、エミュレータで試験する場合、そのデバイスの部分をMock化して、そのでデバイスがプロクラムに渡すデータを、デバックデータとしてExcelで定義するのに使えます。

今、私が開発しているアプリは専用のバーコードカメラの付いたAndroid端末で、バーコードを読み取るアプリを開発しています。このバーコードカメラはエミュレータにはありません。そうなると、エミュレータではデバックどころか全く動かすことができません。

# 実際にやってみる

## ライブラリの依存関係
build.gradleに依存関係を追加します
```gradle:build.gradle
dependencies {
    (中略)
    implementation 'org.apache.poi:poi:5.2.3'
    implementation 'org.apache.poi:poi-ooxml:5.2.3'
}
```
POIのバージョンが古いとPOIの依存関係であるxmlbeansのXMLパーサの問題でエラーになります。これが解決されたのが2022年の2月くらいなので、できるだけ最新のバージョンを使います。
```
(中略)
 Caused by: org.apache.xmlbeans.XmlException: error: The 'namespace-prefix' feature is not supported while the 'namespaces' feature is enabled.
    at org.apache.xmlbeans.impl.store.Locale$SaxLoader.load(Locale.java:2550)
    at org.apache.xmlbeans.impl.store.Locale.lambda$parseToXmlObject$3(Locale.java:715)
    at org.apache.xmlbeans.impl.store.Locale$$ExternalSyntheticLambda7.parse(Unknown Source:6)
    at org.apache.xmlbeans.impl.store.Locale.syncWrap(Locale.java:488)
    at org.apache.xmlbeans.impl.store.Locale.parseToXmlObject(Locale.java:714)
    at org.apache.xmlbeans.impl.schema.SchemaTypeLoaderBase.parse(SchemaTypeLoaderBase.java:233)
    at org.apache.xmlbeans.impl.schema.AbstractDocumentFactory.parse(AbstractDocumentFactory.java:71)
    at org.apache.poi.xssf.model.ThemesTable.<init>(ThemesTable.java:86)
    at org.apache.poi.xssf.usermodel.XSSFRelation$$ExternalSyntheticLambda13.init(Unknown Source:2) 
(中略)
```
## MainActivity
```kotlin:MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater).apply {
            setContentView(this.root)
        }
        val xlsx = File(applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "sample.xlsx")
        Log.d("", xlsx.absolutePath)
        val str = buildString {
            WorkbookFactory.create(xlsx).use { wb ->
                val sheet = wb.getSheet("Sheet1")
                for (r in 0..sheet.lastRowNum) {
                    val row = sheet.getRow(r)
                    for (c in 0..row.lastCellNum -1) {
                        append(row.getCell(c))
                        append(" ")
                    }
                    append("\r\n")
                }
            }
        }
        binding.excelText.text = str
    }
}
```
## Excelデータ
適当にsample.xlsxというファイル名でデータを作ります。
![excel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933408/2dcf6df3-978b-9753-f32e-51b675fdfa46.png)
## ExcelをAndroid端末に転送してやる
このExcelファイルをAndroidStudioのDeviceFileExploerで転送してやります。
物理パスは私がAndroidエミュレータで試した限りでは、以下のようになりましたが、エミュレータの仮想イメージ、実機の機種によって微妙に異なる場合があります。
```
/storage/emulated/0/Android/data/com.example.poi/files/Download/sample.xlsx
```
## 実行してみる
こんな風に表示されます。
![Screenshot_20221217_201010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933408/c95a1f20-d4e0-4c14-ef53-9bc4fe077cf7.png)

POIはJavaで開発していたときもJUnitで単体試験をするときに単体テストフレームワークに組み込んでよく使っていました。テキストよりも、複数シート持てるし、色々な表現ができるので便利です。

# 最後に
完成版は[gitHub](https://github.com/cozyk100/AndroidStudioProjectsPublic/tree/main/POIkotlin)に置きました