アプリ開発は、ハイブリッド開発ではだめなのか
iOSとandroidアプリ作成時の対照表
iOS (Cocoa Touch framework on Xcode) |
android (android SDK, NDK on Android Studio) |
|
開発者 | Apple inc. | Alphabet inc. |
開発言語 | Modern Objective-C, C++, C, Swift*
*SwiftはJavaライクな言語なので、androidアプリしか開発できない人やCが苦手な人には入りやすいと思います。 |
Java, C, C++(JNI)
*C++のほうがJNIで作りやすい |
標準開発環境 (IDE) |
Xcode (Apple) | Android Studio (Google) |
コンパイラ/ビルドシステム | Apple LLVM | Gradle(makeみたいなもの) コンパイラは Javac と Eclipse Compiler for Java on JavaDevelopmentTools から選べる |
プロジェクトのフォルダ構成 | フォルダはどんな名称でどこに作ってもOK | Androidではプロジェクトのフォルダ構成が細かく決まっている
resファイルに属性を指定する方法 |
アプリ内課金 | inAppPurchase | inAppBuilling |
アプリストア | App Store | Google Play |
デベロッパー登録(アプリストアに掲載するために必要) | 年間登録料 99米国ドル( Apple Developer Program – Membership for one year 電話またはメールでのサポート2回付き |
登録料 25米国ドル(約2,500円+税) |
販売手数料 | アプリ価格、アプリ内課金価格の30% | アプリ価格、アプリ内課金価格の30% |
サブスクリプション(購読単位)アプリ内課金 | 1週間、1、2、3か月、6か月、1年 | 1か月、1年 |
アプリ販売申請からストア公開まで | 2日ほどの審査期間、問題なければ公開される(2016年後半から審査が早くなった) 1回だけ Expedited review(特急審査)をリクエストすることができる。 |
審査なし、1~2時間後に公開される |
ストア掲載申請時に必要な情報 | App StoreへiOSアプリ申請時に必要な掲載情報一覧 | GooglePlayへandroidアプリ申請時に必要な掲載情報一覧 |
アプリ設定 | Info.plist(Build設定に定義されたファイル名) 最初に起動する nibファイル(StoryBoard)を定義する |
AndroidManifest.xml 最初に起動するActivityを定義する IntentするActivityはここですべて定義しなければならない |
画面定義 | nib (.xib) storybord (.storyboard) |
layout/.xml |
国際化 | Localizable.strings 文字列 localize.xib 画面 localize.storyboard 画面群 localize.png 画像 *Xcodeの右ペインで国際化指定すると自動的にフォルダ分けされる |
res/values-ja/strings.xml 文字列 res/values-ja/arrays.xml 配列 res/layout-ja/layout.xml 画面 res/drawable-ja/loalize.png 画像 *国際化はリソースサブフォルダ名に国コードを付加したフォルダを作成する |
プロジェクトで使用する画像 | xxx.png
*どのフォルダに置いても良い |
drawable/xxx.png |
アプリ配布形式 | .ipa(appStore, adHoc, enterprise)
.xcarchive(Xcodeでインストール)
|
.apk |
アプリサイズ上限 | 3G4G接続でダウンロード上限:100MB WiFi接続でダウンロード上限:4GB |
android4.0以上 3G4G接続でダウンロード上限:200MB(内apk本体100MB) WiFi接続推奨のダウンロード上限:4GB+100MB(拡張APKアセット) android4.0未満 3G4G接続でダウンロード上限:150MB(内apk本体50MB) |
ファイルタイプ | .m .mm .cpp .c |
.java .cpp (JNI) .c (JNI) |
デバイス画面サイズ(dp) | 画面フラグメント:4
*AutoLayout(SizeClass)という開発し易い仕組みを導入し、画面dpを統一せずフラグメントさせることを選んだApple。 *フルHDで再生表示できるのはiPhone(6(S),7,8)PlusとiPhoneXとiPadのみ |
画面フラグメント:1
* 画面dpを統一し開発者がアプリ開発するのを容易にすることを選んだGoogle。 * 格安スマホ以外はすべてフルHDに対応した。 |
iPhone *320×480(dp:320×480)3.5インチ (iPhone, iPhone3GS) *以上は現在では使用されていない 640×960 (dp:320×480) 3.5インチretina (iPhone4, iPhone4S) *以降は16:9の画面 750×1334 (dp:375×667) 4.7インチretina (iPhone6, iPhone6S, iPhone7, iPhone8) 1242×2208 (dp:414×736) 5.5インチretina@3x (iPhone6Plus, iPhone6SPlus, iPhone7Plus, iPhone8Plus) *以降、16:9ではない |
Phone *mdpi 320×480 *hdpi 480×854 (dp:320×569) 4.3インチ *以上は現在では使用されていない *以降は16:9の画面 xhdpi 720×1280 (dp:360×640) HD 4.3インチ~4.6インチ xxhdpi 1080×1920 (dp:360×640) FullHD 5インチ~5.4インチ xxxhdpi 1440×2560 (dp:360×640) WQHD 5.5インチ *QuadのほうのQHD |
|
iPad 768×1024 (dp:768×1024) 10インチ (iPad, iPad2), 8インチ(iPadmini) 1536×2048 (dp:768×1024) 10インチretina (iPad(air)), 8インチretina(iPadmini), 9.7インチ (iPad Pro) 1668×2224 (dp:556×1112) 10.5インチ(iPad Pro) 2048×2732 (dp:1024×1366) 12.9インチ(iPad Pro) *3:4の画面比率(12.9インチは疑似) |
Tablet hdpi 800×1280 (dp:600×960) HD+ 7インチ~8インチ xhdpi 1200×1920 (dp:600×960) FullHD+ 7インチ~10インチ xhdpi 1600×2560 (dp:800×1280) FullHD+ 10インチ |
|
デバイス機能 | iPhone (2007) 登場時は期待していなかったが、私の人生を変えたデバイスとなった。 iPhone3G (2008) iPhone4 (2010) iPhone 5 (2012) iPhone 5S (2013) iPhone 6, 6plus (2014) iPhone 6S (2015)
iPhone 7 (2016) iPad Pro (2015) iPhone X (2017) *スマホで唯一前面ハードウェアボタンが残っていたiPhoneからついにボタンが消えた。
|
機種によりまちまちで統一されていない *プロセッサも統一されていないので、ネイティブプログラムを動作させるには4通りのネイティブコード(バイナリ)をアプリに実装しなければならない。 HTC Dream (2008) Nexus One (2010) 前面ハードウェアボタンはほとんどの機種で実装していない (2015)
android5からほぼiPhoneと使いやすさが並んだと言ってよい。 |
市場シェアデータ
|
||
世界シェア (途上国の影響が大きいため、実際アプリを積極的に使う人がどれだけいるか疑問) |
40% | 60%
*途上国では安いandroidが使われているため。 |
日本国内シェア *ここ数年はずっとこんな感じ |
60%
*日本は世界シェアとは逆にiPhoneシェアが6割を超えている。 |
40% |
国内メーカ別シェア(スマホ、2015年) | Apple iPhoneシリーズ 50% Sony Xperiaシリーズ 15% Sharp AquosPhoneシリーズ 10% |
|
国内出荷台数 (スマホ、2015年) |
3,650万台
出典:MM総研調査 |
|
対応すべきバージョン (2017年6月現在) |
X iOS3 (2009) 0% X iOS4 (2010) 0% X iOS5 (2011) 0% *iCloud導入、互換性ない X iOS6 (2012) 1%未満 *画面デザイン変わった X iOS7 (2013) 1%未満 *AutoLayout SizeClass BackGroundFetch導入 X iOS8 (2014) 2%未満 *モーション・コプロセッサ連携Health Care導入(iPhone5S以降) ○iOS9 (2015) ◎iOS10 (2016) ○iOS11 (2017) *機械学習AIプロセッサ(iPhoneX以降) 出典 developer.apple.com/support/appstore/ *iOSは新OS配布開始から3ヶ月で導入率7割になるが、ここ最近は配布初期のOS不具合が頻繁に発生することから、徐々にフラグメントが広がりつつある。 *iOSのバージョンによるフラグメントは3つだが、画面サイズのフラグメントが4つもある。 *現状ではandroidよりiOSのフラグメントが高く開発しにくい。しかしメーカによるフラグメントは全くない。 |
X android2 (2010) 0% X android3 (2011) 0% ○android4 (2011-2013) 23% ○android5 (2014-2015) 29% *ようやくまともなOSになったバージョン ◎android6 (2015) 32% ○android7 (2016) 16% X android8 (2017) 0%
*日本国内に限定するとandroid6が40%、4、5、7が20%づつのシェア <詳細> 出典 developer.android.com/about/dashboards/ *androidはお菓子の名前の愛称が読みにくいし、APIとバージョンが1対1でないので分かりにくい *一般的な用途のアプリの場合は、API 16以降に対応する必要がある。 *実にAPI別で5フラグメント、バージョン別で4フラグメントしている。 *画面サイズのフラグメントはない。 *iOSに比べandroidのほうが開発しやすい状態なのだが、メーカによる隠れたフラグメントが大きい。 |
スプラッシュスクリーン | Storyboardで定義する場合 LaunchScreen.storyboard でiPhone、iPadのイメージを指定する 画像ファイルで定義する場合 iPad イメージ画像をプロジェクトフォルダに置くだけ 画像サイズ |
res/values/style.xml (AppTheme) res/drawable/splash.xml res/drawable/splash_image.png を作って MainActivityのonCreateメソッドでsuper呼んだ直後に setTheme(R.style.AppTheme);する 画像サイズ |
アプリケーションデリゲート(メインアクティビティ) | AppDelegate *main()関数中のUIApplicationMain()で定義したAppDelegateクラス |
MainActivity(最初に起動されるActivity) *Androidmanifest.xmlで intent-filter android.intent.action.MAIN intent-filter android.intent.category.LAUNCHER で指定されたactivityクラス |
最初に実行されるviewController (Activity) | plistで指定したMainStoryboardで Is Initial ViewController をチェックしたViewController
Storyboardを使わないとき |
Androidmanifestで intent-filter android.intent.action.MAIN intent-filter android.intent.category.LAUNCHER で指定されたactivityクラス |
provider | Androidmanifestに宣言したproviderクラスは最初に起動されるActivityよりも前にonCreateされる。 | |
ビューコントローラー(アクティビティ、フラグメント) | ViewController | Activity Fragments *ActivityGroupがdeprecateされ、Activity + Fragmentsで複数のアクティビティを実現する。 |
ビュー | UIView (EagleViewなどに継承されて使用) |
View (GLSurfaceViewなどに継承されて使用) widget |
viewController (Activity)に表示するview指定方法 | nibでviewを定義する or viewControllerのviewDidLoadメソッドでviewをalloc initしてaddSubviewする |
setContentView(R.layout.xmlFileName); |
インスタンス生成 (ビュー、ウィジェット、コントローラ、クラスなど) |
ViewClassName *obj = [[ViewClassName alloc] init];
UIButton view = [[UIButton alloc] initWithFrame:CGRectMake()]; nibファイルから生成 |
ViewClassName obj = new ViewClassName();
別の方法 Activity#findViewById, View#findViewById 次の2通りのView生成方法は全く同じこと LinearLayout view = (LinearLayout)getLayoutInflator().inflate(R.layout.viewName, null); |
C++ | // スタック上に生成(メソッド内でのみ有効) ViewClassName obj; // ヒープ上に生成 ViewClassName *obj = new ViewClassName(); |
Javaではヒープ上にインスタンスが生成される |
(Sub)View表示 | [view addSubView:subView]; | ViewGroup#addView(subView); *ViewGroup(LayoutView)だけaddViewできる Activity#addContentView() |
(Sub)Viewを非表示にする | [subView removeFromSuperview];
*Viewのvisible属性をfalseしてもOK |
ViewGroup viewParent = (ViewGroup)this.subView.getParent(); viewParent.removeView(this.subView); *Viewのvisible属性をfalseしてもOK |
クラス名は大文字始まり | クラス名は大文字始まり | |
変数名は小文字始まり | 変数名は小文字始まり | |
フレームワークで定義されているクラス名 | cocoaフレームワークで定義されているクラス名はプレフィックスがついていて何に使うクラスなのかが一目瞭然(ex. UIView, NSData) | Android SDKで定義されているクラス名にプレフィックスが全くないので調べないと何に使うクラスなのかわからない(ex. View, Data) |
リソースファイル名 | パソコンで使えるファイル名ならOK | 小文字と数字だけ。大文字が使えない |
[UIColor whiteColor] | Color.WHITE 又は R.color.white |
|
文字定数の国際化 | NSLocalizedString(@”name”, @”comment”)
定数はLocalizable.strings(Localization)で定義する |
getResources().getString(R.string.name)
定数は |
@“文字定数” | “文字定数” | |
nibでもコードでも画面作れる | xmlでもコードでも画面作れる | |
application:didFinishLaunchingWithOptions: | onCreate() | |
メソッドスコープ | .hの@interface class に定義 | public type methodName() |
.mの@interface class (private)に定義 | private type methodName() | |
変数スコープ | .hの@interface className にプロパティ定義 @property (assign, readwrite) type varName; これによりセッター、ゲッター、インスタンス変数(_varName)を生成する |
public type varName; |
.mの@implementation className {}内にインスタンス変数定義 type _varName; |
private type varName; | |
C++ | public: private: |
|
クラスメソッド | + (type)methodName | public static type methodName() |
インスタンスメソッド | – (type)methodName | private type methodName() |
C++ | クラスメソッド static type methodName() インスタンスメソッド type methodName() |
|
[instanceVariable methodName] | instanceVariable.methodName() | |
self.instanceVariable | this.instanceVariable *this.はつけてもつけないくても同じ意味 |
|
[array objectAtIndex:0] | array.get(0) | |
NSInteger, int | int | |
IBOutlet *view or [topView viewWithTag:] | findViewById() | |
定数 | const type constName = value;
#define constName value |
static final (定数、継承しないメソッド、クラス) |
@interface subClass : superClass { | class subClass extends superClass { | |
#import importFileName | import packageName.className (importするのはフレームワークのみ/自動) |
|
クラス名はプロジェクトで一意 | .javaでpackage名を定義すると同じクラス名でもOK、別スコープとなる | |
ライフサイクル | AppDelegateとviewControllerライフサイクル | ActivityとFragmentライフサイクル |
didFinishLaunchingWithOptions(appDelegate) | onCreate | |
applicationDidBecomeActive(appDelegate) | onStart | |
applicationWillEnterForground(appDelegate) | onResume | |
applicationDidEnterBackground(appDelegate) | onPause | |
applicationWillTerminate(appDelegate) | onStop | |
applicationDidBecomeActive(appDelegate) | onRestart | |
dealloc | onDestroy | |
バンドル情報(パッケージ情報)取得 | productName-Info.plist で定義した値
[[NSBundle mainBundle] infoDictionary] |
AndroidManifest.xml で定義した値
PackageInfo |
keyValue persistentストア(アプリ内共通preference)に保存・読み出し | preferenceファイルは1つ
書込み 読み出し *設定アプリのアプリ設定情報読み出しも同じ方法(NSUserDefaults) |
書込み SharedPreferences settings = getSharedPreferences(“prefName”, 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean(“key”, value); editor.commit(); 読み出し *アプリのSetting(設定情報)は PreferenceManager で読み出す |
スマートフォン設定情報取得(ネットワーク接続) | [[UIDevice currentDevice] networkAvailable]; | ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
ConnectivityManager#getActiveNetworkInfo(); |
画面の向きを固定する | plistのUISupportedInterfaceOrientations | AndroidManifestのscreenOrientation |
アプリ設定情報(Setting)の取得 *iOSは設定アプリ、androidはアプリ内のActionBarButtonから設定した値 |
設定アプリに設定項目を設定する Settings.bundle (Root.plist, Root.strings) に設定項目(key)を定義する NSInteger value = [[NSUserDefaults standardUserDefaults] objectForKey:keyName]; |
ActionBarButton(アプリ内のOptionMenu)表示用Activityを設定する startActivity(new Intent(getBaseContext(), PreferenceActivity.class)); PreferenceManager.getDefaultSharedPreferences(this).getString(“key”, “defaultValue”); |
データベース | SQLiteが使える
CoreDataを使う FMDBなどのthird party libraryもある |
SQLiteが使える
ContentResolver cr = Activity#getContentResolver(); *Androidmanifest.xmlにContentProviderを定義しておく |
フェッチ機構 | NSFetchedResultController | LoaderManager CursorAdapter (SimpleCursorAdapter) |
DBインタフェース | NSManagedObjectContext | ContentResolver |
データ管理 | NSManagedObject | Cursor |
テーブルビューコントローラー(ListActivity)とデータベースの連携 | <UITableViewDelegate, NSFetchedResultControllerDelegate> を宣言して UITableViewController に NSFetchedResultController で取得した値をテーブルセルに-tableView:cellForRowAtIndexPath:で表示する ↓ NSManagedObjectContext ↓ NSEntityDescription ↓ CoreData(Data Modelでスキーマ定義 = SQLite) |
implements LoaderManager.LoaderCallbacks<Cursor> を宣言して ListActivity(or ListFragment) に CursorAdapter(Loaderを使うときCursorは定義しない=null) *リスト行表示レイアウト、取得するDBカラム、表示するレイアウトフィールドを指定する を連携させてリストに表示する *非同期でデータ取得する場合はCursorLoaderと連携する *配列のデータをリスト表示するときはArrayAdapter<T>を使う CursorLoader(Cursorを定義する) |
画面遷移(画面表示) | pushViewController, presentModalViewController | startActivity() |
viewController間の値の受け渡し | allocしたインスタンスのインスタンス変数に値をセットしてから表示(pushViewController etc.)する。
protocol定義のdelegateメソッド引数にセットして呼び出す。 |
Intentのdata,extrasにセットしてstartActivity()する。 |
画面閉じる(非表示) | dismissModalViewController dismissPopOver とか |
Activity#finish() |
Controller – NSManagedObjectContext – CoreDataModel | Controller -> Bean -> DTO <- DAO (sql)-> DB(Entity定義) *Bean:画面UIのためのデータを扱う DTO : テーブル操作のためのデータを扱う |
|
クラス定義(継承) | .h @interface className : superClass <protocolName> @property (retain, nonatomic) type publicVarName; – (type)publicMethodName:(type)paramA rabel:(type)paramB; @end |
.java public class className extends superClass implements interfaceName { accessLevel type vName; accessLevel type methodName() { … } } |
クラス実装 | .m @implementation className { type privateInstanceVarName; } – (type)methodName:(type)paramA rabel:(type)paramB { … } @end |
定義と実装は同じファイル(.java)で行う |
クラス規約定義 (クラスが持つべき型みたいなもの) |
@protocol protocolName <NSObject> | interface interfaceName |
C++ | .h クラス定義 class className : accessLevel super { public: type vName; private: type vName; type methodName(…); } .mm or .cpp メソッド実装 |
accessLevelはsuperクラスのpublicメンバの扱いを定義する |
コールバック | Delegate (デリゲート) | Interface (インタフェース) Listener (リスナー) |
デリゲート(リスナー)機能実装 | プロトコルを宣言して @interface className : superClass <UISearchBarDelegate> nibでデリゲートクラスを定義して クラスにメソッドを実装する |
インターフェースを宣言して public class className extends superClass implements SearchView.onQueryTextListener コールバッククラスを定義して クラスにメソッドを実装する |
インナークラスで実装することもできる *インターフェース宣言(implements)はしなくてよい *1箇所への記述だけでよいため手軽だが分かりにくくなる恐れあり SearchView.setOnQueryTextListener(new SearchView.onQueryTextListener() { |
||
クラスのインスタンスが持つ変数 | インスタンス変数 | プロパティ、フィールド、メンバ変数 などいろんな呼称がある |
インスタンス変数の初期化 | initとかのコンストラクタで行う | クラス定義で初期値を設定できる |
インスタンス変数使用方法 | _iVarName = someOne; // setterは走らない // retainカウント増えない self.iVarName = someOne; // setterが実行される // retainカウント増える |
iVarName = someOne; this.iVarName = someOne; // 全く同じ意味 |
ObjectiveCの@property指定方法 | 何も指定しないと、 クラス変数デフォルト(atomic, strong) プリミティブ変数(intなど)のデフォルト(atomic, assign) strong // このクラスで管理するインスタンス変数 weak // 他クラスで管理しているインスタンスを参照するためのインスタンス変数 nonatomic // 非スレッドセーフ(通常この指定) |
|
デバッグ時だけログ出力 | #ifdef DEBUG NSLog(“%f”, floatVal); #endif *通常appName_Prefix.pchでプリプロセッサ定義して利用する
|
|
クラス変数 | クラス変数なし *クラスメソッドを経由してクラス変数のように扱いたい変数を操作する |
static type variableName |
コンストラクタ | – (id)init – (id)initWithNibName: |
クラス名と同じ名前のメソッド public ClassName() |
デストラクタ | – (void)dealloc | Javaの言語としてのデストラクタはない
androidライフサイクルとしてのデストラクタ |
C++ | className::className() { コンストラクタ | |
className::~className() { デストラクタ | ||
定数(literal) | 0.1f | 0.1f |
[@”あ” stringByAppendingString:@”い”] | “あ” + “い” | |
変数(valiable) | aValiable | aVariable |
クラスのインスタンス変数 | ClassName *instance | ClassName instance |
アクセスレベル(スコープ)デフォルト | 継承クラス内(protected) | パッケージ内 |
C++ | クラス内(private) | |
アプリ間連携(カスタムURLスキーマ、カスタムアクション) | カスタムURLスキーマ <渡すほうのアプリ> [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@”myscheme://anystrings?param=1″]]; *URLのスキーマでどのアプリが受取るか判断する *パラメータも渡せる UIWebViewのアンカータグでも可能 <受取るほうのアプリ> info.plistのCFBundleURLNameにCFBundleIdentifier+scheme(com.company.app.scheme)を定義し、CFBundleURLTypesに扱うカスタムスキーマ(scheme)を定義する ドキュメントを受け渡し Pasteboard |
カスタムアクションで暗黙的Intent(外部インテント) を発行 <渡すほうのアプリ> Intent mailIntent=new Intent(Intent.ACTION_SEND); mailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{“adress@dom.com”}); mailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(filePath)); startActivity(mailIntent); <受取るほうのアプリ> manifestでactivityのintent-filterでカスタムアクションを定義する |
メール連携 | アプリ内からメールの編集送信ができる
MFMailComposeViewController |
アプリ内でのメール送信はできないのでメールソフトを起動する
暗黙的Intentを発行 |
SNS連携(Twitter, Facebook) | twitter iOS6以降 Social Framework iOS5 Twitter Framework facebook *twitter,facebookとも設定アプリからログインできる |
twitter Google+のためか、twitterからもGoogleからもsdkは出ていない サードパティのTwitter4Jライブラリを使ってtwitterWebAPIを利用する facebook |
サーバからアプリへのプッシュ通知(リモート通知) | RemoteNotification
証明書と鍵をサーバーに置く→アプリがデバイストークンを取得→アプリからデバイストークンをサーバに送信→サーバは(APNS)Apple Push Notification Serviceに通知をプッシュ→APNSからアプリ(デバイス)に通知送信 文字列で通知する 通知形式は設定アプリでユーザが決定する |
GCM(Google Cloud Messaging for Android)
ServerAPIKeyとProjectIDをサーバーに置く→アプリがRegistrationIDを取得→アプリからRegistrationIDをサーバに送信→サーバはGoogleAPIサーバに通知をプッシュ→GoogleAPIサーバからアプリ(デバイス)に通知送信→BroadcastReceiverクラスで受信してNotificationManagerで通知表示(ステータスバー表示のとき) json形式で通知する 通知形式はアプリが決定する |
ローカル通知(アプリがローカルデバイスに対して通知する) | UILocalNotification
通知はOSが受け取り表示する 文字列で通知する |
AlarmManager 指定時間後にブロードキャストインテント発行 PendingIntent BroadcastReceiver ブロードキャストインテント受信 NotificationManager ステイタスバーに通知を表示 PendingIntent(文字列)で通知する |
plist – CFBundleURLTypes, CFBundleDocumentTypes | AndroidManifest – intent-filter | |
UIGestureRegognizer | GestureDitector | |
@interface myView : UIView <UIGestureRecognizerDelegate> { | public class myView extends Activity implements OnGestureListener { | |
CoreGraphics | Canvas | |
CoreGraphics | 座標 (Coordinate)
左下が原点(変更可能)
座標系変更方法 // 原点のy座標を移動 ○:原点 CoreGraphics (CG**) ^ | | | ○——–>
UIKit (UI**)、UIView#drawRect:内 ○——–> | | | V * UIView#drawRect: 内では左上が原点になるようにあらかじめframeworkが座標系を変更している 回転方向 右回り * CGContextScaleCTM(context, 1.0, -1.0); // Y座標ベクトルを反転 で変更すると、左回りになる
画像(png, jpeg)のレンダリング CoreGraphics (CG**) 標準は左下が描画の原点なのでCGContextDrawImage() すると画像が逆立ちに描画される y座標描画方向を反転(CGContextScaleCTM(context, 1.0, -1.0))してから CGContextDrawImage() すると画像の右下から左上に向かって逆立ちに描画される 画像を回転するには CGSize imageSize = { image.size.height, image.size.width }; // 縦と横サイズを反転 UIGraphicsBeginImageContext(imageSize); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextScaleCTM(context, 1.0, -1.0); // y座標描画方向を反転 float radian = (180+90) * M_PI / 180; // 右に90度回転(実際には左に270度回転している) CGContextRotateCTM(context, radian); CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage); UIImage *rotatedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
*Quartz 2Dと呼ぶ場合があるが、これは描画エンジンの名称でまぎらわしい *UIView#drawRect: 内では左上が原点なので書籍によっては左上原点で説明されているものもある |
|
OpenGL | OpenGLES(C++) | OpenGLES(Java or C++(JNI, NativeActivity)) |
EAGLView(CAEAGLLayer) | GLSurfaceView (Java, JNI) | |
layoutSubview | onSurfaceChanged | |
シェーダとは(プログラム・シェーダの書き方) | ||
OpenGLES2.0の概念と捉え方 | NativeActivity (C++) | |
C++を実行する方法 | モジュールのファイルタイプを .mm にするだけ。 | Javaというフレームワーク(言語)はそのプログラム実行速度がとても遅いので、しばしばCやC++を使う必要性が生ずる。 androidアプリを作るためにC++を使うには、NDKとJNIの二つの仕組みを使う必要がありとても大変。 NativeActivityを利用するとC++を実行するのは簡単だが、C++からフレームワークメソッドを呼び出すにはJNIを使う必要があるので大変。
|
UI座標 | 左上が原点 | 左上が原点 |
CoreGraphicsは左下が原点(変更可能) | ||
OpenGLは左下or中央が原点 (変更可能) | ||
OpenGL関数 | GL10 gl.xxx | |
GL関数を呼び出す | GLSurfaceViewにわたされるGL10(GL20)インスタンスのラッパーメソッド(gl.openGlfunctionName)を呼び出す | |
glViewport() | gl.glViewport() * in onDrawFrame() | |
描画バッファにいつでも頂点を追加できる | onDrawメソッドで描画しなければならない(surfaceView使用時)*毎回全ての頂点を描画 | |
AndroidのsurfaceViewでsetDebugFlagsでデバッグモードにすると例外が発生してしまう | ||
配列 | int var[3]; | int[] array_var = new int[3]; int array_var[] = new int[3]; どっちでもよい。 |
ポインタ(参照、エイリアス、アドレスポインタ) | 基本データ型 int *pointer_var = address; オブジェクト |
基本データ型 int[] pointer_var = new int[1]; *メソッド引数へのポインタ渡し(参照渡し)に使う。 オブジェクト |
C(Objective-C)とJavaのポインタのコンセプトの違い ポインタとはコンピュータ(CPU)の仮想アドレス空間上のメモリアドレスをポイントする変数のことです。 Cのポインタ変数はメモリアドレスをメモリ上に保持することができるし、それを四則演算することもできる。 対して、Javaのポインタ(参照渡しされた変数)はメモリアドレスをメモリ上に保持していないし、それを四則演算することができない。 要するに、Javaは使う人にコンピュータハードウェアの知識を求めていないのです。コンピュータに詳しくない人でもプログラムが作れるようにと言うSun MicroSystemsの配慮です。 それ以外は、C言語のポインタとJavaの参照は全く同じ概念です。 |
||
ポインタ表現 | ポインタ *variable |
配列にする int[] pointer_var |
クラス配列の使い方 | [C++] CGPoint **var = new CGPoint*[count]; |
CGPoint var[] = new CGPoint[count]; var[i] = new CGPoint(); |
リソースファイル読み出し | glTexImage2D(), CGContextDrawImage |
R.idを指定するだけ |
Assetファイルのファイル・コピー | NSFileManager#copyItemAtURL を呼ぶだけ | InputStream#read して OutputStream#write するのを ファイルサイズ/bufferサイズ回数ループしなければファイルコピーひとつできない。Javaのメモリ管理の脆弱さが理由だと思われるが低水準すぎる。これがフレームワークと言えるのか? ANSI Cを思い出した。
*FileChannel#transferTo というメソッドもあるが、Assetファイル(AssetManager)などをパスに変換するメソッドが用意されていないため使えない。 |
UI操作はメインスレッドで行う |
|
or // メインスレッドを指定してHandlerを生成 AsyncTask#doInBackground() { // バックグランド |
非同期実行 | iOSはGCDのおかげでサブスレッド作成実行が簡単
or NSThread detachNewThreadSelector or UIView#performSelectorOnMainThread |
androidのサブスレッド作成実行手続きはiOSに比べて複雑
or AsyncTask を継承してインナークラスを作る or
or
|
非同期ネットワーク通信(XML取得など) | [NSURLRequest requestWithURL:NSURL];
[NSURLConnection initWithRequest:NSURLRequest delegate:]; delegateのメソッドで非同期受信する |
AsyncTask<String, Void, String>#execute(url);
java.net.URL#openConnection(); HttpURLConnection#connect(); AsyncTaskをextendsしたクラスをインナークラスで定義して、 |
リソースフォルダ | 好きな名称にしてよい | projectRoot/app/src/main/res/ *R.id でアクセスすることができる。 projectRoot/app/src/main/assets/
htmlファイルなどはres/rawでもassetsにも保存することができるが、アクセス方法が異なる。 |
画像フォーマット | png | png |
サウンドフォーマット | caf, mp3, wav | ogg, mp3, wav |
アプリアイコン(ランチャーアイコン)サイズ | Asset Catalog で指定
icon.png 72×72 *iPhone, iPad共通 |
48dp
mdpi:48dp(x1.0) *dp |
アプリ起動時に前回終了状態復元 | NSUserDefault に保存して、アプリ起動時に読み出す
UIStateRestoration(iOS6以降) |
SharedPreferences に保存して、アプリ起動時に読み出す |
画面回転時に行うこと | アプリのメモリ内の状態は保持されるので、何もしなくてよい。
UIView#layoutSubviews |
アプリのメモリ内の状態は保持されないので、保存復元をアプリで行う必要がある。
Activity#onSaveInstanceState(Bundle) { |
static変数(ヒープ領域にメモリ領域を確保する) | メソッド中に定義できる | クラスメンバ変数として定義する |
nill | null | |
UIEvent | MotionEvent | |
使用するクラスは #import または @class して使う | パッケージ内のクラスは定義する必要なく使える | |
bundleID | packageName | |
CGPoint | ポイント座標の構造体がない | |
touchBigan, Move, Endを別メソッドで受け取る | onTouchEventメソッドですべて受け取る | |
typedef | 型定義の機能がない | |
ポイント座標のY軸をgl->cocoa変換する | 変換しなくてよい(android座標で取得される) | |
bool | boolean | |
配列初期化 | type var[10]; *var = malloc(10*sizeof(type)) |
type[] var = new type[10]; |
NSFileManager | File (ContentWrapperを継承したクラスで使う) | |
エンディアン | little-endian | little-endian(nexsus7 native) x big-endian (JVMの使用で) |
オリエンテーション取得 | // デバイスの向き UIDeviceOrientation = [[UIDevice currentDevice] orientation]; // 描画されている画面の向き |
this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT |
オリエンテーションイベント取得 | UIViewController#viewWillTransitionToSize: withTransitionCoordinator:
iOS7まで |
manifestに Activity android:configChanges=”orientation|keyboardHidden”すると onConfigurationChanged()が呼ばれる |
座標とピクセル値対応 | scalefactor 1.0, 2.0(retina) |
getResources().getDisplayMetrics().density; mdpi(1.0),hdpi(1.5),xhdpi(2.0),xxhdpi(3.0) |
テーブル(リスト)表示管理 | UITableViewController | ListActivity ListFragment *テーブルUI+表示データの管理 |
セクション(グループ)数 | tableView:numberOfSectionInTableView: | CursorAdapter#getViewTypeCount() |
セクション・ヘッダー表示 | tableView:titleForHeaderInSection: | CursorAdapter#newView() |
テーブル(リスト)表示内容設定 | tableView:cellForRowAtIndexPath: | CursorAdapter#newView() CursorAdapter#bindView() |
ビューとウィジェット | UITabView | ActionBarActivity (旧TabHost, TabActivity) |
UIRadioButton | RadioButton | |
UITextView | TextView | |
CheckBox | ||
コレクション(配列) | NSArray *vName = @[]; // 空の配列 NSMutableArray *vName = [@[ @”val1″, @”val2″ ] mutablecopy]; // 配列の初期化 |
List<ObjectType> vName = new ArrayList<ObjectType>(); // 空の配列 List<ObjectType> vName = Arrays.asList(“value1”, “value2”, “value3”); // 配列の初期化 |
コレクション(連想配列) | NSDictionary *vName = @{}; // 空の連想配列
NSMutableDictionary *vName = [@{ @”key1″ : @”val1″, @”key1″ : @”val1″ } mutablecopy]; // 連想配列の初期化 |
|
画面構成デザイン | ||
ステイタスバー | ステイタスバー Status Bar | |
ホームボタン/ナビゲーションバー | ハードウェア シングル、ダブルタップ、長押しに対応 アプリ終了、タスク一覧、切り替え、音声入力 |
OSにより画面下部に自動表示される3つのアイコン アプリ終了、タスク一覧、切り替え *ソフトボタンならアプリで表示制御や無効にできる ナビゲーションバー Navigation Bar |
ナビゲーションバー/アクションバー
(メニューボタン) |
UToolBar, UINavigationBar
画面上部に表示する |
ActionBar
画面上部または画面下部を選べる オプションメニュー SDK10以下:画面下のメニューボタン押下で画面下にタイル表示される onCreateOptionsMenu() |
アプリ画面で使用する標準アイコンサイズ | 32dp (normal:32dpi, retina:64dpi) *iPhone, iPad共通 |
32dp(24dp) ldpi:19dpi(x0.75) mdpi:25dpi(x1.0) *dp hdpi:38dpi(x1.5) xhdpi:51dpi(x2.0) xxhdpi:96dpi(x3.0) *本来だと75dpiだがxxxhdpiと共用化しているため? xxxhdpi:96dpi(x4.0) *frameworkで実際に使っているアイコンサイズ |
ナビゲーションバーボタンはステータスごとにアイコンを定義できる(タップされたときなど) | アクションバーボタンをタップしたときにハイライトするなどはできない。背景色がかわるだけ。 | |
タブバー | 画面下に表示
UITabBarController |
画面上に表示 *画面下にはナビゲーションバーがあるためと思われる ActionBarActivity (旧TabHost, TabActivity) |
メソッド・オーバーライド | 普通にsuperClassやプロパティのメソッドを再定義すればOK | @Override public void superClassMethod() {} @Override public void otherClassMethod() {} |
いろいろ使うAppDelegateやMainActivityの求め方 | [[UIApplication sharedApplication] delegate]で求める | MainActivityのonCreateでGlobalクラスのクラス変数に保存しておく *所属するFragmentやView以外からは求めるしくみは用意されていない |
FragmentからActivityの求め方 | Fragment#getActivity(); | |
ViewからActivityの求め方 | View#getContext(); | |
変数型(データ型) | 変数型を見ただけで何に使われる変数かが分かる。 ex. UIDeviceOrientation orientation |
Java標準のデータ型しかない。 ex. int orientation *Javaにはプリプロセッサー(#define)やtypedefが無いため |
タッチイベント | viewのtouchesEnd: でイベントを受け取る | viewのpublic boolean onTouchEvent(MotionEvent event) { で手続きすると複雑
GestureDetector.SimpleOnGestureListener |
サウンド再生 | AVAudioPlayer | MediaPlayer 再生時に読み込む BGMなどに使用 SoundPool メモリ上に常駐 SEに使用 |
YES, NO | true, false | |
配列領域確保 | float var[4]; | float[] var = new float[4]; |
三角関数 | cos(); | (float)Math.cos(); |
クラウド | iCloud | GoogleDrive |
国際化(文字定数、画面レイアウト) | 文字定数を国際化するとき Localizable.stringsに値を定義して NSLocalizedString(@”key”, @”comment”)で呼び出す 画面ごとに国際化するとき |
文字定数を国際化するとき res/values-ja/strings.xmlに値を定義して getResources().getString(R.string.name)で呼び出す 画面ごとに国際化するとき |
機種判定 | UIViewController#UITraitCollectionで判定 if (self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular) { // iPad } else { // iPhone } iOS7以下 |
res/layout/activity_main.xml res/values/dimens.xml ↑と↓で画面サイズによって、レイアウトやパディング値を切り替える res/layout-w820dp/activity_main.xml res/values-w820dp/dimens.xml |
OSバージョンチェック | if (IOS_VERSION_GREATER_THAN_OR_EQUAL_TO(@”6.0″)) { // iOS6以上 } |
if (Build.VERSION.SDK_INIT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { // android 4以上 } |
文字列数値変換 | NSString *text = [[NSNumber numberWithInt:intVal] stringValue]; or NSString *text = [NSString stringWithFormat:@”%d”, intVal]; NSString *text = @”1″; |
String text = Integer.toString(intVal);
int intVal = Integer.parseInt(text); |
写真などの画像操作 | CGImage
CGDataProvider CGContent (UIGraphics) |
Bitmap
ByteBuffer (IntBuffer) Canvas |
続きはこちらからどうぞ!!
コメントはまだありません