ShapeDrawableのRoundRectShapeとGradientDrawableをそれぞれ使って角丸ボタン作ってみた
ShapeDrawableのRoundRectShape、あるいは、GradientDrawableを使うとコードからフチの余白や色を指定できる形で角丸四角形の背景を持つボタンが作ることができます。
もともとShapeDrawable
で書いていたのですが、こちらの記事でShapeDrawable
のリソースをGradientDrawable
にコンパイルされていることを知り、GradientDrawable
で書いてみたらスッキリ記述できたので書き直した、という話です。
どちらにせよ、リソースファイルから読み込んだほうが基本的に高速なので、あまり表示頻度が多くない箇所をカスタマイズして表示したいとか、そういった場合に使えば良いと思います。
ShapeDrawableのRoundRectShapeで作る場合
同じ指定をxmlで記述した場合
今回はxmlで記述した場合は下記のようになる指定をコードで実装することにします。
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <padding android:left="@dimen/button_padding_horizontal" /> <padding android:top="@dimen/button_padding_vertical" /> <padding android:right="@dimen/button_padding_horizontal" /> <padding android:bottom="@dimen/button_padding_vertical" /> <!-- 上記の指定はButton要素に下記属性を追加するのと同じ結果となる。 android:paddingLeft="@dimen/button_padding_horizontal" android:paddingTop="@dimen/button_padding_vertical" android:paddingRight="@dimen/button_padding_horizontal" android:paddingBottom="@dimen/button_padding_vertical" --> <!-- デフォルトのボタンではButton要素は paddingを設定しなくてもButton要素に余白を持っているが、それは android.R.drawable.btn_default_normal.xmlなどで paddingが指定されているため --> <solid android:color="#f00"/> <corners android:radius="@dimen/button_round_radius"/> </shape>
RoundRectShapeを利用してコードで記述した場合
Button button = (Button) findViewById(R.id.button_bg_code); // 角丸の設定 int roundRadius = getResources().getDimensionPixelSize(R.dimen.button_round_radius); ShapeDrawable shape = new ShapeDrawable(); shape.setShape(new RoundRectShape(new float[] {roundRadius, roundRadius, roundRadius, roundRadius, roundRadius, roundRadius, roundRadius, roundRadius}, null, // 背景のうち、真ん中の方をくりぬいた形にすることができるのですが、その領域の大きさをRectFで指定可能. 利用しない(=一色塗りつぶしにする)場合はnull null)); // 背景のうち、くりぬいた部分の長方形の角丸指定. 利用しない(=くりぬいた部分は長方形のまま)場合はnull // 余白の設定 int horizontalPadding = getResources().getDimensionPixelSize(R.dimen.button_padding_horizontal); int verticalPadding = getResources().getDimensionPixelSize(R.dimen.button_padding_vertical); shape.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding); shape.getPaint().setColor(Color.RED); if (BuildConfig.VERSION_CODE >= 16) { button.setBackground(shape); } else { button.setBackgroundDrawable(shape); }
<!-- レイアウト --> <Button android:id="@+id/button_bg_resource" android:layout_below="@id/some_element" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/test" android:layout_marginTop="8dp" <!-- 見やすくするためつけてる --> android:text="TestTestTestTest" /> <Button android:id="@+id/button_bg_code" android:layout_below="@id/button_bg_resource" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" <!-- 見やすくするためつけてる --> android:text="TestTestTestTest" />
<!-- dimenリソースの設定 --> <resources> <dimen name="button_round_radius">8dp</dimen> <!-- この辺の元ネタはこちら https://github.com/android/platform_frameworks_base/blob/c29fff50322599f53feadf9cf87df9956c9ac44e/core/res/res/values/dimens_material.xml#L109-L116 --> <dimen name="button_padding_horizontal">8dp</dimen> <dimen name="button_padding_vertical">4dp</dimen> </resources>
見た目はこんな感じです。上がdrawableリソースで背景を指定したボタンで下がコードで背景を指定したボタンになります。
GradientDrawableで作る場合
コードでの記述
Button button = (Button) findViewById(R.id.button_bg_code); // 角丸の設定 int roundRadius = getResources().getDimensionPixelSize(R.dimen.button_round_radius); GradientDrawable gradientDrawable = new GradientDrawable(); gradientDrawable.setCornerRadius(roundRadius); gradientDrawable.setColor(Color.RED); // 余白の設定はButton要素の属性でやる if (BuildConfig.VERSION_CODE >= 16) { button.setBackground(gradientDrawable); } else { button.setBackgroundDrawable(gradientDrawable); }
<!-- レイアウト --> <Button android:id="@+id/button_bg_resource" android:layout_below="@id/some_element" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/test" android:layout_marginTop="8dp" <!-- 見やすくするためつけてる --> android:text="TestTestTestTest" /> <Button android:id="@+id/button_bg_code" android:layout_below="@id/button_bg_resource" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="@dimen/button_padding_horizontal" <!-- GradientDrawableの場合はpaddingを設定するメソッドはないので、 View側にpaddingの指定を置く --> android:paddingTop="@dimen/button_padding_vertical" android:paddingRight="@dimen/button_padding_horizontal" android:paddingBottom="@dimen/button_padding_vertical" android:layout_marginTop="8dp" <!-- 見やすくするためつけてる --> android:text="TestTestTestTest" />
見た目は変わってません。
参考
- https://developer.android.com/reference/android/graphics/drawable/ShapeDrawable.html
- http://qiita.com/guchio/items/bebc13db0d2c0ae03363
- https://developer.android.com/guide/topics/resources/drawable-resource.html#Shape
- https://developer.android.com/reference/android/graphics/drawable/GradientDrawable.html
AndroidのNDKやABIについてのメモ
はじめに
C系の言語で動いているクロスプラットフォームのブリッジを作成するには、そのコンパイルなどの過程でNDKに関する知識があるとトラブルシューティングやテストケースの設計に役立つことがあります。
なので、ここではその概要をまとめておきます。はじめに用語からまとめようと思ったのですが、
https://developer.android.com/ndk/guides/index.html?hl=ja
Native Development Kit(NDK)は、Android アプリで C および C++ コードを使用できるようにするツールのセットのことです。NDK を使用して独自のソースコードをビルドしたり、事前にビルドされた既存のライブラリを活用したりできます。
https://developer.android.com/ndk/guides/concepts.html?hl=ja
アプリケーション バイナリ インターフェース(ABI)はアプリのマシンコードが実行時にどのようにシステムと連携するかを正確に定義したものです。 NDK はこれらの定義に対して
.so
ファイルをビルドします。
と言われてもよくわからなかったので、NDKがABIを使ってC, C++のソースコードから.so
ファイルをビルドする周辺で何をしているかを説明します。
.soファイルとは
と書いたものの、書いた本人は.soファイルについての事前知識が調査開始時点でなかったため、簡単に説明します。
so
はshared object file
の略です。
.so
ファイルは、UNIX系の環境下での共通ライブラリファイルのことで、Windowsでいうdllにあたります。具体的には、複数の実行ファイル間で共有して利用される処理の定義がまとめられています。
中身は実行形式ファイルですが、あくまで処理を含んでいるだけでエントリポイントがなく単体で起動することはできません。
各々の実行ファイルに含まれず、実行ファイルを起動した時に動的にロードとリンクが行われるため、動的(ダイナミック)リンクライブラリと呼ばれることがあり、Android Developers GuideのNDKについての記述では主に動的リンクライブラリ、と呼称されています。
Androidでは、アプリのコンパイル時にリンクされている方が.a
(静的リンクライブラリ), アプリの起動時にロードされてリンクされる方が.so
で、NDKを使ってABIに従って作成される方が基本的に.so
という感じです。
自分が作成したライブラリが.so
か、.a
になるかはコンパイルの設定によりますが、Androidアプリのビルドの文脈だと、.so
ファイルの方しか、公式のドキュメントに記載がなかったので、こちらが多いかもしれません。
NDKを利用してビルドする際の流れ
https://developer.android.com/ndk/guides/concepts.html のフローをもとに適宜用語の説明を入れながら説明します。
- Androidプロジェクトを作成します
- Androidプロジェクト直下にjniディレクトリを作成し、ネイティブライブラリ(.aファイル, .soファイルなど.アーキテクチャごとにフォルダを用意したり…)とコンパイル対象のソースコード(CやC言語で書かれたソースコード)とそれらをコンパイルするモジュールにどう含むか記述していくAndroid.mkを配置
- 2のディレクトリに任意でターゲットABI, ツールチェーン, リリース/デバッグモード, STLを設定するApplication.mkファイルを作成(任意)
- デフォルト設定
- ABI: armeabi
- ツールチェーン: GCC 4.8(4.8 32bit / 4.9 64bit端末対応らしい)
- モード: リリース
- リリースモードだと変数宣言が最適化されていたりして、C言語側のステップ実行が困難になったりします
- STL: system
- NDKツールを適切に利用するために作成されたシェルのラッパーであるndk-buildを使用して、Cのソースコードをネイティブ(.so, .a. 主に.so?)ライブラリへコンパイルしたり、静的ライブラリをリンクしたり
- Javaのソースコードをビルドして、DVMで実行可能な.dexファイルを生成
- アプリの実行に必要な .so, .dex、その他ファイルをすべてapkファイルにパッケージ化
ところで
.soファイルやネイティブライブラリと.dexを一緒に処理していませんが、JNI用のヘッダとそれに対応した規則のメソッド名で作成していけば、JVM(DVM)上でメソッドテーブルが作成されると理解していて、それにしたがってJava<=>C,C++間でメソッドを利用し合うことが可能みたいです。
この辺を調べていると、CMakeといった言葉もでてきますが、ひとまずこの記事を書くにあたっての状況に限っては(Andorid Studioでビルドをしない場合は)関係なかったので省きます。
CMakeについては、また今度機会あればまとめます。
参考
https://developer.android.com/ndk/guides/concepts.html http://blog.katty.in/4347 http://www.peliphilo.net/archives/681 http://qiita.com/m1takahashi/items/3a3c9d2845e9b57aeda3 http://morado106.blog106.fc2.com/blog-entry-80.html
COUNT関数の引数にNULLが入る場合について
SQLで行数を数える時に利用する COUNT
関数で一番使い慣れているのは COUNT(*)
なのですが、 COUNT(col)
や、COUNT(1かNULLになる式)
といった形を見かけたのでメモ。
COUNT(*)
は行数をカウントするCOUNT(col)
はcolの値がNULLの行はカウントしない- resfs: http://nippondanji.blogspot.jp/2010/03/innodbcount.html?m=1
- フラグが立っているかどうかでカウントする条件を分ける、みたいなことをしたい場合はわかりやすさのために
COUNT(1かNULLになる式)
といった形で書くのかも
見かけた時は面白いなと思ったのに短い。。
iOSのProvisioning Profile周りについてざっくり確認するシートを作りました
iOSのProvisioning Profile周りについて毎回ハマっているような気がしていたので、どのファイルをいつ用意して用意した後どうすればいいのか、といったことを手短に確認するために
- 主になんて呼ばれているか
- 代表的なファイル名
- 主にいつ作成するか、どう作成するか
- ファイルをDLするか、DLした後どうするか
- XCode上でそのファイルに関連してどう設定するか
- そのほか備考
をまとめて、作成する際に必要、など関連のあるところの線を引いた図を作ったので貼ります。
上の図に千鳥足みたいなのありますが、一つのファイルに対して他方のファイルが複数存在するといった場合は複数存在する方が雑に鳥足みたいにしました。 なんかそういうツールや様式があった気がしますが、それは次の機会ということで。
参考
https://i-app-tec.com/ios/provisioning-profile.html http://macdays.hatenablog.com/entry/2013/10/11/172532 http://qiita.com/edo_m18/items/6f10e57f95b25d9dab4e http://qiita.com/fujisan3/items/d037e3c40a0acc46f618
注釈
※1 Apple Developer Centerに登録した1つのチームから複数の開発者を招待して、その開発者はそれ以外のチームに所属していないという想定。個人開発している人もいるのでいつも事実では無いですが会社アドレスのアカウントでは事実なこと多いのでは
※2 証明書周りがよくわからなくなってむやみやたらに再発行され各種ファイルの作り直しに励むのはよくあるので助けてください
※3 1つのアプリについてProductionとSand Boxというつもり。
気になるところあったらツッコミください。
ObjCで戻り値以外にNSErrorのポインタを出しているのが面白かった話
NSErrorの使い方というよりはNSErrorが使われる場面についての話で、 C言語をはじめとした関数やメソッドの戻り値は基本的に1つになります。
複数の値を返したい場合、構造体を作ったり、オブジェクトを使ったり、といった方法がありますが、基本的にメソッドの戻り値は1つの値か、関連する値をまとめた一つのオブジェクト、となります。
普段はそこまでこまらないのですが、本来の処理と全然関係ない情報を呼び出し元に返したくなった場合はどうしたらよいでしょうか。例えば、エラーが起きてしまって本来の値が返せない、といった場合。
JavaやRubyなら上記のような想定外の事態が起きて処理が進められない場合には例外を投げてしまえばいいのでしょうが、メモリ管理にARCを利用している関係もあり、Objective-Cでは基本的にアプリがそのまま死ぬような場合以外は例外を投げたくありません。
では、どうするか、と言いますと、例外を表すNSErrorのインスタンスのポインタを渡して、何か起きた時にはNSErrorのポインタがさす変数にnil以外が入っているようにするのです。
// 引用元: http://www.inazumatv.com/contents/archives/9078 NSError *error = nil; NSString *result = [ExampleError exampleMethod:&error] if (error) { // error処理 } // class ExampleError +(NSString *)exampleMethod:(NSError **)didFailWithError { NSError *error = nil; NSString *resultMessage = @"success message"; [self innerMethod:&error]; if (error) { *didFailWithError = error; return nil; } return resultMessage; }
というのが面白かった、というお話。
参考:
iOSでファイルにオブジェクトの状態を保存する/.plistとは
ObjCのコードを読んでいた時に書いたメモを放流。
アーカイブとは
プログラムで使われている複数のオブジェクトを、その属性値や相互の関係も含めてファイルに保存したり、他のプロセスに渡したりしたい場合があります。 そのために、互いに関連するオブジェクトをバイト列に変換する機能をアーカイブといい、さらにバイト列から元のオブジェクトを復元する機能をアンアーカイブといいます。 また、オブジェクトを変換して得られたバイト列をアーカイブと呼ぶことも。
一方、シリアライゼーションはプロパティーリスト程度の単純な階層構造しか表現できません。
アーカイブができるオブジェクトは、NSCodingプロトコルに適合するように実装する必要があります。
// アーカイブ用 - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; // スーパークラスがNSCodingプロトコルに適合していない場合は不要 [coder encodeObject:オブジェクト forKey:キー文字列]; // またはencodedConditionalObject:forKey:を使う ... [coder encodeDouble:実数の変数 forKey:キー文字列]; // いくつかの方に合わせたメソッドが用意されている } // オブジェクトがまた依存しているオブジェクト...とたどっていくと元のオブジェクトのencode...が // 呼ばれそうだが、すでにアーカイブされていたら二度目はされないらしい
// アンアーカイブ用 - (id)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; // スーパークラスがNSCodingプロトコルに適合していない場合は[super init]でよい 変数 = [[coder decodeObjectForKey:キー文字列] retain]; ... 変数 = [coder decodeDouble:キー文字列]; }
NSKeyedArchiver
上記のencodeWithCoder
を使って、対象となるオブジェクトをエンコードした結果をデータオブジェクトに書き込み。
初期化の際は、書き込み先のNSMutableData
のオブジェクトが必要となります。
一つずつアーカイブしていっても良いが、ルートオブジェクトをエンコードすると再起的にオブジェクトグラフがアーカイブ化される。全部完了したらfinishEncodingを読んで後処理をします。
書き込み先はデータオブジェクトなのでデータオブジェクトからファイルへ書き込んだり、他プロセスへ送信したりができるし、ファイルに書き込むまで一括で出来るメソッドもあります。
NSKeyedUnarchiver
上記のinitWithCoder
を使って、アーカイブされたオブジェクトが書き込まれたデータオブジェクトからオブジェクトを復元します。
ファイルからデータオブジェクトを作って読み込んだりもできます。
.plist
https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html http://glassonion.hatenablog.com/entry/20110910/1315609950 http://qiita.com/hp0me/items/b619680611fd6667273f
Apple系が発祥のNSPropertyListSerializationクラス
でパースできる*1、データ永続化などのために使われるファイル形式。
プロパティリストと呼ぶみたいです。
代表的なものはアプリの設定を保存している Info.plist
だが、それ以外にも利用可能なようです。
ファイル形式としてはXMLの一種だと思います。*2
*1:NSArray や NSDictionary クラスでもまぁできる
*2:そういえば、AndoridのSharedPreferencesの実装もXMLだったはず http://qiita.com/ochim/items/dc4d77d478e87c1449ad
ideviceinstallerで「Could not connect to lockdownd. Exiting.」と出てアプリのインストールに失敗する
$ ideviceinstaller -u b93fd1bed1bbdf952070fa4160a34510efbe71ee -i /Users/woshidan/to/app/dir/iOSApp/build/sym/Release-iphoneos/iOSApp.app Could not connect to lockdownd. Exiting.
Mac OS X El Captain以降のバージョンだと、iOSアプリのインストールに利用している ideviceinstaller
が依存している libimobiledevice
のバージョンが古い場合、iPhoneにアプリをインストールする際に操作する必要のある /var/db/lockdown
の編集権限がない場合があります。
この場合は、libmobiledevice
から新しく入れ直してした後、古い /var/db/lockdown
ディレクトリ以下を削除してやり直すと自分の場合は解決しました。
brew uninstall ideviceinstaller -g brew uninstall libimobiledevice -g brew install --HEAD libimobiledevice -g // libimobiledeviceの新しいバージョンを入れ直す brew install ideviceinstaller -g sudo rm -rf /var/db/lockdown/*