2013年6月29日 星期六

[PhoneGap] Writing a mobile app with PhoneGap

Recently, I'm trying to write a mobile app with PhoneGap. To my surprise, it is not that difficult if you are familiar with HTML, jQuery and CSS. Most functions used in a tool app are already provided in html.

For example, a hotel search app may need those methods:
Call the hotel:
<a href="tel:+44123123123"> CALL ME </a>

Write an email:
<a href="mailto:123@123123123"> EMAIL ME </a>

Send a text message:
<a href="sms:123@123123123"> TEXT ME </a>

Find the location on map (I'm writing an Android app, it is working to call the native google map app. Not pretty sure if it works on iOS)
<a href="geo:54,42"> FIND ME </a>

Get the direction
<a href="http://maps.google.com/maps?saddr=startPos&daddr=endPos&ll=StartPos" > GET THE DIRECTION </a>

When writing an application becomes easy, I think what really important is why you want to write an app and what you want to present to the world.

2013年5月21日 星期二

[Android] Dynamically generate a table layout and scroll

When we have to show a result of uncertain amount of records, it is reasonable to generate the table dynamically. Since the count might be large, scrolling is a way to present it.
Here is an example.

To scroll, we put ScrollView and HorizontalScrollView outside our table layout in the main_activity.xml.
<scrollview 
    android:layout_alignleft="@+id/textView2" 
    android:layout_below="@+id/buttonDisc"
    android:layout_height="wrap_content" 
    android:layout_marginleft="14dp" 
    android:layout_margintop="47dp" 
    android:layout_width="wrap_content">
<horizontalscrollview android:layout_height="wrap_content" android:layout_width="wrap_content"> <tablelayout android:id="@+id/tb1" android:layout_height="wrap_content" android:layout_width="wrap_content"> </tablelayout> </horizontalscrollview>  </scrollview>
To generate table, take the track details as an example
        
private void drawTable(ArrayList<TrackDetail> tdList)
{
    try
    {
        TableLayout t1 = (TableLayout) findViewById(R.id.tb1);
        t1.removeAllViews();
        t1.addView(generateTitle());
   
        for(int i = 0; i < tdList.size() ; i++)
        {
           TableRow row = generateView(tdList.get(i));
           t1.addView(row);
        }
    }   
    catch(Exception e)
    {
        Toast.makeText(this, "draw fail" +e.toString(), Toast.LENGTH_LONG).show();
    }
}

public TableRow generateTitle()
{
    TableRow row = new TableRow(mContext);
    row.setBackgroundColor(0xffffff00);  
  
    TextView tv = new TextView(mContext);
    tv.setTextColor(0xFF000000);
    tv.setText("Disc No   ");
    row.addView(tv);
    
    TextView tv2 = new TextView(mContext);  
    tv2.setText("Track No   ");
    tv2.setTextColor(0xFF000000);
    row.addView(tv2);
  
    TextView tv3 = new TextView(mContext);
    tv3.setText("Composer   ");
    tv3.setTextColor(0xFF000000);
    row.addView(tv3);
  
    TextView tv4 = new TextView(mContext);
    tv4.setText("work   ");
    tv4.setTextColor(0xFF000000);
    row.addView(tv4);
  
    TextView tv5 = new TextView(mContext);
    tv5.setText("title   ");
    tv5.setTextColor(0xFF000000);
    row.addView(tv5);
  
    return row;
}
 
// produce the row filled with track detail dynamically
public TableRow generateView(TrackDetail t)
{
    TableRow row = new TableRow(mContext);
  
    TextView tv = new TextView(mContext);
    tv.setTextColor(0xFF000000);
    tv.setText( Integer.toString(t.getDiscNo())+ "   ");
    row.addView(tv);
  
    TextView tv2 = new TextView(mContext);
    tv2.setTextColor(0xFF000000);
    tv2.setText( Integer.toString(t.getTrackNo())+ "   ");
    row.addView(tv2);
  
    TextView tv3 = new TextView(mContext);
    tv3.setTextColor(0xFF000000);
    tv3.setText(t.getComposer() + "   ");
    row.addView(tv3);
  
    TextView tv4 = new TextView(mContext);
    tv4.setTextColor(0xFF000000);
    tv4.setText(t.getWorkNm()+ "   ");
    row.addView(tv4);
  
    TextView tv5 = new TextView(mContext);
    tv5.setTextColor(0xFF000000);
    tv5.setText(t.getTitleNm()+ "   ");
    row.addView(tv5);
  
    return row;
}


The result looks like

When we scroll it

2013年5月19日 星期日

[Android] Passing an array list of user-defined objects to another activity

Passing objects between activities is common in Android. We pass strings, integers, arrays and etc. There are corresponding methods there. But what if we want to pass an array list of our own objects?

This time, I have an object include strings and integers received from a web service, and I want to pass it to main activity.

When passing an array list of user-defined objects between different activities on Andoird, using Parcelable is an efficient way to do it. To fulfill the requirement, we have to implement our object Parcelable and declare related functions.

Here is an example, we have a class called TrackDetail with integer and string attributes implements Parcelable and add related functions in the code.

public class TrackDetail implements Parcelable
{
 private int discNumber;
 private int trackNumber;
 private String composerName;
 private String workName;
 private String titleName;
 
 public TrackDetail(int discno, int trackno, String comNm, String wkNm, String tNm)
 { 
  this.discNumber = discno;
  this.trackNumber = trackno;
  this.composerName = comNm;
  this.workName = wkNm;
  this.titleName = tNm;
 }
 
 public TrackDetail(Parcel in) {
  readFromParcel(in);
 }

  @Override
 public int describeContents() {
  // TODO Auto-generated method stub
  return 0;
 }

  @Override
 public void writeToParcel(Parcel dest, int flags) {
  // TODO Auto-generated method stub
  dest.writeInt(discNumber);
  dest.writeInt(trackNumber);
  dest.writeString(composerName);
  dest.writeString(workName);
  dest.writeString(titleName);
 }
 
 private void readFromParcel(Parcel in) {
   
  // We just need to read back each
  // field in the order that it was
  // written to the parcel
  discNumber = in.readInt();
  trackNumber = in.readInt();
  composerName = in.readString();
  workName = in.readString();
  titleName = in.readString();
 }

  public static final Parcelable.Creator CREATOR =
      new Parcelable.Creator() {
             public TrackDetail createFromParcel(Parcel in) {
                 return new TrackDetail(in);
             }
  
             public TrackDetail[] newArray(int size) {
                 return new TrackDetail[size];
             }
         };

}

Then we can just ignore the functions and use it as a normal class.
ArrayList<TrackDetail> tdList = new ArrayList<TrackDetail>();
for (int i = 0; i< count; i++)
{
    SoapObject tmp = (SoapObject)resultAry.getProperty(i);
    TrackDetail td = new TrackDetail(
          Integer.parseInt(tmp.getProperty("discNumber").toString()),
          Integer.parseInt(tmp.getProperty("trackNumber").toString()),
          tmp.getProperty("composerName").toString(),
          tmp.getProperty("workName").toString(),
          tmp.getProperty("titleName").toString()); 
    tdList.add(td);    
} 
Finally we can use the putParcelableArrayList method of bundle to pass it.
Bundle b = new Bundle(); //Bundle will take the results
b.putInt("result", count); //add to bundle
b.putParcelableArrayList("tdlist", tdList);
//pass the array list of track detail to main activity
receiver.send(0, b);