1. 引言
最近工作有解析外部xml文件在App中显示的需求,特来写篇文章记录一下,方便下次使用。
2. 准备工作
首先,在项目的AndroidManifest.xml文件中添加读取外部存储的权限声明。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
3. XML示例文件
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item>
<number>1</number>
<description>First item description</description>
</item>
<item>
<number>2</number>
<description>Second item description</description>
</item>
</items>
4. 请求运行时权限
在你的Activity中,需要处理运行时权限请求。
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
if (isGranted) {
readAndParseXmlFile()
} else {
Toast.makeText(this, "读取外部存储权限被拒绝", Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
when {
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) ==
PackageManager.PERMISSION_GRANTED -> {
readAndParseXmlFile()
}
else -> {
requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
}
}
}
5. 定义数据模型和适配器
定义一个数据类Item和一个RecyclerView的适配器ItemAdapter。
Item.kt
data class Item(val number: Int, val description: String)
ItemAdapter.kt
class ItemAdapter(private val items: MutableList<Item>) :
RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {
class ItemViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val textView = LayoutInflater.from(parent.context)
.inflate(android.R.layout.simple_list_item_2, parent, false) as TextView
return ItemViewHolder(textView)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val (number, description) = items[position]
holder.textView.text = "$number - $description"
}
override fun getItemCount() = items.size
fun addItem(item: Item) {
items.add(item)
notifyItemInserted(items.size - 1)
}
}
6. 解析XML文件
义一个方法来解析XML文件,并在解析出新数据时即时更新RecyclerView。
private fun parseXmlAndUpdateRecyclerView(inputStream: InputStream) {
try {
val factory = XmlPullParserFactory.newInstance()
factory.isNamespaceAware = true
val parser = factory.newPullParser()
parser.setInput(inputStream, null)
var eventType = parser.eventType
var currentNumber: Int? = null
var currentDescription: String? = null
while (eventType != XmlPullParser.END_DOCUMENT) {
when (eventType) {
XmlPullParser.START_TAG -> {
when (parser.name) {
"number" -> currentNumber = parser.nextText().toIntOrNull()
"description" -> currentDescription = parser.nextText()
}
}
XmlPullParser.END_TAG -> {
if (parser.name == "item" && currentNumber != null && currentDescription != null) {
val newItem = Item(currentNumber, currentDescription)
runOnUiThread {
adapter.addItem(newItem)
}
currentNumber = null
currentDescription = null
}
}
}
eventType = parser.next()
}
} catch (e: Exception) {
Log.e("XMLParser", "Error parsing XML", e)
Toast.makeText(this, "解析XML文件失败: ${e.message}", Toast.LENGTH_SHORT).show()
} finally {
try {
inputStream.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
7. 使用
在你的代码中直接调用readAndParseXmlFile
方法,我是把xml文件直接放到了 sdcard
目录下了,你也可以随意修改目录,注意不同安卓版本的权限问题,
private fun readAndParseXmlFile() {
try {
// 修改为从SD卡根目录获取XML文件
val xmlFile = File(Environment.getExternalStorageDirectory(), "data.xml")
if (!xmlFile.exists()) {
Log.e("XMLParser", "File does not exist.")
Toast.makeText(this, "XML文件不存在", Toast.LENGTH_SHORT).show()
return
}
FileInputStream(xmlFile).use { fis ->
parseXmlAndUpdateRecyclerView(fis)
}
} catch (e: Exception) {
Log.e("XMLParser", "Error reading XML file", e)
Toast.makeText(this, "读取XML文件失败: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
8.效果图
THE END