65.9K
CodeProject 正在变化。 阅读更多。
Home

Android 中简单的 XML 操作技术

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (6投票s)

2012年7月11日

CPOL

7分钟阅读

viewsIcon

47054

downloadIcon

1030

在本文中,我将讨论如何在 Android 应用程序中有效处理 XML。

引言

在本文中,我将讨论使用 XML 文件存储自定义配置的各种可能性。XML 文件非常适合执行此类任务,原因如下……它们可以使用 XML DOM 或 SAX 解析器轻松地被 Android 对象模型 (AOM) 吸收。正如我们很快就会看到,在本文的未来扩展中,XML 配置也可以通过 SIMPLE 方法生成并摄取到您的对象中,这在我的上一篇文章中已经简要提及。

.NET 技术,用于将大型文本文件转换为适用于 Android 对象的 XML 结构

XML 甚至不需要在 Android 设备上进行物理存储!这意味着您的配置可以通过 Web 服务或云服务的 HTTP 响应即时提供。

为了举例说明,我们可以将一个或多个 XML 配置文件最初存储在 ASSETS 文件夹中,然后修改这些默认配置,并将它们移动到设备的 SD 卡。这样,最终用户将始终有一个默认配置可以回退!我通常会为最终用户提供恢复到“工厂配置”或更常称为“默认配置”的选项。这简单地转化为一种单步方法,即删除 SD 卡配置,然后软件将为您进行所有智能检查,以确定用户是否已恢复到默认状态。一旦修改了默认配置,它就会被复制回 SD 卡。用户始终拥有原始配置的副本,并且永远不会丢失。如果需要,它实际上可以来自配置服务器。我将在稍后详细讨论这种有吸引力的方法。我也承认,这种双重方法是我通常偏好使用 XML 配置文件的方式,而不是将配置存储在其他数据库中。总的来说,我认为 XML 是我存储元数据、配置特定数据的首选方法,正如我们在上一篇文章中看到的,我甚至在我的电子阅读器应用程序中使用它来将整个电子书部分吸收到我的基线 AOM 中。请参阅我上一篇文章以进一步说明这个 XML 数据存储的含义。 请务必自己尝试独特的实现,这将有助于您掌握这项技术……

背景

我不会花太多时间讨论将 XML 作为在应用程序中存储频繁更改信息的策略的详尽需求!我不会主张您将此技术用于 Android 工具集中的其他工具,您可以根据您的需求和应用程序规范更好地自己做出决定。正如我之前提到的,我只是 觉得 XML 很好用,因为它易于使用且可扩展,适用于 各种用途!如果构建得当,其结构可以非常直观简单且 自描述。它可以为您的对象模型的最终结构提供很好的线索,如果该模型尚未以某种方式定义!无论哪种方式,都可以轻松地进行转换!有时,我喜欢将我的对象模型完全按照 XML 结构本身来考虑。我提供了一个我用在我电子书阅读器中的配置的小例子。此配置包含用于建模电子阅读器正文中使用的字体的所有样式管理器参数。显然,这可以发展为包含核心 应用程序的各种其他选项,但现在,让我们只想象它就是我为之设计的,即格式化电子阅读器应用程序中的字体样式。让我们仔细看看!

使用代码

<?xml version="1.0" encoding="utf-8"?>
<STYLEMANAGER>
<FONTSTYLES>
<DISPLAYTEXT>Book Title Font</DISPLAYTEXT>
<FONT>Celebration Text Fancy-Normal</FONT>
<FONTSIZE>37</FONTSIZE>
<FONTCOLOR>Ghost White</FONTCOLOR>
<FONTBOLD>False</FONTBOLD>
<FONTSHADOW></FONTSHADOW>
<DISPLAYTEXT>Book ID Font</DISPLAYTEXT>
<FONT>Caligrafia De Bula-Regio</FONT>
<FONTSIZE>28</FONTSIZE>
<FONTCOLOR>Black</FONTCOLOR>
<FONTBOLD>False</FONTBOLD>
<FONTSHADOW></FONTSHADOW>
<DISPLAYTEXT>Book Name Font</DISPLAYTEXT>
<FONT>Ballade Bold</FONT>
<FONTSIZE>27</FONTSIZE>
<FONTCOLOR>Red</FONTCOLOR>
<FONTBOLD>False</FONTBOLD>
<FONTSHADOW></FONTSHADOW>
<DISPLAYTEXT>Section Font</DISPLAYTEXT>
<FONT>Ballade Bold</FONT>
<FONTSIZE>15</FONTSIZE>
<FONTCOLOR>White</FONTCOLOR>
<FONTBOLD>False</FONTBOLD>
<FONTSHADOW></FONTSHADOW>
<DISPLAYTEXT>Verse Font</DISPLAYTEXT>
<FONT>Ballade Contour</FONT>
<FONTSIZE>25</FONTSIZE>
<FONTCOLOR>Aqua</FONTCOLOR>
<FONTBOLD>False</FONTBOLD>
<FONTSHADOW></FONTSHADOW>
</FONTSTYLES>
<SKINSTYLES>
<MAIN_BACKGROUND>Stationary Rust</MAIN_BACKGROUND>
<VERSES_BACKGROUND>Stationary Rust</VERSES_BACKGROUND>
<CLICKSELECTOR>Blue Selector with Bible</CLICKSELECTOR>
</SKINSTYLES>
</STYLEMANAGER>

FontStyle Manager 分为两个部分!从 XML 结构中,我们可以确定有 </FONTSTYLES> 节点和 </SKINSTYLES> 节点。不言而喻,有些部分会重复出现。在此配置中(5)五个这样的部分将构成各种字体样式类型。这在很多方面都很好,因为它通过轻松索引此特定配置文件中的信息,使阅读器对象非常简洁。那么,如何将此信息解析并呈现为对象形式呢?有很多方法可以做到这一点,但我只会介绍我自己的观点。您可能会想出更有创意的选择,以更好地满足您自己的需求!

// XMLParser.java

// Created by Mario Ghecea on 6/05/2012
// e-mail: solarasoft@gmail.com

// This is a simple XML DOM parsing utility
// which allows fetching, storing and loading of XML 
// configuration documents in a bi-directional methodology
// From Assets folder and saving and retrieving To and From SD Card.
// I offer this code copyright FREE for demonstrative and educational purposes!
// Feel free to modify and distribute as you wish! 
// If you decide to include its contents inside your 
// source, please leave a courtesy notice inside your code specifying that 
// I'm the original author! 

package com.solara.engineering.kjv.Parsers;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
 
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
 
import android.content.Context;
import android.content.res.AssetManager;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;
 
public class XMLParser {
 
 // constructor
 public XMLParser() {
 
 }
 
 /**
  * Getting XML from URL making HTTP request
  * @param url string
  * */
 public String getXmlFromUrl(String url) {
  String xml = null;
 
  try {
   // defaultHttpClient
   DefaultHttpClient httpClient = new DefaultHttpClient();
   HttpPost httpPost = new HttpPost(url);
 
   HttpResponse httpResponse = httpClient.execute(httpPost);
   HttpEntity httpEntity = httpResponse.getEntity();
   xml = EntityUtils.toString(httpEntity);
 
  } catch (UnsupportedEncodingException e) {
   e.printStackTrace();
  } catch (ClientProtocolException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
  // return XML
  return xml;
 }
 
 public String readXMLFromFile(Context activity, String xmlFile, boolean useConfigDir)
 {
  InputStream is = null;
  File file = null;
  File sdCard = null;
  Writer writer = new StringWriter();
  boolean exists = false;
 
  if (useConfigDir)
  {
   sdCard = Environment.getExternalStorageDirectory(); 
   file = new File (sdCard.getAbsolutePath() + "/kjv/config/" + xmlFile); 
   
   if (!(file == null)) 
   {
    if (exists = file.exists())
     try {
      is = new FileInputStream(file);
     } catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
   }
  }
  
  if (!exists)
  { 
    AssetManager assetManager = activity.getAssets(); 
    try {
    is = assetManager.open(xmlFile);
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } 
  }
   
  if (is != null) 
  {            
               
   char[] buffer = new char[1024];            
   try 
   {                
    Reader reader = new BufferedReader(                        
      new InputStreamReader(is, "UTF-8"));                
    int n;                
    while ((n = reader.read(buffer)) != -1) {                    
     writer.write(buffer, 0, n);                
     }
     is.close();
   }
   catch (IOException e) {
    Log.e("Error: ", e.getMessage());
    return null;
   }
  }
     
   
  return writer.toString();
  
 }
 
 public void writeXMLToFile(Context context, String xmlFile, String xmlData)
 {
   
         FileOutputStream fOut = null; 
 
         OutputStreamWriter osw = null;
         
 
         try
         {
          
          File sdCard = Environment.getExternalStorageDirectory(); 
          File dir = new File (sdCard.getAbsolutePath() + "/kjv/config");
          
          
          if (!dir.exists())
          {
           if (!(dir.mkdirs())) 
            Log.e("mkdirs", "Failed to create SDCARD mounted directory!!!");
          }
          
          
          fOut = new FileOutputStream(new File(dir, xmlFile)); 
          
           
  
          osw = new OutputStreamWriter(fOut); 
  
          osw.write(xmlData); 
  
          osw.flush(); 
  
          Toast.makeText(context, "Settings saved",Toast.LENGTH_SHORT).show();
 
          } 
 
   catch (Exception e) 
   {       
    e.printStackTrace(); 
    Toast.makeText(context, "Settings not saved",Toast.LENGTH_SHORT).show();
   } 
  
   finally 
   { 
     try
     { 
       osw.close(); 
       fOut.close(); 
     } 
     catch (IOException e) 
     { 
       e.printStackTrace(); 
     }
   }   
}
 
/**
  * Getting XML DOM element
  * @param XML string
* */
public Document getDomElement(String xml){
  Document doc = null;
  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  try {
 
   DocumentBuilder db = dbf.newDocumentBuilder();
 
   InputSource is = new InputSource();
          is.setCharacterStream(new StringReader(xml));
          doc = db.parse(is); 
 
   } catch (ParserConfigurationException e) {
    Log.e("Error: ", e.getMessage());
    return null;
   } catch (SAXException e) {
    Log.e("Error: ", e.getMessage());
             return null;
   } catch (IOException e) {
    Log.e("Error: ", e.getMessage());
    return null;
   }
 
   return doc;
}
 
/** Getting node value
   * @param elem element
   */
public final String getElementValue( Node elem ) {
      Node child;
      if( elem != null){
          if (elem.hasChildNodes()){
              for( child = elem.getFirstChild(); child != null; child = child.getNextSibling() ){
                  if( child.getNodeType() == Node.TEXT_NODE  ){
                      return child.getNodeValue();
                  }
              }
          }
      }
      return "";
}
  
/**
   * Getting node value
   * @param Element node
   * @param key string
* */
public String getValue(Element item, String str) {  
   NodeList n = item.getElementsByTagName(str);  
   return this.getElementValue(n.item(0));
}
  
public void setValue(Element elem, String str){
   Node child;
      if( elem != null){
          if (elem.hasChildNodes()){
              for( child = elem.getFirstChild(); child != null; child = child.getNextSibling() ){
                  if( child.getNodeType() == Node.TEXT_NODE  ){
                      child.setNodeValue(str);
                  }
              }
          }
      }
}
  
public String GetElementAttribute(Element item, String attribName){
   return item.getAttribute(attribName);   
}
}

上面列出的代码是一个名为 XMLParser.java 的辅助类,用于通过我上面指定的两种方法加载各种 XML 配置!一种方法 getXmlFromUrl() 将通过 HTTP 请求从 Web 服务器获取文件资源!另一种方法 readXMLFromFile() 将从存储在 Assets 文件夹中的文件中获取 XML 配置。AssetManager 将打开此文件并将其作为 InputStream 对象返回。这进一步细分为 Reader 缓冲区中的独立固定大小的块,并以大致相同的方式写入。最终,您将获得从原始文件中读取的已连接的 XML 配置,采用有用的字符串格式。当设置为 True 时,布尔值 useConfigDir 指定您也想使用 SD 卡目录。方法 writeXMLToFile() 相反,将允许您将这些各种 XML 配置保存到 SD 卡,并为您进行所有必需的健全性检查!请随意随意扩展和修改这些功能!它们绝非完美,仅用作加载和保存 XML 配置的模板机制。可能有更高效、速度更快的处理方法……如果您找到更好的、更快速的替代方案,请告知我!这是我的快速修复,它在我的应用程序中效果相对不错!作为对辅助代码的最后陈述,我应该提到最后几个函数仅用于方便,它们负责获取包含所有元素节点的 XML Dom 对象层次结构,或者获取和设置元素节点中的值。仅此而已!在下一节中,我们将讨论这个辅助类的简单实现!

// KjvFontStyle.java

// Created by Mario Ghecea on 6/05/2012
// e-mail: <a href="mailto:solarasoft@gmail.com">solarasoft@gmail.com</a>

// This is an implementation of the XML DOM parsing utility!
// It offers bi-directional config file access methodologies
// SD Card access and has been extended to offer an XML Serializer

// I offer this code copyright FREE for demonstrative and educational purposes!
// Feel free to modify and distribute as you wish! 
// If you decide to include its contents inside your 
// source, please leave a courtesy notice inside your code specifying that 
// I'm the original author! 

 
package com.solara.engineering.kjv.StyleManager;
 
import java.io.StringWriter;
 
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
 
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
 
import com.solara.engineering.kjv.Parsers.XMLParser;
 

import android.content.Context;
 
public class KjvFontStyle
{
  static final int maxPos = 4;
  private int position = 0;
  private String displayText;
  private String font;
  private String fontSize;
  private String fontColor;
  private String fontBold;
  private String mainBackground;
  private String versesBackground;
  private String clickSelector;
  
  static final String TAG_FONTSTYLES = "FONTSTYLES";
  static final String TAG_SKINSTYLES = "SKINSTYLES";
  static final String TAG_DISPLAYTEXT = "DISPLAYTEXT";
  static final String TAG_FONT = "FONT";
  static final String TAG_FONTSIZE = "FONTSIZE";
  static final String TAG_FONTCOLOR = "FONTCOLOR";
  static final String TAG_FONTBOLD = "FONTBOLD";
  static final String TAG_MAIN_BACKGROUND = "MAIN_BACKGROUND";
  static final String TAG_VERSES_BACKGROUND = "VERSES_BACKGROUND";
  static final String TAG_CLICKSELECTOR = "CLICKSELECTOR";
     
  XMLParser parser;
  Document doc;
  NodeList nl;
  Element e;
  Context context;
  String xml;
  
  // constructor
  public void LoadFontStyle(Context activity)
  {
   context = activity;
   
   parser = new XMLParser();
   xml = parser.readXMLFromFile(activity,"stylemanager.xml", true);
 
   doc = parser.getDomElement(xml); // getting DOM element  
  
  
   nl = doc.getElementsByTagName(TAG_DISPLAYTEXT);
   Element e = (Element) nl.item(position);
   displayText = parser.getValue(e, TAG_DISPLAYTEXT);
   nl = doc.getElementsByTagName(TAG_FONT);
   e = (Element) nl.item(position);
   font = parser.getValue(e, TAG_FONT);
   nl = doc.getElementsByTagName(TAG_FONTSIZE);
   e = (Element) nl.item(position);
   fontSize = parser.getValue(e, TAG_FONTSIZE);
   nl = doc.getElementsByTagName(TAG_FONTCOLOR);
   e = (Element) nl.item(position);
   fontColor = parser.getValue(e, TAG_FONTCOLOR);
   nl = doc.getElementsByTagName(TAG_FONTBOLD);
   e = (Element) nl.item(position);
   fontBold = parser.getValue(e, TAG_FONTBOLD);
   nl = doc.getElementsByTagName(TAG_MAIN_BACKGROUND);
   e = (Element) nl.item(0);
   mainBackground = parser.getValue(e, TAG_MAIN_BACKGROUND);
   nl = doc.getElementsByTagName(TAG_VERSES_BACKGROUND);
   e = (Element) nl.item(0);
   versesBackground = parser.getValue(e, TAG_VERSES_BACKGROUND);
   nl = doc.getElementsByTagName(TAG_CLICKSELECTOR);
   e = (Element) nl.item(0);
   clickSelector = parser.getValue(e, TAG_CLICKSELECTOR);
    
   //if (position <= maxPos)
   //position ++;
  
  }
  
  public void SetPosition(int pos){
   if (pos >= 0 && pos <= maxPos)
    position = pos;
  }
  
  // Property getters
  
  public int GetMaxPosition()
  {
   return maxPos;
  }
  
  public int GetPosition()
  {
  return position; 
  }
  
  public void ResetPosition()
  {
   position = 0;
  }
  
  public String GetDisplayText()
  {
  return displayText; 
  }
  
  public String GetFont()
  {
   return font;
  }
  
  public String GetFontSize()
  {
   return fontSize;
  }
  
  public String GetFontColor()
  {
   return fontColor;
  }
  
  public String GetFontBold()
  {
   return fontBold;
  }      
  
  // Property Setters
  
  public String GetMainBackground()
  {
   return mainBackground;
  }
  
  public String GetVersesBackground()
  {
   return versesBackground;
  }
  
  public String GetClickSelector()
  {
   return clickSelector;
  }
  
  
  public void SetFont(String fontValue)
  {    
   nl = doc.getElementsByTagName(TAG_FONT);
   e = (Element) nl.item(position);
   parser.setValue(e, fontValue); 
   font = parser.getValue(e, TAG_FONT);
  }
  
  public void SetFontSize(String fontSizeValue)
  {    
   nl = doc.getElementsByTagName(TAG_FONTSIZE);
   e = (Element) nl.item(position);
   parser.setValue(e, fontSizeValue);
   fontSize = parser.getValue(e, TAG_FONTSIZE);
 }
  
  public void SetFontColor(String fontColorValue)
  {
    
    nl = doc.getElementsByTagName(TAG_FONTCOLOR);
    e = (Element) nl.item(position);
    parser.setValue(e, fontColorValue); 
    fontColor = parser.getValue(e, TAG_FONTCOLOR);
  }
  
  public void SetFontBold(String fontBoldValue)
  {
   nl = doc.getElementsByTagName(TAG_FONTBOLD);
   e = (Element) nl.item(position);
   parser.setValue(e, fontBoldValue);
   fontBold = parser.getValue(e, TAG_FONTBOLD);
  } 
  
  public void SetMainBackground(String mainBackgroundImage)
  {
   nl = doc.getElementsByTagName(TAG_MAIN_BACKGROUND);
   e = (Element) nl.item(0);
   parser.setValue(e, mainBackgroundImage);
   mainBackground = parser.getValue(e, TAG_MAIN_BACKGROUND);
  }
  
  public void SetVersesBackground(String versesBackgroundImage)
  {
   nl = doc.getElementsByTagName(TAG_VERSES_BACKGROUND);
   e = (Element) nl.item(0);
   parser.setValue(e, versesBackgroundImage);
   versesBackground = parser.getValue(e, TAG_VERSES_BACKGROUND);
  }
  
  public void SetClickSelector(String clickSelectorImage)
  {
   nl = doc.getElementsByTagName(TAG_CLICKSELECTOR);
   e = (Element) nl.item(0);
   parser.setValue(e, clickSelectorImage);
   clickSelector = parser.getValue(e, TAG_CLICKSELECTOR);
  }
  
  public void SaveXMLSettings()
  {
   String serializedXML = this.SerializeXML(doc);
   parser.writeXMLToFile(context, "stylemanager.xml", serializedXML );
  }
  
  public String SerializeXML (Document doc)
  {
 
  // create Transformer object

  Transformer transformer = null;
  try {
   transformer = TransformerFactory.newInstance().newTransformer();
  } catch (TransformerConfigurationException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (TransformerFactoryConfigurationError e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  StringWriter writer = new StringWriter();
  StreamResult result = new StreamResult(writer);
  try {
   transformer.transform(new DOMSource(doc), result);
  } catch (TransformerException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  // return XML string
  return writer.toString();
  }
}

在本节代码中,我将通过将 KjvFontStyle 对象封装其使用来演示 XML 辅助类的实现。我还决定免费赠送一个 **XML 序列化器**,因为 **DOM** 内部没有提供!这在修改原始 XML 配置中的许多内部节点值和属性时非常有用。如果没有附带的序列化器,您将不得不绞尽脑汁地想出一个巧妙的方法将这些信息转换回通用的 String 格式!简而言之,这就是所有那些 Transformer 对象操作在 **序列化器** 中的用途!嗯,我想我可能已经耗尽了本文的主题,我希望您发现这些代码列表很有用!它们希望能为您提供对这个“野兽”性质的一些见解!最棒的是,一旦您完成了这个解析框架的主要工作,通过添加您自己独特的改进,当然,它可以很容易地扩展以访问您自己选择的任意数量的 XML 配置!我之前承诺的最后一个我想简要提及的主题是 SIMPLE 框架的有利用途!但由于本文空间不足,我将不再在此部分添加我有些广泛的电子阅读器框架的长代码片段!我将在即将发布的下一系列文章中透露更多关于这些 Android XML 摄取技术的技巧!请经常回来查看,如果您觉得这些文章有用,请给我留言,并且不要忘记给我某种评分……下次在 CP 上再见!

历史

首次修订 2012 年 7 月 11 日。

© . All rights reserved.