Tuesday, June 28, 2011
Wednesday, June 22, 2011
iOS 4 iPad Camera and UIImagePickerController Application (Xcode 4)
The application user interface for this example will consist of an image view and a toolbar containing two buttons. When selected by the user, the first button will display the camera to the user and allow a photograph to be taken and subsequently displayed in the image view. The second button will provide access to the camera roll where the user may select an existing photo image. In the case of a new image taken with the camera, this will be saved to the camera roll.
Since we will be covering the playback of video in the next chapter (Video Playback from within an iOS 4 iPad Application) the camera roll and camera will be restricted to still images in this example. The addition of video support to this application is left as an exercise for the reader at the end of the next chapter.
Creating the Camera Project
Begin the project by launching Xcode and creating a new iPad iOS application project named camera using the View-based application template.
Adding Framework Support
The application developed in this chapter relies on the MobileCoreServices framework which must be added to the project. This can be achieved by selecting the product target entry from the project navigator panel (the top item named camera) and clicking on the Build Phases tab in the main panel. In the Link Binary with Libraries section click on the ‘+’ button, select the MobileCoreServices.framework entry from the resulting panel and click on the Add button.
Configuring Protocols, Outlets and Actions
The UIImagePickerController class requires a delegate to be declared that conforms to the UIImagePickerControllerDelegate and UINavigationControllerDelegate protocols. Since the camera roll will be presented to the user using a popover, it will also be necessary to implement the UIPopoverControllerDelegate protocol within the view controller.
In addition we will need an outlet that provides access to the UIImageView object in the user interface where photos will be displayed. We also need to declare two action methods that will be called when the user selects the toolbar buttons, a boolean flag to indicate whether the image needs to be saved or not and references to both the toolbar and popover controller objects. We will also need to import the MobileCoreServices.h file. With these requirements in mind, select the cameraViewController.h file in the Xcode project navigator panel and modify it is follows:
#import UIKit/UIKit.h
#import MobileCoreServices/MobileCoreServices.h
@interface cameraViewController : UIViewController
{
UIToolbar *toolbar;
UIPopoverController *popoverController;
UIImageView *imageView;
BOOL newMedia;
}
@property (nonatomic, retain) IBOutlet UIImageView *imageView;
@property (nonatomic, retain) UIPopoverController *popoverController;
@property (nonatomic, retain) IBOutlet UIToolbar *toolbar;
- (IBAction)useCamera: (id)sender;
- (IBAction)useCameraRoll: (id)sender;
@end
Designing the User Interface
The next step in this tutorial is to design the user interface. This is a very simple user interface consisting of a UIImageView and a UIToolbar. Select the cameraViewContoller.xib file and drag and drop components from the Library window (View -> Utilities -> Object Library) onto the view. Position and size the components so that the user interface resembles the following illustration:
An iPad UIPickerView camera application user interface
Next, Ctrl-click on the File’s Owner object and drag the resulting line to the UIImageView object in the view window. Select the imageView outlet from the resulting menu. Repeat this step to connect the toolbar outlet to the UIToolBar component.
Adding Buttons to the Toolbar
Now that the toolbar has been added to the user interface the next step is to create two UIBarButtonItems and add them to the UIToolbar component. One button will launch the camera interface and the other a popover providing access to the camera roll. Since these buttons only need to be added once at application startup the code should be added to the viewDidLoad method of the view controller. Select the cameraViewController.m file, remove the comment markers from around the viewDidLoad method and modify it as follows:
- (void)viewDidLoad
{
UIBarButtonItem *cameraButton = [[UIBarButtonItem alloc]
initWithTitle:@"Camera"
style:UIBarButtonItemStyleBordered
target:self
action:@selector(useCamera:)];
UIBarButtonItem *cameraRollButton = [[UIBarButtonItem alloc]
initWithTitle:@"Camera Roll"
style:UIBarButtonItemStyleBordered
target:self
action:@selector(useCameraRoll:)];
NSArray *items = [NSArray arrayWithObjects: cameraButton,
cameraRollButton, nil];
[toolbar setItems:items animated:NO];
[cameraButton release];
[cameraRollButton release];
[super viewDidLoad];
}
The above code creates two UIBarButtonItem objects with title properties set to “Camera” and “Camera Roll” and configures them to call the useCamera: and useCameraRoll: action methods respectively when tapped by the user. An array is then created containing the two button items which is, in turn, used to add the buttons to the toolbar. The button items are then released from memory.
Implementing the Camera Action Method
The useCamera method now needs to be implemented. This method first needs to check that the device on which the application is running has a camera. It then needs to create a UIImagePickerController instance, assign the cameraViewController as the delegate for the object and define the media source as the camera. Since we do not plan on handling videos, the supported media types property is set to images only. Finally, the camera interface will be displayed and the UIImagePickerController object released. The last task is to set the newMedia flag to YES to indicate that the image is new (and therefore needs to be saved) and is not an existing image from the camera roll. Bringing all these requirements together, along with the @synthesize directive for the previously declared outlets, gives us the following useCamera method:
#import "cameraViewController.h"
@implementation cameraViewController
@synthesize imageView, popoverController, toolbar;
- (IBAction) useCamera: (id)sender
{
if ([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeCamera])
{
UIImagePickerController *imagePicker =
[[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType =
UIImagePickerControllerSourceTypeCamera;
imagePicker.mediaTypes = [NSArray arrayWithObjects:
(NSString *) kUTTypeImage,
nil];
imagePicker.allowsEditing = NO;
[self presentModalViewController:imagePicker
animated:YES];
[imagePicker release];
newMedia = YES;
}
}
.
.
@end
Implementing the useCameraRoll Method
The useCameraRoll method is responsible for displaying the camera roll view within a popover, the code for which is as follows:
- (IBAction) useCameraRoll: (id)sender
{
if ([self.popoverController isPopoverVisible]) {
[self.popoverController dismissPopoverAnimated:YES];
[popoverController release];
} else {
if ([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeSavedPhotosAlbum])
{
UIImagePickerController *imagePicker =
[[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType =
UIImagePickerControllerSourceTypePhotoLibrary;
imagePicker.mediaTypes = [NSArray arrayWithObjects:
(NSString *) kUTTypeImage,
nil];
imagePicker.allowsEditing = NO;
self.popoverController = [[UIPopoverController alloc]
initWithContentViewController:imagePicker];
popoverController.delegate = self;
[self.popoverController
presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionUp
animated:YES];
[imagePicker release];
newMedia = NO;
}
}
}
The code begins by checking if the popover is already displayed. If so then this indicates that the user has touched the Camera Roll button a second time after the popover was displayed. Convention dictates that under such circumstances the popover should be dismissed. In the event that the popover is not yet visible an image picker object is created, configured to display the camera roll with images only and editing disabled.
The popover controller is then created passing through the image picker as the view. The view controller is designated as the delegate for the popover object before the popover is displayed to the user. The sender object passed through to this method references the Use Camera button in the toolbar. This object is passed through to the popoverController’s presentPopoverFromBarButtonItem: method so that the popover is positioned directly above, and pointing to, the button when displayed. Finally, the imagePicker object is released and the newMedia flag set to NO so that the selected image does not get re-saved to the camera roll.
Writing the Delegate Methods
As described in Accessing the iPad Camera and Photo Library, in order to fully implement an instance of the image picker controller delegate protocol it is necessary to implement some delegate methods. The most important method is the didFinishPickingMediaWithInfo which is called when the user has finished taking or selecting an image. The code for these methods in our example is as follows:
-(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[self.popoverController dismissPopoverAnimated:true];
[popoverController release];
NSString *mediaType = [info
objectForKey:UIImagePickerControllerMediaType];
[self dismissModalViewControllerAnimated:YES];
if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
UIImage *image = [info
objectForKey:UIImagePickerControllerOriginalImage];
imageView.image = image;
if (newMedia)
UIImageWriteToSavedPhotosAlbum(image,
self,
@selector(image:finishedSavingWithError:contextInfo:),
nil);
}
else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie])
{
// Code here to support video if enabled
}
}
-(void)image:(UIImage *)image
finishedSavingWithError:(NSError *)error
contextInfo:(void *)contextInfo
{
if (error) {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle: @"Save failed"
message: @"Failed to save image"\
delegate: nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
}
The code in this delegate method dismisses and releases the image picker popover and identifies the type of media passed from the image picker controller. If it is an image it is displayed on the view image object of the user interface. If this is a new image it is saved to the camera roll. The finishedSavingWithError method is configured to be called when the save operation is complete. If an error occurred it is reported to the user via an alert box.
It is also necessary to implement the imagePickerControllerDidCancel delegate method which is called if the user cancels the image picker camera session without taking a picture or making an image selection. In most cases all this method needs to do is dismiss the image picker camera interface:
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[self dismissModalViewControllerAnimated:YES];
}
Releasing Memory
The final task before trying out the application is to make sure that any memory allocated in the course of execution is released:
- (void)viewDidUnload {
self.imageView = nil;
self.popoverController = nil;
self.toolbar = nil;
}
- (void)dealloc
{
[toolbar release];
[popoverController release];
[imageView release];
[super dealloc];
}
Building and Running the Application
In order to experience the full functionality of this application it will be necessary to install it on a physical iPad device. Steps on performing this are covered in Testing iOS 4 Apps on the iPad – Developer Certificates and Provisioning Profiles.
Assuming certificates and provisioning are configured, click on the Run button to launch the application. Once application loads, select the Camera button to launch the camera interface:
Once a picture has been taken and selected for use in the application it will appear in the image view object of our application user interface:
Selecting the Camera Roll button will provide access to the camera roll of the device via a popover where an image selection can be made:
Tuesday, June 14, 2011
Android: Switching screens in an Activity with animations (using ViewFlipper)
how to switch between layers using animations to make it look like you are changing screens… we will be using a ViewFlipper widget in the layout XML.
1. Create a new Android project, unless you already have one
2. Create a new Activity class that extends android.app.Activity.
3. Create a new directory under the /res directory and call it anim
4. Right-click on the new directory called anim and Import all the XML files from: Android_SDK\Platform\android-1.5\samples\ApiDemos\res\anim.
These are animations created using XML. The same animations can be created in code, but these are ready for us to use in XML.
5. Open res\layout\main.xml and copy/paste the following in:
<?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"
android:background="#ffffff"
>
<ViewFlipper android:id="@+id/details"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffff">
<TextView android:id="@+id/tv_country"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textStyle="bold"
android:textSize="18px"
android:text="Country" >
</TextView>
<Spinner android:text=""
android:id="@+id/spinner_country"
android:layout_width="200px"
android:layout_height="55px">
</Spinner>
<Button android:text="Next"
android:id="@+id/Button_next"
android:layout_width="250px"
android:textSize="18px"
android:layout_height="55px">
</Button>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#ffffff">
<TextView android:id="@+id/tv_income"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textStyle="bold"
android:textSize="18px"
android:text="Income" >
</TextView>
<EditText android:text=""
android:id="@+id/et_income"
android:layout_width="200px"
android:layout_height="55px">
</EditText>
<Button android:text="Previous"
android:id="@+id/Button_previous"
android:layout_width="250px"
android:textSize="18px"
android:layout_height="55px">
</Button>
</LinearLayout>
</ViewFlipper>
</LinearLayout>
This is a little long, let’s inspect it more closely.
- The outmost layer is a LinearLayout.
- It contains only one inner layer: ViewFlipper
- The first-level layers inside ViewFlipper will be the screens!
- ViewFlipper contains 2 LinearLayouts. Each LinearLayout is 1 screen.
- The first LinearLayout contains a label, a spinner (a dropdown), and a button
- The second LinearLayout contains a label, an edit view (input box), and a button
6. Here is what Activity1.cs looks like:
package com.warriorpoint.taxman3;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.ViewFlipper;
public class Activity1 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set main.XML as the layout for this Activity
setContentView(R.layout.main);
// Add a few countries to the spinner
Spinner spinnerCountries = (Spinner) findViewById(R.id.spinner_country);
ArrayAdapter countryArrayAdapter = new ArrayAdapter(this,
android.R.layout.simple_spinner_dropdown_item,
new String[] { "Canada", "USA" });
spinnerCountries.setAdapter(countryArrayAdapter);
// Set the listener for Button_Next, a quick and dirty way to create a listener
Button buttonNext = (Button) findViewById(R.id.Button_next);
buttonNext.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
// Get the ViewFlipper from the layout
ViewFlipper vf = (ViewFlipper) findViewById(R.id.details);
// Set an animation from res/anim: I pick push left in
vf.setAnimation(AnimationUtils.loadAnimation(view.getContext(), R.anim.push_left_in));
vf.showNext();
}
});
// Set the listener for Button_Previous, a quick and dirty way to create a listener
Button buttonPrevious = (Button) findViewById(R.id.Button_previous);
buttonPrevious.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
// Get the ViewFlipper from the layout
ViewFlipper vf = (ViewFlipper) findViewById(R.id.details);
// Set an animation from res/anim: I pick push left out
vf.setAnimation(AnimationUtils.loadAnimation(view.getContext(), R.anim.push_left_out));
vf.showPrevious();
}
});
}
}
Let’s try and decipher the code:
- First I set the main.xml to be the layout for this Activity
- I input Canada and USA as the values in my dropdown (the Spinner object)
- Then I create two listeners for the two buttons that I have Button_next and Button_previous
- Each button does the following:
- Gets a reference to the ViewFlipper
- Sets an animation by passing it the context of this class and an animation from a res/anim XML file
- showNext() or showPrevious() is called – which literally flips between the LinearLayouts in the ViewFlipper widget in the main.xml either forwards or backwards
That’s it! Run it!
Look at that fancy Spinner!
Click Next and watch it flow!
Disclaimer: There is a problem with the back animation when you press “Previous”. It flickers a little and doesn’t flow properly backwards. I still have to figure out why. If anyone knows why, drop me a comment below!
Next up: Removing the “Next” and “Previous” buttons and switching screens by using your finger to drag on the touch screen.
Android: How to switch between Activities
create an Activity, and to switch to another Activity (think of it as another screen) on the click of a button.
1. Create a new Android project – or you might already have one created.
2. Add a new Class that extends android.app.Activity. You need a total of two classes that extend Activity. You will switch from one Activity to another.
3. Now, we’ll create two XML files to store the layout of each Activity. Under the res/layouts directory create a copy of main.xml
4. Each XML file will contain 1 button. On the click of the button, the Activities will switch.
main.xml will contain:
<?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"
android:background="#ffffff" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:text="This is Activity 1" />
<Button android:text="Next"
android:id="@+id/Button01"
android:layout_width="250px"
android:textSize="18px"
android:layout_height="55px">
</Button>
</LinearLayout>
main2.xml will contain:
<?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"
android:background="#ffffff" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:text="This is Activity 2" />
<Button android:text="Previous"
android:id="@+id/Button02"
android:layout_width="250px"
android:textSize="18px"
android:layout_height="55px">
</Button>
</LinearLayout>
So each Activity will have a text that says “This is Activity x” and a button to switch the Activity.
5. Add the second Activity to the main manifest file. Open AndroidManifest.xml and add:
<activity android:name=".Activity2"></activity>
The final result will look similar to this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.warriorpoint.taxman2"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Activity1"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Activity2"></activity>
</application>
<uses-sdk android:minSdkVersion="3" />
</manifest>
If you forget to do this, then the you will get a Null Pointer exception because “Activity2” will not be found at runtime. It took me some time to find out how to find what Exception was getting thrown as well. I will include how to debug and look at Exceptions in another future post.
5. Open Activity1.java and enter the following code:
package com.warriorpoint.taxman2;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class Activity1 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button next = (Button) findViewById(R.id.Button01);
next.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent myIntent = new Intent(view.getContext(), Activity2.class);
startActivityForResult(myIntent, 0);
}
});
}
}
Here’s a quick explanation of what this does:
- setContentView(R.layout.main) makes sure that main.xml is used as the layout for this Activity.
- Gets a reference to the button with ID Button01 on the layout using (Button) findViewById(R.id.Button01).
- Create san OnClick listener for the button – a quick and dirty way.
- And the most important part, creates an “Intent” to start another Activity. The intent needs two parameters: a context and the name of the Activity that we want to start (Activity2.class)
- Finally, the Activity is started with a code of “0”. The “0” is your own code for whatever you want it to mean. Activity2 will get a chance to read this code and use it. startActivityForResult means that Activity1 can expect info back from Activity2. The result from Activity2 will be gathered in a separate method which I will not include here.
6. Open Activity2.java and enter the code below:
package com.warriorpoint.taxman2;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class Activity2 extends Activity {
/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
Button next = (Button) findViewById(R.id.Button02);
next.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent intent = new Intent();
setResult(RESULT_OK, intent);
finish();
}
});
}
This code does the following:
- Sets main2 as the layout for this Activity
- Gets a reference to Button02 and creates an OnClick listener
- In the OnClick listener, the Activity finishes with finish(). setResult() returns information back to Activity 1. In this example, it returns no information; and Activity1 doesn’t even have the listener to receive this information anyway.
That’s it! Run it!
The app will load in Activity 1:
When you click the button you will see Activity 2. There are no animations, no tweens, etc, so the screen will just “change”. I’ll talk about animations in future posts.
And clicking on the button “Previous” here will go back to Activity1.
Still to come:
1. How to create animations when switching screens.
2. How to switch using a dragging motion of your finger.
3. How to see a log of the exceptions that your app throws.
ExpandableListView on Android
As with any other widget on Android, you are free to customize the widgets as per your needs. Here, I will show how to create such a custom list adapter for the ExpandableListView.
For this example, I want to show a list of vehicles with their names. Also, I want to group them according to their category.
I have 4 classes.
1. Vehicle : The parent class for the rest.
2. Car: Extends the Vehicle class.
3. Bus: Extends the Vehicle class.
4. Bike: Extends the Vehicle class.
I have a method called getRandomVehicle(String name) which returns a random vehicle instance setting the name that I pass. The vehicle can be a Bus, Car or a Bike. Its completely random.
In the ExpandableListAdapter, (the custom adapter), there's a method called addItem(Vehicle vehicle), which manages the groups and their children.
In the SampleActivity, I have initialized a blank ExpandableListAdapter and set it to the list view. Now, I start a thread, which gets a random vehicle after every 2 seconds, and adds it to the adapter, and calls the adapter to notify that the data has changed.
There are a few methods, in the ExpandableListAdapter, which you should go through carefully. I have two layout files, group_layout.xml and child_layout.xml which are used as the layout for the group views and the child views of the ExpandableListView.
There you go, you have a custom ExpandableListView. You can find the full source code for this example here, ready to run.
There are some more methods that you might be interested in, like, how to change the "arrow icon" for the group views, or how to expand or collapse a group at will, or how to handle specific events like group collapsed or group expanded. Read the docs on ExpandableListView.
Monday, June 13, 2011
How to add CoverFlow Effect on your iPhone App
The main criteria of this post is to help you add a cool effect called the “cover flow/open flow” effect to any of your iphone apps. This is cool in two ways actually. One it adds animation kind of effect to your app and the other, its very easy to build too.
I got to learn about this effect when I was working on my “pianos” app where in i’ll have bunch of animals to select which would be displayed as a menu using this “cover flow” effect. Once a particular animal is selected your piano view for that animal comes up. My piano app will be out soon and you can check that out. The source for this post is the link displayed below.
“http://fajkowski.com/blog/2009/08/02/openflow-a-coverflow-api-replacement-for-the-iphone/”
Based on his post I have simplified things further. He uses “flicker API” for the images in his “cover flow” but in my version I would just use my own library of images so that this post would target the beginners. To begin with we should work with Photoshop a bit to generate your images. If your not acquainted with photoshop, never mind not a problem at all. This particular task with the photoshop just wants you to scale the images you use in your library to size “225 * 225″ applying “Free Transformation”. That’s all what you got to do. Once you got your images then your ready to go.
Creating the project
Firstly Create a new “view based” project with project name like “CoverFlow”.
Once you create a new project you would arrive at a screen shown below with predefined classes already generated for you.
Add the OpenFlow source code to your project
You could get the “OpenFlow” source code from “http://apparentlogic.com/openflow“. Add the Openflow library to your project.
Add QuartzCore
and CoreGraphics
frameworks to your project.
Add both the frameworks form the “Existing Frameworks..” .
Implementing the “CoverFlowViewController.h” class.
-Import “AFOpenFlowView.h” into your CoverFlowVieController.h class.
-Then, you will have to implement two protocols AFOpenFlowViewDelegate
and AFOpenFlowViewDataSource.
-The only member of .h class would be a queue to hold all the images needed for the cover flow.
Once you are done with all 3 steps, your source code would be as shown below.
// CoverFlowViewController.h
// CoverFlow
//
// Created by Avinash on 4/7/10.
// Copyright Apple Inc 2010. All rights reserved.
//
#import
#import "AFOpenFlowView.h"
@interface CoverFlowViewController : UIViewController {
// Queue to hold the cover flow images
NSOperationQueue *loadImagesOperationQueue;
}
@end
Implementing the “CoverFlowViewController.m” class.
Open the CoverFlowViewController.m class and you will see many methods already generated and commented out.
As we want this effect to occur as soon as our view is loaded, we start writing our code under the method -(void)viewDidLoad{
}
Just uncomment the method and start writing the code.
Lets see how we implement this class
-Load the images into the queue
- (void)viewDidLoad {
[super viewDidLoad];
// loading images into the queue
loadImagesOperationQueue = [[NSOperationQueue alloc] init];
NSString *imageName;
for (int i=0; i < 10; i++) {
imageName = [[NSString alloc] initWithFormat:@"cover_%d.jpg", i];
[(AFOpenFlowView *)self.view setImage:[UIImage imageNamed:imageName] forIndex:i];
[imageName release];
NSLog(@"%d is the index",i);
}
[(AFOpenFlowView *)self.view setNumberOfImages:10];
}
-We implement two delegate protocols one to set the default image for the cover flow and the other to tell which image has bee selected.
//delegate protocols
// delegate protocol to tell which image is selected
- (void)openFlowView:(AFOpenFlowView *)openFlowView selectionDidChange:(int)index{
NSLog(@"%d is selected",index);
}
// setting the image 1 as the default pic
- (UIImage *)defaultImage{
return [UIImage imageNamed:@"cover_1.jpg"];
}
CoverFlow@end
Once both your classes are implemented you need your Image library to display the images in the Cover Flow effect. Just drag and drop your set of images folder under the Resources tab of your project
Once you have implemented both the classes and your images library is ready you will be just left out with final step, modifying the interface builder i.e, the .xib file.
You will find this .xib file under the Resources tab in the project. Double click this .xib file and it opens up the “interface builder”.
Resources-iphone –> CoverflowViewController.xib
Once you open this file the Interface Builder would present you with 3 to 4 separate windows like Library, Identity Inspector, View, Reveal in document window.
If they do not open up automatically, nothing to worry, you can open it up manually and you find all these windows in the Tools tab of the Interface Builder. Once you open up the CoverFlowViewController.xib window you will find attributes like Files owner, First Responder, Open Flow View.
As you can see the type of this Open Flow View attribute will be set to UIView.
But as we are creating a Open Flow View instead of the regular UIView its identity should be changed to AFOpenFlowView.
To do this select the Open Flow View attribute in the .xib window and in the Identity Inspector window go to the tab “i” (identity) the right corner tab. Change the class UIView to AFOpenFlowView as shown below.
Once you are done with this final step the stage is set and you are ready to go. Save and Run this project in the iphone simulator and you must be able to see the Cover Flow effect as shown in the screen shot below.
And there you go, you have just added the cool Cover FLow Effect to your apps in minutes. But it took me much longer time to complete this post.
Download the complete source code here
Sunday, June 12, 2011
Android - Add data to SQLite database, with SimpleCursorAdapter updated dynamically
Modify main.xml and row.xml to update our 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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Enter content of column 1"
/>
<EditText
android:id="@+id/content1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Enter content of column 2"
/>
<EditText
android:id="@+id/content2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/add"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Add"
/>
<Button
android:id="@+id/deleteall"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Delete All"
/>
<ListView
android:id="@+id/contentlist"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
<?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="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:padding="2dip"
android:text="#"/>
<TextView
android:id="@+id/id"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:padding="2dip"
android:paddingRight="10dip"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:padding="2dip"
android:paddingRight="10dip"
android:text="-" />
<TextView
android:id="@+id/text1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="2dip"/>
</LinearLayout>
<TextView
android:id="@+id/text2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="2dip"/>
</LinearLayout>
SQLiteAdapter.java
package com.exercise.AndroidSQLite;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
public class SQLiteAdapter {
public static final String
MYDATABASE_NAME = "MY_DATABASE";
public static final String
MYDATABASE_TABLE = "MY_TABLE";
public static final int MYDATABASE_VERSION = 1;
public static final String KEY_ID = "_id";
public static final String KEY_CONTENT1 = "Content1";
public static final String KEY_CONTENT2 = "Content2";
//create table MY_DATABASE (ID integer primary key, Content text not null);
private static final String SCRIPT_CREATE_DATABASE =
"create table " + MYDATABASE_TABLE + " ("
+ KEY_ID + " integer primary key autoincrement, "
+ KEY_CONTENT1 + " text not null, "
+ KEY_CONTENT2 + " text not null);";
private SQLiteHelper sqLiteHelper;
private SQLiteDatabase sqLiteDatabase;
private Context context;
public SQLiteAdapter(Context c){
context = c;
}
public SQLiteAdapter openToRead() throws android.database.SQLException {
sqLiteHelper = new SQLiteHelper(context, MYDATABASE_NAME, null,
MYDATABASE_VERSION);
sqLiteDatabase = sqLiteHelper.getReadableDatabase();
return this;
}
public SQLiteAdapter openToWrite() throws android.database.SQLException {
sqLiteHelper = new SQLiteHelper(context, MYDATABASE_NAME, null,
MYDATABASE_VERSION);
sqLiteDatabase = sqLiteHelper.getWritableDatabase();
return this;
}
public void close(){
sqLiteHelper.close();
}
public long insert(String content1, String content2){
ContentValues contentValues = new ContentValues();
contentValues.put(KEY_CONTENT1, content1);
contentValues.put(KEY_CONTENT2, content2);
return sqLiteDatabase.insert(MYDATABASE_TABLE, null, contentValues);
}
public int deleteAll(){
return sqLiteDatabase.delete(MYDATABASE_TABLE, null, null);
}
public Cursor queueAll(){
String[] columns = new String[]{KEY_ID, KEY_CONTENT1, KEY_CONTENT2};
Cursor cursor = sqLiteDatabase.query(MYDATABASE_TABLE, columns,
null, null, null, null, null);
return cursor;
}
public class SQLiteHelper extends SQLiteOpenHelper {
public SQLiteHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL(SCRIPT_CREATE_DATABASE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
}
AndroidSQLite.java
package com.exercise.AndroidSQLite;
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
public class AndroidSQLite extends Activity {
EditText inputContent1, inputContent2;
Button buttonAdd, buttonDeleteAll;
private SQLiteAdapter mySQLiteAdapter;
ListView listContent;
SimpleCursorAdapter cursorAdapter;
Cursor cursor;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
inputContent1 = (EditText)findViewById(R.id.content1);
inputContent2 = (EditText)findViewById(R.id.content2);
buttonAdd = (Button)findViewById(R.id.add);
buttonDeleteAll = (Button)findViewById(R.id.deleteall);
listContent = (ListView)findViewById(R.id.contentlist);
mySQLiteAdapter = new SQLiteAdapter(this);
mySQLiteAdapter.openToWrite();
cursor = mySQLiteAdapter.queueAll();
String[] from = new String[]{SQLiteAdapter.KEY_ID,
SQLiteAdapter.KEY_CONTENT1, SQLiteAdapter.KEY_CONTENT2};
int[] to = new int[]{R.id.id, R.id.text1, R.id.text2};
cursorAdapter =
new SimpleCursorAdapter(this, R.layout.row, cursor, from, to);
listContent.setAdapter(cursorAdapter);
buttonAdd.setOnClickListener(buttonAddOnClickListener);
buttonDeleteAll.setOnClickListener(buttonDeleteAllOnClickListener);
}
Button.OnClickListener buttonAddOnClickListener
= new Button.OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
String data1 = inputContent1.getText().toString();
String data2 = inputContent2.getText().toString();
mySQLiteAdapter.insert(data1, data2);
updateList();
}
};
Button.OnClickListener buttonDeleteAllOnClickListener
= new Button.OnClickListener(){
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
mySQLiteAdapter.deleteAll();
updateList();
}
};
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
mySQLiteAdapter.close();
}
private void updateList(){
cursor.requery();
}
}
Download the files.
Android: Reading, using and working with XML data and web services in Android
One of the most powerful aspects of any mobile application for a 3G phone is that it can connect to the Internet. By connecting to the Internet the application can offer much more value to the user since it becomes an interface for a web-based component, e.g. using Twitter’s API to create a Twitter application so that you can get your Twitter updates without having to open the mobile browser. The most common way of interfacing with a web-based component is by using web services in XML format.
While trying to developer my own app which reads a web service from my own server, I ran into a lot of difficulties in implementing the client that consumes the web service. Android does not have libraries for XPath handling of XML documents, so it makes deciphering XML data a little bit more difficult. From what I’ve read online the Android team is currently working on including such libraries in future versions.
After some digging around I found an amazing link that shows different methods for consuming an XML file in Android and parsing through it without the use of XPaths. The link is this: Working with XML on Android. To start off, this link is an absolute must-read. Everything that I am going to write in my post here relates to this link. The code offered on that webpage uses polymorphism to show you 4 different methods of working with XML data. It provides a fully-functional Android application and all the source code for it. The source code can be found here: AndroidXML.zip.
My post today will concentrate on how to customize the code from the application in the above link, in order to read and parse your own XML data. If you are a Java pro, you might not need this post. My Java is a little rusty, so I needed some time to figure out exactly what I had to change and where in order to get this to work with my own web service XML. Now that I’ve figured it out, I thought I’d share it. In my next post I will give the simplified version of this code – where there is no polymorphism, and thus there are only the minimum number of classes needed to implement this XML-reading solution.
So until I post the simplified source code for working with XML data in Android, here are some tips on getting through the larger polymorphism-based source code and customizing it for your own XML data:
1. First off, read over the link Working with XML on Android as much as you can.
2. Download the source code for the Android application that they offer: AndroidXML.zip.
3. Import the project into your Eclipse workspace by right-clicking in Project Explorer and selecting “Import”.
4. Select “Existing Projects into Workspace”
5. Browse to the directory where you extracted the ZIP file with the source code and then click on the Next buttons to finish off the wizard. The project is called “AndroidXml”.
6. You will now see the project in your workspace:
7. Here is a quick breakdown of what some of those Java classes do:
- MessageList.java is the main activity that gets started. It lists the items from the XML data using a ListActivity. In this project the items come from an RSS feed.
- FeedParser.java, FeedParserFactory.java, BaseFeedParser.java, RSSHandler.hava are all classes that this particular example uses to set the framework for polymorphism.
- This example uses 4 methods for grabbing the XML data and reading it.The 4 methods that this example uses are:
1. AndroidSaxFeedParser.java (the default)
2. DomFeedParser.java
3. SaxFeedParser.java
4. XmlPullFeedParser.java
These 4 classes all extend BaseFeedParser.java.
8. In order to customize this for your own XML file you need to edit the following places (assuming you are using AndroidSaxFeedParser, which is the default):
- FeedParserFactory.java: you need to change the URL location of the web service or XML document in the global variable here:
static String feedUrl = "http://www.androidster.com/android_news.rss";
- AndroidSaxFeedParser.java: you need to change the root node of your XML document. This is stored in the String called RSS.
static final String RSS = "RootNode";
- BaseFeedParser.java: you need to change this class according to the nodes that your XML document has.
The nodes CHANNEL and ITEM refer to the nodes <Channel> and <Item> in the RSS feed that this example uses. You need to change them to mimic your nodes from your XML document:
static final String CHANNEL = "channel";
static final String ITEM = "item";
The other constants that are declared refer to the nodes for each repeating item.
static final String PUB_DATE = "pubDate";
static final String DESCRIPTION = "description";
static final String LINK = "link";
static final String TITLE = "title";
For this particular example, since an RSS feed XML document is used, it has repeating nodes for <Description>, <Link>, <Title>, <PubDate>. You need to change this structure to mimic your structure.
Note: Remember that if you change the name of the constants (as opposed to the value of the constants), you will need to change other classes which call these constants.
- If you change the names of the constants, you will have to update AndroidSaxFeedParser.java in this section:
item.getChild(TITLE).setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentMessage.setTitle(body);
}
});
item.getChild(LINK).setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentMessage.setLink(body);
}
});
item.getChild(DESCRIPTION).setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentMessage.setDescription(body);
}
});
item.getChild(PUB_DATE).setEndTextElementListener(new EndTextElementListener(){
public void end(String body) {
currentMessage.setDate(body);
}
});
As you can see this section is hardcoded for the 4 nodes that are expected in this XML document (TITLE, LINK, DESCRIPTION, PUB_DATE). You will need to change this section and hardcode this for your own nodes.
- If you change the names of the higher-level nodes, i.e. <Channel> and <Item>, then you need to update the following section of AndroidSaxFeedParser.java:
Element itemlist = root.getChild(CHANNEL);
Element item = itemlist.getChild(ITEM);
And that is all. The customized code will use the AndroidSaxParser implementation of an XML Parser, it will go to the URL you provided in FeedParserFactory.java, and it will iterate through the updated nodes as you have labeled them in BaseFeedParser.java and AndroidSaxFeedParser.java.
In my next post I will provide a simplified version of this code, which does not use polymorphism. It will (hopefully) use the minimum required classes to get XML data and parse it.
Friday, June 10, 2011
How to read the assets directory resources - Andriod
1. Get the input stream resource
Resource file sample.txt at $ PROJECT_HOME / assets / directory, can be adopted in the Activity
Context.getAssets (). Open ("sample.txt")
Method to obtain input stream.
Note: If the resource file is a text file that you need to consider file encoding and line breaks. Recommend the use of UTF-8 and Unix line breaks.
2. WebView load the assets directory html files
Resource file sample.html at $ PROJECT_HOME / assets / directory, the following code can be
WebView.loadUrl ("file: / / / android_asset / sample.html");
Load html file.
Wednesday, June 8, 2011
Adding a UIButton Programatically
UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchDown];
[btn setTitle:@"SKIP" forState:UIControlStateNormal];
btn.frame = CGRectMake(0, 100, 320, 50);
[self.view addSubview:btn];
and if you want to add a background image
UIImage *someImage = [UIImage imageNamed:@"splashImage.png"];
[btn setBackgroundImage:someImage forState:UIControlStateNormal];
Difference between 2 UIDatePicker’s in hours
UIDatePicker *date2;
The function to show difference between these two date pickers
NSDate *date1Val = date1.date;
NSDate *date2Val = date2.date;
NSTimeInterval interval = [date2Val timeIntervalSinceDate:date1Val];
int hours = (int)interval / 3600;
int minutes = (interval - (hours*3600)) / 60;
NSString *timeDiff = [NSString stringWithFormat:@"%d:%d", hours, minutes];
Hacking into PhoneGap iPhone/iPad Apps
Steps:
Get hold of an phonegap iPhone App. I have one, which I had created a long time back.
- So go to – http://www.phonegap.com/apps
- Select iPhone or iPad
- Pick an app for which you want code (Let me pick my application)
- Get the link to the app and open with iTunes
Download the app. It will save the app in your Apps Folder
Right Click on the app and select “Show in Finder”
You will see the .ipa file.
TicTacToe 1.1.ipa
Rename it to
TicTacToe 1.1.zip
Unzip the file and you will find something like this
Right Click on TicTacToe and select “Show Package Contents” – There will be mostly a folder in it “www” which will have the entire HTML, JS and CSS files or whatever the person used.
iPhone/iPad AirPrinting Tutorial in 3 steps
Step 1:
Start Xcode and make a New Project – Select View-based Application – I call it AirPrinting.
Add an image to your project – I added demo.png – Here is the image
Step 2:
Go to AirPrintingViewController.h and add the following code
# import <UIKit/UIKit.h> @ interface AirPrintingViewController : UIViewController <UIPrintInteractionControllerDelegate>{ } -( void )printItem; @end |
Next in AirPrintingViewController.m add the following code
# import "AirPrintingViewController.h" @implementation AirPrintingViewController -( void )printItem { NSString *path = [[NSBundle mainBundle] pathForResource:@ "demo" ofType:@ "png" ]; NSData *dataFromPath = [NSData dataWithContentsOfFile:path]; UIPrintInteractionController *printController = [UIPrintInteractionController sharedPrintController]; if (printController && [UIPrintInteractionController canPrintData:dataFromPath]) { printController.delegate = self; UIPrintInfo *printInfo = [UIPrintInfo printInfo]; printInfo.outputType = UIPrintInfoOutputGeneral; printInfo.jobName = [path lastPathComponent]; printInfo.duplex = UIPrintInfoDuplexLongEdge; printController.printInfo = printInfo; printController.showsPageRange = YES; printController.printingItem = dataFromPath; void (^completionHandler)(UIPrintInteractionController *, BOOL, NSError *) = ^(UIPrintInteractionController *printController, BOOL completed, NSError *error) { if (!completed && error) { NSLog(@ "FAILED! due to error in domain %@ with error code %u" , error.domain, error.code); } }; [printController presentAnimated:YES completionHandler:completionHandler]; } } - ( void )viewDidLoad { [ super viewDidLoad]; UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [btn addTarget:self action:@selector(printItem) forControlEvents:UIControlEventTouchDown]; [btn setTitle:@ "PRINT" forState:UIControlStateNormal]; btn.frame = CGRectMake( 0 , 100 , 320 , 50 ); [self.view addSubview:btn]; } @end |
Step 3:
Build and Run – You will see a print button – Click the print Button and you will see UIPrintInteractionController popup – Go ahead and Print.
Here is the entire Code