Monday, July 26, 2010
Android: Intent Switching / Reusing
Monday, August 31, 2009
Notes on iPhone:Safari ComboBox in an app
Here's a photo of the results. Without the pickerup
Heres one with the picker.
Okay the first thing your going to want is the picture for the drop down.
The basic implementation from a top level perspective is a UITextField that is not enabled. This is a simple approach that will get you what you want but could be better. The following creates the combobox in a given cell, this can be made more generic by making cell into a generic (id*) or (uiview*).
//params
//string default string to show if a user preference isn't already choosen
//field the UITextField you want to assign if you want to save it.
//cell the view you will add the textfield and button to
//action a selector for the uibutton
//fieldstring the user define string which may be empty so i pass it with the default string
- (void)comboboxwith:(NSString*)string withview:(UITextField*)field oncell:(UITableViewCell*)cell withSel:(SEL)action forfield:(NSString*)fieldstring{
//textfield creation
//basic reason for using UITextField it allows you to add a subview to the left or right
//of the text field
field =[[CustomTextField alloc] initWithFrame:CGRectMake(5, 3, self.tableView.frame.size.width-10, 38)];
field.borderStyle = UITextBorderStyleRoundedRect;
field.text = (fieldstring == nil || [fieldstring compare:@""] == 0) ? string : fieldstring;
field.font = [UIFont fontWithName:@"Helvetica" size:24.0];
field.autoresizingMask = UIViewAutoresizingFlexibleWidth;
//add the down arrow for list view
UIImageView *imgview = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"downarrow.png"]];
field.rightView = imgview;
field.rightViewMode = UITextFieldViewModeAlways;
[imgview release];
field.enabled=NO;
[cell addSubview:field];
//uibutton creation on top of uitextfield, plain transparent button that listens to touchs.
UIButton *bnt = [UIButton buttonWithType:UIButtonTypeCustom];
bnt.frame = CGRectMake(5, 3, self.tableView.frame.size.width-10, 38);
bnt.autoresizingMask = UIViewAutoresizingFlexibleWidth;
bnt.backgroundColor = [UIColor clearColor];
[bnt addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:bnt];
}
As for loading the UIPickerView on selection of the combobox, the selector handles to loading of the uipickerview and toolbar with the done button as to exit from the uipickerview. As follows:
//load a uipicker with states, you can give the picker a tag, and a default state if
//loaded from a preference or something.
- (void)selectstate:(id)sender{
[self pickerwith:@"States" withId:0 withdefault:state];
[self adddone];
}
//this loads the uitoolbar and changes its location so that the done button is displayed
//above the uipickerview
- (void)adddone{
UIBarButtonItem *done = [[[UIBarButtonItem alloc]
initWithTitle:@"Done"
style:UIBarButtonItemStyleDone
target:self action:@selector(done:)] autorelease];
UIBarButtonItem *flex = [[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
target:self action:nil] autorelease];
self.navigationController.toolbar.barStyle=UIBarStyleBlackTranslucent;
[self.navigationController.toolbar setFrame:CGRectMake(0,self.parentViewController.view.window.frame.size.height-(216+40), 320, 40)];
self.navigationController.toolbar.hidden = NO;
self.toolbarItems = [[[NSArray alloc] initWithObjects:flex, done, nil] autorelease];
}
//creates the actual picker
- (void)pickerwith:(NSString*)string withId:(int)tag withdefault:(NSString*)defaultstring{
//loads the array from a plist file
NSString *bPath = [[NSBundle mainBundle] resourcePath];
NSString *plistFile = [bPath stringByAppendingPathComponent:@"Root.plist"];
NSDictionary *settingsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFile];
selectionarray = [settingsDictionary objectForKey:string];
[selectionarray retain];
//if picker is not nil close a previous picker
if(picker != nil){
[self done:self];
}
//create picker
picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, self.parentViewController.view.window.frame.size.height-216, 320, 216)];
picker.delegate=self;
picker.dataSource=self;
picker.showsSelectionIndicator=YES;
picker.autoresizingMask = UIViewAutoresizingFlexibleWidth;
//if defaultstring is defined load the picker at that index.
if(defaultstring != nil ){
NSInteger ind = [selectionarray indexOfObject:defaultstring];
[picker selectRow:ind inComponent:0 animated:NO];
}
//since we are inside a tableview disable scrolling
//better response from picker
self.tableView.scrollEnabled=NO;
picker.tag = tag;
[self.view.window addSubview:picker];
}
//remove picker and toolbar should put toolbar back in place if needed.
//reload data
- (void)done:(id)sender{
self.navigationController.toolbar.hidden = YES;
self.toolbarItems = nil;
[picker removeFromSuperview];
[picker release];
picker=nil;
self.tableView.scrollEnabled=NO;
[self.tableView reloadData];
}
As for the UIPickerView delegates I am not going to cover as you can look that up elsewhere.
A better approach to this problem would be to use a uiview subclass, and put a uitextlabel on the left side and a uiimageview on the right side, do the same as above with the loading of the pictures and adding this view as a subview to the parent view.
Once you do that you have the combobox look without any functionality to do that you have two approaches, the first is implement touchesdidbegin and touchdidend, I could be wrong on function names this is more for future proofing, then use this to determine if your uiview has been touch properly and load up the picker as above. Another apporach is to implement the uiview subclass a a UIControl subclass which allows you to use the following:
-(void) addtarget:self withselector:action forEvent:UIEventType
this makes it a uibutton basicly and its a lot simpler to work around. Once you have it loading up the Picker all you have to do is in the didSelectRow delegate method for the UIPickerView is tell the UITextLabel to show the text currently selected.
I hope this was convient, and informational. The last thing I have to say is for further improvement the next view button should also be implemented.
Thursday, August 13, 2009
Notes on Android SDK:PreferenceActivity, XML Preferences and using them
Well the android has a similiar functionality, that creates a setting page quickly. The beauty of it is that you can load it into your app quite easily. There is some documentation on this widely available, the following website provides a great tutorial on setting up the ui.
http://androidguys.com
the article in question is What's Your Preference in three parts.
The one thing that it doesn't cover is how to use the application settings throughout the app. Anyway asuming you are using the above tutorial to set up user preferences.
the folllowing line of code will recover the settings.
SharedPreferences mprefs = PreferenceManager.getDefaultSharedPreferences(this);
and then you can read them like so.
CharSequence[] names = getResources().getTextArray(R.array.questiontypes);
for(int i=0; i
Log.v(TAG, mprefs.getBoolean(names[i].toString(), true) + "");
}
if you want to alter them from somewhere other than the settings page. you need
a SharedPreferences.editor from the above. Then just do some thing like the following.
meditprefs.putInt(key,value);
meditprefs.commit()
//the above is very important it savs until this line is called and changes are ignored
its like
[[NSUserDefaults standardUserDefaults] synchronize];
Wednesday, August 12, 2009
Notes on Android SDK: TabHost, Tabs and Activities
the first one is on tabhost, which allows you to switch between different views easily with the tab being pressed, for iPhone Developers its the equiavelent of TabBar Controller, but requires more work to get going specifically if you set it up via the xml view.
import android.widget.TabHost.TabSpec;
import android.widget.TabHost;
import android.app.TabActivity;
//first note is if you want to use a TabHost extend TabActivity instead of Activity
public class Maestro extends TabActivity {
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
addTabs();
}
private void addTabs(){
Resources resources = getResources();
//since your using the TabActivity you can simply call this method to
//set up the tabhost make sure to call setup()
TabHost tabs = getTabHost();
tabs.setup();
//tabspec defines the attributes of the tab
TabSpec tab;
//this creates a new tabspec with an id of quiz
tab = tabs.newTabSpec("Quiz");
//this sets the intent/ activity of the tab when pressed
tab.setContent(new Intent(this, Quiz.class));
//this sets the name displayed and the image if you add one
//tab.setIndicator("Quiz");
tab.setIndicator("Quiz",resources.getDrawable(R.drawable.quiz));
//then you add the tab to the tab view
tabs.addTab(tab);
tab = tabs.newTabSpec("Practice");
tab.setContent(new Intent(this, Practice.class));
tab.setIndicator("Practice",resources.getDrawable(R.drawable.practice));
tabs.addTab(tab);
tab = tabs.newTabSpec("Settings");
tab.setContent(new Intent(this, Settings.class));
tab.setIndicator("Settings",resources.getDrawable(R.drawable.settings));
tabs.addTab(tab);
//sets the current tab
tabs.setCurrentTab(0);
//add the tabhost to the main content view
setContentView(tabs);
}
now of course you need to make sure that the classes you have set above to be invoked by the tabhost are all activities of some sort depending on your application, and make sure you add them as activites in your Application Manifest.
this is simple just add the following xml to your manifest file. where Settings is the name of your classes.
\>activity label="Settings" name="Settings">
\>\activity>
Now the last thing to note is that these are the basic of using the tabhost, using the above methods you can easily setup a tab bar and get your other views up and running, now if you want to use xml to set up tabhost there are strict requirements in setting it up.
here is what you need for the tabhost via xml
you need a tabhost
with a tabwidget
with a framelayout
according to sources there seems to be a naming convention as well, but in my opinion its too much of a hassle especially if your app only needs the tabhost as the main controller.
Next instead of using the tabhost lets use the menu list that you get when you press menu. The benefits of this is you get more room in your app. this is quite simple with a few options.
well first to get an options menu use the following.
public boolean onCreateOptionsMenu(Menu menu){
//add(groupid, itemid, order, itemname)
menu.add(0, 1, 0, "Quiz").setIcon(R.drawable.quiz);
menu.add(0, 2, 0, "Settings").setIcon(R.drawable.settings);
menu.add(0, 3, 0, "Practice").setIcon(R.drawable.practice);
return true;
}
public boolean onOptionsItemSelected(MenuItem item){
switch(item.getid()){
case 1:{
startactivity(new Intent(this,Quiz.class));
return true;
}
case 2:{
startactivity(new Intent(this,Settings.class));
return true;
}
case 3:{
startactivity(new Intent(this,Practice.class));
return true;
}
}
return false;
}
now that is the simple way that simply starts a new activity when you click on one of the menu items. Now to get back you click on the back button on the phone. As you can imagine that is annoying so you have to thinking about the design of app before you consider something like this. For instance if you want to bring up a settings page the above would be nice as you can get rid of a button from the main ui. then simply press back after changing your settings.
But if your going to try and use this as a navigation controller like a tab bar then make sure you add the following after each start activity.
finsh();
that closes the current activity before going to the next one. this gets rid of the tabhost and give you the same benefits, also by doing this the menu is dynamic unlike the tabhost. I mean why would you allow a user to choose the quiz when you are in the quiz already.
Notes on Android SDK: Dynamic TableLayouts, adding Views and TableRows
now the code to adding something to a TableLayout is quite simple.
//set the content view from an XML file
setContentView(R.layout.main);
//first get the TableLayOut from the xml file that you previous loaded
TableLayout table = (TableLayout) findViewById(R.id.TableLayout01);
//set the columns to resize so that the row gets the full length of the screen
table.setColumnStretchable(0, true);
table.setColumnCollapsed(1, true);
//useful for getting a resource by name
int resID = getResources().getIdentifier("filename", "drawable", "com.yourcompany.yourApp");
//now create a view you want to add to the tableLayout
//in this case a ImageView
ImageView img = new ImageView(activity);
img.setImageResource(ResID);
//set the height to 100 and width to the width of the parent
img.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, 100));
//finally add to the tableLayout view
table.addView(img);
its quite simple, but now replace adding the ImageView to the Table with adding a TableRow to the Table which may have any number of views within. A TableRow is simply a LinearLayout.
//set the content view from an XML file
setContentView(R.layout.main);
//first get the TableLayOut from the xml file that you previous loaded
TableLayout table = (TableLayout) findViewById(R.id.TableLayout01);
//set the columns to resize so that the row gets the full length of the screen
table.setColumnStretchable(0, true);
table.setColumnCollapsed(1, true);
//useful for getting a resource by name
int resID = getResources().getIdentifier("filename", "drawable", "com.yourcompany.yourApp");
TableRow row = new TableRow(this);
//the tablerow is suppose to use this layout params always according to sdk guidelines
row.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT);
//now create a view you want to add to the tablerow
//in this case a ImageView
ImageView img = new ImageView(this);
img.setImageResource(ResID);
//set the height to 100 and width to the width of the parent
img.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, 100));
//add the ImageView to the row
row.addView(img);
//finally add to the tableLayout view
table.addView(row);
now if you have tried the above could nothing should come up, that's because certain views have their own LayoutParams class.
so what you need to do to make the above code work is change the following line.
row.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));
with
row.setLayoutParams( new TableRow.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
the basic thing is make sure to check your using the right LayoutParams with a given class, because if your not the platform won't know what to do with that layout.
Anyway though it was worthing mentioning cause it took me a while to figure it out.
Monday, August 10, 2009
Notes on iPhone:MapKit, CFNetwork, and Push Notifications Part 1
#import "UIKit/UIKit.h"
@interface FirstViewController : UIViewController {
IBOutlet UILabel *opened;
IBOutlet UILabel *removed;
IBOutlet UILabel *reinstalled;
}
@property (nonatomic,retain) IBOutlet UILabel *opened;
@property (nonatomic,retain) IBOutlet UILabel *removed;
@property (nonatomic,retain) IBOutlet UILabel *reinstalled;
@end
FirstViewController.m make sure to synthesize and dealloc
#import "FirstViewController.h"
@implementation FirstViewController
@synthesize opened;
@synthesize removed;
@synthesize reinstalled;
- (void)dealloc {
[opened dealloc];
[removed dealloc];
[reinstalled dealloc];
[super dealloc];
}
@end
#import "UIKit/UIKit.h"
#import "MapKit/MapKit.h"
@interface Map : UIViewController {
IBOutlet MKMapView *map;
}
@property (nonatomic,retain) IBOutlet MKMapView *map;
@end
#import "Map.h"
@implementation Map
@synthesize map;
- (void)viewDidLoad{
//enable location on the map
[mapview setShowsUserLocation:YES];
[super viewDidLoad];
}
- (void)dealloc {
[map dealloc];
[super dealloc];
}
@end
[[UIApplicatio sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge];
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken;
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err;
#import "AppTrackerAppDelegate.h"
@implementation AppTrackerAppDelegate
@synthesize window;
@synthesize tabBarController;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge];
[window addSubview:tabBarController.view];
}
- (void)sendProviderDeviceToken:(const void*)devToken{
}
//Delegate methods that tell you if you succeeded.
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
const void *devTokenBytes = [devToken bytes];
self.registered = YES;
[self sendProviderDeviceToken:devTokenBytes]; // custom method
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
NSLog(@"Error in registration. Error: %@", err);
}
@end
#include "CFNetwork/CFNetwork.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "arpa/inet.h"
#define PORT 2048
#define IPADDR "127.0.0.1"
//this is the callback method that gets called when
//client opens up the connection to the server
//if you were using more than one callback type you would need
//to check before continuing but since we only
//used the connect callback there was no need.
void ConnectCallBack(CFSocketRef socket, CFSocketCallBackType type,
CFDataRef address, const void *data, void *info){
//gets the native socket its a BSD socket so
//all normal BSD socket methods work
//like send you need to close the socket
CFSocketNativeHandle sock = CFSocketGetNative(socket);
send(sock, info, sizeof(info)+1, 0);
close(sock);
}
//this will open up a socket to our server
//we use the connectcallback to know when the socket actually connects
//once connected you need to have ConnectCallBack write the
//devToken to the server.
- (void)sendProviderDeviceToken:(const void*)devToken{
//socket reference and context, which get past around in the callback method
CFSocketRef sock;
CFSocketContext context = { 0, &devToken, NULL, NULL, NULL };
//open socket with default allocator its a TCP socket in ipv_4
sock = CFSocketCreate(NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketConnectCallBack, (CFSocketCallBack)ConnectCallBack, &context);
//if can't create socket fail to send devtoken
if(sock == NULL){
NSLog(@"error");
}
else {
//put the address of your server and port
//port define above as well as ip address
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = inet_addr(IPADDR);
//create reference and connect to server
CFDataRef connectAddr = CFDataCreate(NULL, (unsigned char *)&addr, sizeof(addr));
CFSocketConnectToAddress(sock, connectAddr, -1);
//run the socket through run loop,
//if connects callback method is called
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sock, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
CFRelease(sourceRef);
CFRunLoopRun();
}
}