Archive

Posts Tagged ‘text’

Image Processing – Draw Text on a Curve

December 1, 2011 2 comments

Just watching on StackOverflow and found this interesting question: How to write curve text?

Here what I’ve tried so far:

Drawing Text on a Curve

Drawing Text on a Curve

Here my implementation:

package pete.android.study;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;

import android.graphics.RectF;
import android.view.View;

public class GraphicsView extends View {
    private static final String MY_TEXT = "xjaphx: Draw Text on Curve";
    private Path mArc;

    private Paint mPaintText;

    public GraphicsView(Context context) {
      super(context);

      mArc = new Path();
      RectF oval = new RectF(50,100,200,250);;
      mArc.addArc(oval, -180, 200);
      mPaintText = new Paint(Paint.ANTI_ALIAS_FLAG);
      mPaintText.setStyle(Paint.Style.FILL_AND_STROKE);
      mPaintText.setColor(Color.WHITE);
      mPaintText.setTextSize(20f);

    }

    @Override
    protected void onDraw(Canvas canvas) {
      canvas.drawTextOnPath(MY_TEXT, mArc, 0, 20, mPaintText);
      invalidate();
    }
  }

Enjoy painting Android 🙂

Cheers,
Pete Houston

Categories: Tutorials Tags: , , ,

Android XML Adventure – Parsing XML Data with SAXParser

October 9, 2011 15 comments

Article Series: Android XML Adventure

Author: Pete Houston (aka. `xjaphx`)

TABLE OF CONTENTS

  1. What is the “Thing” called XML?
  2. Parsing XML Data w/ SAXParser
  3. Parsing XML Data w/ DOMParser
  4. Parsing XML Data w/ XMLPullParser
  5. Create & Write XML Data
  6. Compare: XML Parsers
  7. Parsing XML using XPath
  8. Parsing HTML using HtmlCleaner
  9. Parsing HTML using JSoup
  10. Sample Project 1: RSS Parser – using SAXParser
  11. Sample Project 1: RSS Parser – using DOM Parser
  12. Sample Project 1: RSS Parser – using XMLPullParser
  13. Sample Project 2: HTML Parser – using HtmlCleaner
  14. Sample Project 2: HTML Parser – using JSoup
  15. Finalization on the “Thing” called XML!

=========================================

Hope you’ve already known what XML is in previous article `What is the “Thing” called XML?`.

At the start of the series, I will talk about how to parse XML data. Why parsing first? Well, it’s because most of the applications tend to parse XML data from other sources, like RSS, which is very common.

There are many ways to parse XML file in Android, however, the most three common methods are:

  1. SAXParser
  2. DOM
  3. XmlPullParser

I’ll talk about SAXParser in this article.

For a quick and easy understandings, I’ve created this flow-chart to describe how SAX-Parser works.

How SAX-Parser works!

How SAX-Parser works!

All the elements in XML document will be parsed through a ContentHandler with five pre-defined methods handling actions taken.

Class Overview

This is the main interface that most SAX applications implement: if the application needs to be informed of basic parsing events, it implements this interface and registers an instance with the SAX parser using the setContentHandler method. The parser uses the instance to report basic document-related events like the start and end of elements and character data.

The order of events in this interface is very important, and mirrors the order of information in the document itself. For example, all of an element’s content (character data, processing instructions, and/or subelements) will appear, in order, between the startElement event and the corresponding endElement event.

This interface is similar to the now-deprecated SAX 1.0 DocumentHandler interface, but it adds support for Namespaces and for reporting skipped entities (in non-validating XML processors).

(@Quote from: http://developer.android.com/reference/org/xml/sax/ContentHandler.html)

I guess the picture I drew above explains everything. Let’s head to work.

First, we need a sample XML file, I called it: “record.xml” and put it under “assets” directory, using as assets.

<?xml version="1.0" encoding="UTF-8"?>
<record>
	<study id="1">
		<topic>SAX Parser</topic>
		<content>Learn how to parse XML using SAXParser</content>
		<author>Pete Houston</author>
		<date>02-Oct-2011</date>
	</study>
</record>

We will use SAX-Parser to parse these data and display on a TextView.

The data we need: [ study id, topic, content, author, date ], I store them as an entity called “Study“, `Study.java`.

package pete.android.study.data;

public class Study {
	public int mId;
	public String mTopic;
	public String mContent;
	public String mAuthor;
	public String mDate;

	public static final String STUDY = "study";
	public static final String ID = "id";
	public static final String TOPIC = "topic";
	public static final String CONTENT = "content";
	public static final String AUTHOR = "author";
	public static final String DATE = "date";}

Great, we’ve done half of the job. Next, we need a ContentHandler that does the job of parsing every single elements in XML document, called it “StudyHandler.java“:

package pete.android.study.parser;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import pete.android.study.data.Study;

/*
 * class StudyHandler *
 */
public class StudyHandler extends DefaultHandler {

	// members
	private boolean isTopic;
	private boolean isContent;
	private boolean isAuthor;
	private boolean isDate;
	// 'Study' entity to parse
	private Study mStudy;

	// 'getter' is enough
	public Study getStudy() {
		return mStudy;
	}

	/*
	 * (non-Javadoc)
	 * @see org.xml.sax.helpers.DefaultHandler#startDocument()
	 */
	@Override
	public void startDocument() throws SAXException {
		// create new object
		mStudy = new Study();
	}

	/*
	 * (non-Javadoc)
	 * @see org.xml.sax.helpers.DefaultHandler#endDocument()
	 */
	@Override
	public void endDocument() throws SAXException {
		// nothing we need to do here
	}

	/*
	 * (non-Javadoc)
	 * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
	 */
	@Override
	public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
							throws SAXException {
		// if this element value equals "study"
		if(localName.equals(Study.STUDY)) {
			// get id right away
			mStudy.mId = Integer.parseInt(atts.getValue(Study.ID));
		}
		// if this element value equals "topic"
		else if(localName.equals(Study.TOPIC)) {
			// mark current element is "topic"
			isTopic = true;
		}
		// if this element value equals "content"
		else if(localName.equals(Study.CONTENT)) {
			// mark current element is "content"
			isContent = true;
		}
		// if this element value equals "author"
		else if(localName.equals(Study.AUTHOR)) {
			// mark current element is "author"
			isAuthor = true;
		}
		// if this element value equals "date"
		else if(localName.equals(Study.DATE)) {
			// mark current element is "date"
			isDate = true;
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
	 */
	@Override
	public void endElement(String namespaceURI, String localName, String qName) throws SAXException {

		if(localName.equals(Study.STUDY)) {
		    // already get the attribute "id", nothing needs to do here
		}
		// if this element value equals "topic"
		else if(localName.equals(Study.TOPIC)) {
			// uncheck marking
		    isTopic = false;
		}
		// if this element value equals "topic"
		else if(localName.equals(Study.CONTENT)) {
			// uncheck marking
			isContent = false;
		}
		// if this element value equals "topic"
		else if(localName.equals(Study.AUTHOR)) {
			// uncheck marking
			isAuthor = false;
		}
		// if this element value equals "topic"
		else if(localName.equals(Study.DATE)) {
			// uncheck marking
			isDate = false;
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
	 */
	@Override
	public void characters(char ch[], int start, int length) {
		// get all text value inside the element tag
		String chars = new String(ch, start, length);
	    chars = chars.trim(); // remove all white-space characters

	    // if this tag is "topic", set "topic" value
	    if(isTopic) mStudy.mTopic = chars;
	    // if this tag is "content", set "content" value
	    else if(isContent) mStudy.mContent = chars;
	    // if this tag is "author", set "author" value
	    else if(isAuthor) mStudy.mAuthor = chars;
	    // if this tag is "date", set "date" value
	    else if(isDate) mStudy.mDate = chars;
	}
}

There we go a data handler done. Basically, that’s enough; however, for simplicity, I’d like to create an utility class that handles the whole parsing stuffs, called “StudyParser.java“:

package pete.android.study.parser;

import java.io.InputStream;

import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

import android.util.Log;

import pete.android.study.data.Study;

public class StudyParser {

	public static Study parse(InputStream is) {
		Study study = null;
		try {
			// create a XMLReader from SAXParser
			XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
			// create a StudyHandler too
			StudyHandler studyHandler = new StudyHandler();
			// apply handler to the XMLReader
			xmlReader.setContentHandler(studyHandler);
			// the process starts
			xmlReader.parse(new InputSource(is));
			// get the target `Study`
			study = studyHandler.getStudy();

		} catch(Exception ex) {
			Log.d("XML", "StudyParser: parse() failed");
		}

		// return Study we found
		return study;
	}
}

OK! So in main program, just giving a static call to “StudyParser.parse()” will return us a “Study” object from input XML.
Here my main program, you can just create any program you like. Mine having a TextView to display result is so enough for demonstration.

package pete.android.study;

import java.io.IOException;

import pete.android.study.data.Study;
import pete.android.study.parser.StudyParser;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class Main extends Activity {
    TextView tvStudy;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        tvStudy = (TextView)findViewById(R.id.text);
        Study study = null;
        try {
			study = StudyParser.parse(getAssets().open("record.xml"));
		} catch (IOException e) {
			Log.d("XML","onCreate(): parse() failed");
			return;
		}

		String output = "";
		output += "Study ID: " + study.mId + "\n";
		output += "Topic: " + study.mTopic + "\n";
		output += "Content: " + study.mContent + "\n";
		output += "Author: " + study.mAuthor + "\n";
		output += "Date: " + study.mDate + "\n";

		tvStudy.setText(output);
    }
}

And this is the output result:

SAX-Parser Result

SAX-Parser Result

The data displays as expected!

Simple enough with SAX-Parser?

If you’re beginner or newbie, just read this article slowly and don’t skim, and you will understand it.

There are still more about SAX-Parser, however, I just guide you the most common usage and the most simple concept of parsing XML data using SAX-Parser. You will find more by Googling, I believe!

See you in the next article!!!

Cheers,

Pete Houston

Store and use files in Assets

October 2, 2011 32 comments

There are times when you probably want to your application distribution with raw resources, instead of pre-defined resources, the ‘res‘ folder, you gonna have to make use of ‘Asset‘.

Assets’ folder will be distributed along with the APK, which contains all the raw files you need for application, such as: text files (.txt), non-Android XML files (.xml), Audio files (.wav, .mp3, .mid)…; those cannot be put into ‘res‘ folder as usual.

Thing needed to be looked up here: AssetManager from Android Developers’ References

This class does the job that we need.

First, create a project as usual, then put files into ‘asset‘ like below:

Files in 'Assets'

Files in 'Assets'

Now, create a simple layout containing a TextView for displaying the content of ‘text.txt‘ and an ImageView for displaying the image ‘avatar.jpg’, which are put in Asset.

The implementation quite easy using AssetManager as mentioned above.

package pete.android.study;

import java.io.IOException;
import java.io.InputStream;

import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;

public class Main extends Activity {

	ImageView mImage;
	TextView mText;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mImage = (ImageView)findViewById(R.id.image);
        mText = (TextView)findViewById(R.id.text);
        loadDataFromAsset();
    }

    public void loadDataFromAsset() {
    	// load text
    	try {
    		// get input stream for text
	    	InputStream is = getAssets().open("text.txt");
	    	// check size
	    	int size = is.available();
	    	// create buffer for IO
	    	byte[] buffer = new byte[size];
	    	// get data to buffer
	    	is.read(buffer);
	    	// close stream
	    	is.close();
	    	// set result to TextView
	    	mText.setText(new String(buffer));
    	}
    	catch (IOException ex) {
    		return;
    	}

    	// load image
    	try {
	    	// get input stream
	    	InputStream ims = getAssets().open("avatar.jpg");
	    	// load image as Drawable
	    	Drawable d = Drawable.createFromStream(ims, null);
	    	// set image to ImageView
	    	mImage.setImageDrawable(d);
    	}
    	catch(IOException ex) {
    		return;
    	}

    }
}

That’s a quick sample code giving this result.

Read data from Asset

Read data from Asset

The distributed APK file contains the folder ‘assets‘, you might wanna check by opening it.

Quite easy, isn’t it?

@p/s: you can load image from Asset into Bitmap by using BitmapFactory.decodeStream(), instead of using Drawable.

Have fun w/ Android Coding 🙂

 

Cheers,

Pete Houston

Categories: Tutorials Tags: , , , , , ,