올해는 머신러닝이다.
WORKING WITH JSON,XML AN ANDROID 본문
출처 : http://www.ibm.com/developerworks/xml/library/x-andbene1/index.html
Mobile devices and platforms boast more features and functionality with each new release, and often mere months separate significant announcements from the leading mobile vendors. The headlines are mostly about UI features (such as advanced multitouch capabilities and Adobe® Flash® technology) and hardware enhancements (such as processor speed and storage capacity). But the crucial fact remains that content is king. Content—or, more generally, data—is exchanged constantly among applications, servers, mobile devices, and users. Without being able to work with it, smartphones such as Apple's iPhone and Google's Android simply become overpriced and underperforming cell phones.
Consider the phenomenal success of social-networking platforms such as Facebook, LinkedIn, and Twitter. From a pure feature-and-function perspective, these platforms are largely pedestrian. They are popular because members and site visitors derive value from the content published there. And that content is accessed increasingly by mobile devices.
This article demonstrates the use of XML and JSON data-interchange formats on the Android platform. The source of the data for the example application is a status-update feed for a Twitter account. The feed data is available from Twitter in both XML and JSON formats. As you'll see, the programming approach to manipulating the data varies significantly between the two formats.
I suggest you have the Android SDK version 1.5 or later installed along with Eclipse to run the example code that accompanies this article. To learn more about setting up your environment, visit the Android Developers website. It is also helpful, but not required, to have an active Twitter account to follow along with the example. See Resources for relevant links.
I'll begin with a brief look at both data formats, starting with XML. If you're already familiar with XML and JSON, you can safely skip to Application opportunity: Twitter feeds to start working with them on Android.
Virtually anyone who has done programming for the enterprise, web, or mobile markets in recent years has encountered XML. It is just about everywhere you look.
An XML document has a recognizable structure: a series of elements that can optionally contain attributes and child elements. Every valid XML document begins with a declaration on the first line: <?xml version="1.0" encoding="utf-8"?>
. What comes after the first line is application-dependent. The beauty of XML is that it is self-describing.
Although XML documents are self-describing, they must follow certain rules and guidelines. This is where the XML schema comes in. It is a document that describes the structure of a particular XML file. Such structures are often verbose and complex. (Arguably, XML's worst contribution to the IT field is the data explosion that took place once the idea of highly descriptive data structures came into fashion, fueled in part by the greatly reduced cost of disk-storage technology in the past decade.)
As these large and complex files became more the norm, the art of working on them manually was often a nonstarter for programmers and analysts. To address this issue, XML editors and validating tools became available to aid in the management of the files and the tasks relating to them, such as documentation and conversion to legacy formats.
In addition to general text data, XML can also be used to store binary data through a special set of tags known as CDATA
. CDATA
tags within an XML document can contain any kind of data, including other mark-up text, provided that text does not contain CDATA
itself.
It's not uncommon for APIs to leverage this capability by using XML as a structure for performing request/response queries. Often the response data contains an XML structure contained within a CDATA
tag. For example, an API call might request a customer record with a last name equaling Mott
. When the data is found, it is packaged into an XML structure and placed within the response element, as in Listing 1:
Listing 1. Packaging data into an XML structure and placing it within the response element
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <request> <query> <lastname>Mott</lastname> <maxhits>100</maxhits> </query> </request> <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <response> <returncode>200</returncode> <query> <lastname>Mott</lastname> <hits>1</hits> </query> <data> <![CDATA[ <contact> <firstname>Troy</firstname> <lastname>Mott</lastname> <age>not telling</age> </contact> ]]> </data> </response> |
Today XML is the default, expected data format. Although the same data might be available in other formats too, it is a safe bet to plan on the availability of an XML structure.
Enterprise Resource Planning (ERP) packages use XML heavily for data import and export tasks. Internet news sites often make data available as Really Simple Syndication (RSS) feeds—XML documents with a predefined format that news readers are set up to process. Even word-processing applications such as OpenOffice.org and Microsoft® Office use XML.
Today's Microsoft Office documents are PKZIP-compatible files containing multiple XML documents. Each XML file shares the common declaration in the first line. As you can see in Listing 2, the attributes can be somewhat hard to follow:
Listing 2. Common declaration in the first line of each XML file
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:document xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml"> <w:body><w:p w:rsidR="00B6337C" w:rsidRDefault="00663F0E"><w:r> <w:t xml:space="preserve">This is a sample </w:t></w:r><w:r w:rsidRPr="006906EA"><w:rPr><w:i/></w:rPr><w:t>Microsoft Word document</w:t></w:r><w:r><w:t xml:space="preserve"> used to </w:t></w:r><w:r w:rsidRPr="006906EA"><w:rPr><w:b/> <w:u w:val="single"/></w:rPr><w:t>demonstrate</w:t></w:r> <w:r><w:t xml:space="preserve"> some XML topics.</w:t></w:r> </w:p><w:p w:rsidR="00B14B2A" w:rsidRDefault="00B14B2A"/><w:p w:rsidR="00B14B2A"w:rsidRDefault="00B14B2A"><w:r><w:rPr> <w:noProof/></w:rPr><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="3276600" cy="3838575"/><wp:effectExtent l="19050" t="0" r="0" b="0"/><wp:docPr id="1" name="Picture 0" descr="frankableson.jpg"/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"> <pic:nvPicPr><pic:cNvPrid="0"name="frankableson.jpg"/><pic:cNvPicPr/> </pic:nvPicPr><pic:blipFill><a:blip r:embed="rId4" cstate="print"/><a:stretch><a:fillRect/></a:stretch> </pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/> <a:ext cx="3276600" cy="3838575"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic> </a:graphicData></a:graphic></wp:inline></w:drawing> </w:r></w:p><w:p w:rsidR="00663F0E" w:rsidRDefault="00663F0E"/> <w:p w:rsidR="00CC16CE" w:rsidRDefault="00CC16CE"/><w:sectPr w:rsidR="00CC16CE" w:rsidSect="00B6337C"><w:pgSz w:w="12240" w:h="15840"/> <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/><w:cols w:space="720"/><w:docGrid w:linePitch="360"/></w:sectPr></w:body></w:document> |
XML is self-describing, but that doesn't mean that the tags are necessarily easy to decipher. This cryptic example also demonstrates the use of multiple XML namespaces, which can make XML documents even more challenging to follow without specialized tools.
XML is everywhere, but it can often be a poor choice for an Android programmer, particularly if the data structure has fallen victim to the data explosion often associated with XML structures. A resource-constrained platform such as Android, which is usually working on a cellular data network, cannot store and parse massive amounts of XML data. However, if a particular programming task requires the interchange of both text and binary data, XML can be a solid choice.
Now you'll look at an alternative data-interchange format: JSON.
More and more Internet API providers are offering JSON as a data-format option. JSON made a name for itself in the Ajax (Asynchronous JavaScript and XML) web-programming community. Ajax technology enables web pages to update dynamically by refreshing data in selected pockets rather the entire page. Because less data is transferred—and, more important, because much less data is parsed and drawn to the browser window—an Ajax-enabled application can provide a much better user experience than a traditional web application. In fact, a well-written Ajax application can rival smart- or fat-client applications in user experience.
When an Ajax application exchanges data with a web server, it is often requesting a refresh of some kind of data, but ideally without the formatting. It is generally considered poor practice for a web server to serve up preformatted HTML. Instead, a well-written application should send data content to the browser and apply a Cascading Style Sheets (CSS) file to provide visual effects such as colors and font particulars.
Suppose an application wants to request a contact record for our mythical Mr. Mott. The application has more than one data element to send back to the browser. So how does it get packaged? In the Listing 1 example, you might use a simple request/response structure in XML. This is perfectly adequate; however, it requires that you parse each response from the server, store the data in a structure of some sort (a DOM), and then update the web-page content.
Alternatively, you can simply get some JavaScript back from the server and work directly with it. Here is a sample response from a hypothetical application responding to a query (http://<yourserver/app/searchcontact?Mott
) for a man named Mott. The response is a string representation of a JavaScript object—that is, a JSON string (split into two lines here to fit this article's page width):
[{"firstname":"Troy","lastname":"Mott","age":"don't ask!"},{"firstname":"Apple seed", "lastname":"Mott's","age":"99"}] |
Whereas XML is known for its verbosity, JSON has a reputation for being somewhat hard to read. JSON objects are constructed in a key:value pair format. The object's elements are separated by commas, and each object is contained within curly braces {}
. An array of objects is contained within square brackets. This is a common approach to transferring a series of rows from a database to an array of objects in which each array element corresponds to a database row, and each property of the object represents a column of data.
Listing 3 shows an example of using this kind of object within an HTML page. For simplicity, the server communications are not included; instead, the JSON data is provided as a string variable named serverresponse
.
Listing 3. Using a JSON object within an HTML page
<html> <head> <script language="JavaScript"> var serverresponse = "[{\"firstname\":\"Troy\",\"lastname\":\"Mott\",\"age\":\"don't ask!\"},{\"firstname\":\"Apple seed\",\"lastname\":\"Mott's\",\"age\":\"99\"}]"; function updatepage() { var contacts = eval(serverresponse ); var i; var s = "Search Results:<br />"; for (i=0;i<contacts.length;i++) { s = s + contacts[i].firstname + " " + contacts[i].lastname + "'s age is ... " + contacts[i].age + "<br />"; } document.getElementById("target").innerHTML = s; } </script> </head> <body> <button onclick="updatepage();">Search for Mott</button><br /> <span id="target"> </span> </body> </html> |
Note that this example uses the JavaScript function named eval()
to turn a string into a JavaScript array. JSON libraries are available to provide faster and more secure methods of performing this step. The approach in Listing 3 is not a best practice. I provide it here to give some context to how a JSON object can be used in an Ajax application: The JSON structure is exchanged, parsed, and manipulated by the client code.
In summary, JSON is:
- A data-interchange format.
- A means to encode JavaScript objects as strings.
- Limited to text and numeric values. Binary values are explicitly not permitted. JSON has no
CDATA
equivalent. - More economical than XML in terms of data size, at the expense of readability.
- Increasingly available as an option from API providers such as Twitter.
In Listing 3 the client is a web browser running client-side scripting. Returning to this article's focus, next you'll examine the use of XML and JSON in an Android application.
Application opportunity: Twitter feeds
Twitter has become an international force, providing updates on everything from what people are having for breakfast and how their kid's sports team is faring at the ballpark, to serious topics such as street-level updates on political revolts in closed nations or a play-by-play of an organ transplant.
The easiest way to get to get the XML and JSON documents to use with the sample code accompanying this article is through the URL http://twitter.com/statuses/user_timeline/userid.format, where userid is your own Twitter user ID and format is either XML or JSON.
You can also find a link to this page directly on your Twitter page, as in Figure 1. You can see your Twitter user ID there.
Figure 1. Link to feeds page on your Twitter page
The complete feed files are very verbose, so the next two listings show only the first entry of the feed (from my own Twitter account). Listing 4 contains the XML snippet:
Listing 4. The XML snippet
<?xml version="1.0" encoding="UTF-8"?> <statuses type="array"> <status> <created_at>Thu Apr 29 05:25:29 +0000 2010</created_at> <id>13052369631</id> <text>Wrapping up new article on JSON for Android programmers...</text> <source><a href="http://www.linkedin.com/"rel="nofollow"> LinkedIn</a></source> <truncated>false</truncated> <in_reply_to_status_id/> <in_reply_to_user_id/> <favorited>false</favorited> <in_reply_to_screen_name/> <user> <id>15221439</id> <name>fableson</name> <screen_name>fableson</screen_name> <location>Byram Township, NJ</location> <description/> <profile_image_url>http://a3.twimg.com/profile_images/260492935 /bookcover_normal.jpg</profile_image_url> <url>http://msiservices.com</url> <protected>false</protected> <followers_count>52</followers_count> <profile_background_color>9ae4e8 <profile_text_color>000000</profile_text_color> <profile_link_color>0000ff</profile_link_color> <profile_sidebar_fill_color>e0ff92 </profile_sidebar_fill_color> <profile_sidebar_border_color>87bc44 </profile_sidebar_border_color> <friends_count>10</friends_count> <created_at>Tue Jun 24 17:04:11 +0000 2008</created_at> <favourites_count>0</favourites_count> <utc_offset>-18000</utc_offset> <time_zone>Eastern Time (US & Canada)</time_zone> <profile_background_image_url>http://s.twimg.com/a/1272044617/ images/themes/theme1/bg.png</profile_background_image_url> <profile_background_tile>false</profile_background_tile> <notifications>false</notifications> <geo_enabled>false</geo_enabled> <verified>false</verified> <following>false</following> <statuses_count>91</statuses_count> <lang>en</lang> <contributors_enabled>false</contributors_enabled> </user> <geo/> <coordinates/> <place/> <contributors/> </status> </statuses> |
Listing 5 shows the same data, this time in JSON format:
Listing 5. Feed data in JSON format
[ {"in_reply_to_status_id":null, "favorited":false, "created_at":"Thu Apr 29 05:25:29 +0000 2010", "in_reply_to_screen_name":null, "geo":null, "source":"<a href=\"http://www.linkedin.com/\" rel=\"nofollow\ ">LinkedIn</a>", "contributors":null, "place":null, "truncated":false, "coordinates":null, "user": { "friends_count":10, "description":"", "lang":"en", "statuses_count":91, "time_zone":"Eastern Time (US & Canada)", "profile_link_color":"0000ff", "favourites_count":0, "created_at":"Tue Jun 24 17:04:11 +0000 2008", "contributors_enabled":false, "profile_sidebar_fill_color":"e0ff92", "following":null, "geo_enabled":false, "profile_background_image_url":"http://s.twimg.com/a/1272044617/images/themes /theme1/bg.png", "profile_image_url":"http://a3.twimg.com/profile_images/260492935 /bookcover_normal.jpg", "notifications":null, "profile_sidebar_border_color":"87bc44", "url":"http://msiservices.com", "verified":false, "profile_background_tile":false, "screen_name":"fableson", "protected":false, "location":"Byram Township, NJ", "profile_background_color":"9ae4e8", "name":"fableson", "followers_count":52, "id":15221439, "utc_offset":-18000, "profile_text_color":"000000" }, "in_reply_to_user_id":null, "id":13052369631, "text":"Wrapping up new article on JSON for Android programmers..."} ] |
Notice how much extra data is included in both listings beyond the mere status update. All you need to care about is the date/time when the post was made and the text from the post itself. Next I'll show you the pertinent portions of an Android application that parses this data. The full project is available for download.
The Android application is simple. It contains full copies of the XML and JSON data feeds and gives the user the option of parsing either one. Figure 2 shows the structure of the project files within Eclipse. (View a text-only version of Figure 2.)
Figure 2. File structure of the Eclipse project
Figure 3 shows the application's UI prior to the selection of a parsing option:
Figure 3. The application's UI prior to selection of a parsing option
The application UI shows two buttons, Parse XML and Parse JSON file, followed by default text. Listing 6 contains the layout for this UI, found in the main.xml file in the res/layout folder of the project:
Listing 6. The layout for the UI
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnXML" android:text="Parse XML"></Button> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btnJSON" android:text="Parse JSON file"></Button> </LinearLayout> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ScrollView01" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="default text" android:layout_gravity="center_horizontal" android:id="@+id/txtData" /> </ScrollView> </LinearLayout> |
The Parse XML and Parse JSON file buttons are defined above a ScrollView
, which in turn contains a TextView
control. The idea here is that you want the user to be able to scroll through the resulting data.
Note the use of multiple LinearLayout
structures. The first is a vertical alignment, and it contains both a LinearLayout
with a horizontal structure and a ScrollView
. The inner LinearLayout
contains the two Button
widgets. This layout is inflated and wired up in the onCreate()
method, in Listing 7:
Listing 7. The
onCreate()
methodButton btnXML; Button btnJSON; TextView tvData; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tvData = (TextView) findViewById(R.id.txtData); btnXML = (Button) findViewById(R.id.btnXML); btnXML.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { examineXMLFile(); } }); btnJSON = (Button) findViewById(R.id.btnJSON); btnJSON.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { examineJSONFile(); } }); } |
The examineXMLFile()
method controls the XML parsing.
Parsing of XML data is often done with a SAX-style parser. With this style of parser, you set up an InputSource
pointing to the source XML data and provide a handler that receives certain events as the document is "walked." Listing 8 shows the examineXMLFile()
method, which takes care of the following tasks:
- Sets up the
InputSource
with the XML file from the raw resources - Creates a
SAXParser
, associated with a handler namedtwitterFeedHandler
( in Listing 9) - Invokes the parser and displays the results in a
TextView
widget identified in the layout file asR.id.txtData
and referenced in the code astvData
- Shows any errors, also in the
TextView
Listing 8. The
examineXMLFIle()
methodvoid examineXMLFile() { try { InputSource is = new InputSource(getResources() .openRawResource(R.raw.xmltwitter)); // create the factory SAXParserFactory factory = SAXParserFactory.newInstance(); // create a parser SAXParser parser = factory.newSAXParser(); // create the reader (scanner) XMLReader xmlreader = parser.getXMLReader(); // instantiate our handler twitterFeedHandler tfh = new twitterFeedHandler(); // assign our handler xmlreader.setContentHandler(tfh); // perform the synchronous parse xmlreader.parse(is); // should be done... let's display our results tvData.setText(tfh.getResults()); } catch (Exception e) { tvData.setText(e.getMessage()); } } |
Whereas examineXMLFile()
sets things up, the real parsing work from the perspective of the application takes place in the handler that's implemented in the twitterFeedHandler.java file. This class, which implements the DefaultHandler
interface, is inListing 9:
Listing 9. The
twitterFeedHandler
classpublic class twitterFeedHandler extends DefaultHandler { StringBuilder sb = null; String ret = ""; boolean bStore = false; int howMany = 0; twitterFeedHandler() { } String getResults() { return "XML parsed data.\nThere are [" + howMany + "] status updates\n\n" + ret; } @Override public void startDocument() throws SAXException { // initialize "list" } @Override public void endDocument() throws SAXException { } @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { try { if (localName.equals("status")) { this.sb = new StringBuilder(""); bStore = true; } if (localName.equals("user")) { bStore = false; } if (localName.equals("text")) { this.sb = new StringBuilder(""); } if (localName.equals("created_at")) { this.sb = new StringBuilder(""); } } catch (Exception ee) { Log.d("error in startElement", ee.getStackTrace().toString()); } } @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { if (bStore) { if (localName.equals("created_at")) { ret += "Date: " + sb.toString() + "\n"; sb = new StringBuilder(""); return; } if (localName.equals("user")) { bStore = true; } if (localName.equals("text")) { ret += "Post: " + sb.toString() + "\n\n"; sb = new StringBuilder(""); return; } } if (localName.equals("status")) { howMany++; bStore = false; } } @Override public void characters(char ch[], int start, int length) { if (bStore) { String theString = new String(ch, start, length); this.sb.append(theString); } } } |
Listing 9 contains some noteworthy items. The first thing to consider is that the SAX parser is an event-based parser, which means that you build the actual document as it is parsed. Events are fired whenever the document starts, the document ends, a tag starts, a tag ends, and data is found. This implies that you must define a data structure to keep the data of interest and discard the rest.
Note the StringBuilder
and the appending of data, used because a particular data element can be processed across multiple reads on the InputSource
. Never assume that all of the data is provided in any given call to the characters()
method.
This application collects the data into a simply formatted string. An alternative example might include putting these entries into a collection class or database instead, particularly if a great deal of manipulation were to take place after the parse.
The getResults()
method is custom to this class. It's used to gather the assembled representation of this data and provide it to the application. It is not part of the DefaultHandler
's interface.
Figure 4 depicts the parsed XML data. (View a text-only version of Figure 4.)
Figure 4. The parsed XML data
Although parsing XML with the SAX parser is nontrivial in terms of building up, managing, and navigating the resulting structure, its main advantages are speed and the opportunity to reduce significantly the amount of RAM required both during and after the parsing step.
Now you'll take a look at Android's approach to parsing JSON data.
Parsing JSON data in the application starts when the user selects the JSON button. This invokes the examineJSONFile()
method, in Listing 10. No additional handler class is required, because all parsing and document management take place within the Android-supplied libraries, and all JSON-related code is contained in this method.
Listing 10. Invoking the
examineJSONfile()
methodvoid examineJSONFile() { try { String x = ""; InputStream is = this.getResources().openRawResource(R.raw.jsontwitter); byte [] buffer = new byte[is.available()]; while (is.read(buffer) != -1); String jsontext = new String(buffer); JSONArray entries = new JSONArray(jsontext); x = "JSON parsed.\nThere are [" + entries.length() + "]\n\n"; int i; for (i=0;i<entries.length();i++) { JSONObject post = entries.getJSONObject(i); x += "------------\n"; x += "Date:" + post.getString("created_at") + "\n"; x += "Post:" + post.getString("text") + "\n\n"; } tvData.setText(x); } catch (Exception je) { tvData.setText("Error w/file: " + je.getMessage()); } } |
Like the XML routine shown previously, this code reads in a file from the raw resources. The data is read into memory in its entirety, converted to a java.lang.String
, and then parsed into a JSONArray
. Note that a particular string can be parsed directly into an array, as in this example, or the string can be parsed into a JSONObject
. Because the Twitter data is an array of objects, it makes sense to parse the entire string into an array and then access the object individually by ordinal position.
This method's flow is straightforward; once the data is parsed, the code builds up a string representation that's similar to the approach of the XML parser handler. Of interest here is that the data is managed for you; you don't need to build up additional memory structures to contain the data. Likewise, the application knows up front just how many entries are in the JSONArray
(20 in this example).
Although JSON parsing is much more simple programmatically, it is not cost-free. It adds the burden of memory consumption for reading in the entire data stream before it can be worked on, and for storing all of the data. In contrast, the SAX XML approach uses only the data of interest. That caveat aside, if memory for parsing a particular JSON object is ample, this approach can be favorable for many applications, particularly if working with the DOM is of little interest.
'Android > Tip&Tech' 카테고리의 다른 글
android change titlebar diynamic (0) | 2011.06.14 |
---|---|
Fixed header in a TableLayout (1) | 2011.06.14 |
android HTTP GET,POST Examples (0) | 2011.06.10 |
[펌]ViewFlipper 관한 설명(추천) (0) | 2011.06.09 |
progressbar 배경 및 색깔 바꾸기 (0) | 2011.06.09 |