レイアウト関連
- Drawableフォルダには画材的なxmlを置く。
- dimensフォルダにはデバイスの大きさごとの指定を置く。
- FrameLayoutは何もしないレイアウト
- RelativeLayoutは何々は何々の上にあるとかを指定するレイアウト
- 絶対座標は使わないこと。
- データバインディングはデータ バインディング ライブラリ | Android Developers
バインドの取得とセット
binding = DataBindingUtil.bind(mView); binding.setHoge(mHoge);
Hogeのところはxmlのdataのnameによって自動的に作られる。bindingの型もdataに設定するtypeから作られる。
xmlはレイアウトを以下で包む必要がある。
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <import type="android.view.View"/> <variable name="hoge" type="jp.co.hoge.app.activity.hogeActivity.HogeItem" /> </data>
バインドの指定は
<TextView android:text='@{hoge.name, default=名前 }' />
エミュレータのプロキシ認証
やったこと。
エミュレータの設定でプロキシ設定する
うまくいかなかったのでコマンドライン上でabd, emulatorを使うことにした。
まずパスを通した。
export PATH=/Applications/adt-bundle-mac-x86_64***/sdk/platform-tools:$PATH export PATH=/Applications/adt-bundle-mac-x86_64***/sdk/tools:$PATH
使い方は
emulator -list-avds
で機種一覧がみれる。
プロキシ経由での実行は
emulator -avd devicename -http-proxy http://username:password@proxyhost:port
abdの使い方は以下に詳しい。
qiita.com
googleのブラウザで認証を求められて入れたら検索できるようになった。
しかし、自分のアプリではproxyでエラーになっている。
アプリをいじる
HTTPクライアントにプロキシ認証を仮にできるようにした。
OkHttpClientを使っているのでリクエストを投げる際に以下のように追加した。
Authenticator authenticator = new Authenticator() { @Override public Request authenticate(Route route, Response response) throws IOException { String credentioal = Credentials.basic(username, password); return response.request().newBuilder().header("Proxy-Authorization", credentioal).build(); } }; Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyhost,port) ); OkHttpClient client = new OkHttpClient().newBuilder() .proxy(proxy) .authenticator(authenticator) .proxyAuthenticator(authenticator) .build();
これでプロキシをさしあたり通すことができた。
OKHTTPの使い方は
qiita.com
幾つかのメモ
アダプター
アイテムからアイテム用のビューへの変換、動的に行われる。getView
convertViewはリサイクルされてきたビューのこと。
データバインディング
DataBindingUtil.inflateでbindingを取得して、そいつにデータをセット、
データはレイアウトのほうで受け取れるようにしておく。
gradleで
dataBinding {
enabled = true
}
レイアウトは
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="hoge" type="com.example.Hoge"/> </data> <LinearLayout android:id="@+id/activity_main" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{hoge.text1}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{hoge.text2}"/> </LinearLayout> </layout>
みたいにする。
レイアウトを使う。
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
GSon
JSONからモデルへ一括変換、モデルはアノテーションで何を受け取るか指定する。
Serializable
@SerializedName("hoge") public String hoge;
ビューのタグ
クラスを一時的にもっておける
レイアウトのinclude
レイアウトを入れ子に。しかし共通化か冗長性か。
ハンドラーにランナブルをなげる。
Handler handler = new Handler(getMainLooper()); handler.post(new Runnable() {
AsyncTaskは簡単バージョン
SimpleDateFormat
日時のフォーマット
CoordinatorLayout
iOSのAutoLayoutに近い
画面遷移カスタマイズ2 viewの遷移
ビューの遷移を真似たい場合snapshotViewとtransitionをくみあわせる。
let currentView = self.view.snapshotView(afterScreenUpdates: true)! self.view.addSubview(currentView) setup() UIView.transition(with: currentView, duration: 0.5, options: [.transitionFlipFromRight], animations: { currentView.frame = CGRect(origin: CGPoint(x:-self.view.frame.width, y:currentView.frame.origin.y), size: currentView.frame.size) } , completion: { _ in currentView.removeFromSuperview() })
画面遷移カスタマイズ
class PushAnimator : NSObject, UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 1.0 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromV = transitionContext.view(forKey: .from) else { return } guard let toV = transitionContext.view(forKey: .to) else { return } let container = transitionContext.containerView container.insertSubview(toV, belowSubview: fromV) UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: { fromV.alpha = 0.0 }, completion: { finished in fromV.alpha = 1.0 toV.alpha = 1.0 transitionContext.completeTransition(finished) }) } }
とやって、ナビゲーションのデリゲートに
// 画面遷移 func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { let animator = PushAnimator() switch operation { case .pop,.push: return animator case .none: return nil } }
UIViewのレイアウトライフサイクル
- 制約更新 updateConstraintsIfNeeded() → updateConstraints()が呼ばれる。
- フレーム更新 layoutIfNeeded() → layoutSubViews()が呼ばれる。 端末回転によるフレーム変更、UIScrollViewでcontentOffsetの変更時等
- レンダリング setNeedsDisplay() → drawRect()が呼ばれる。
UIViewControllerのレイアウトライフサイクル
- viewWillLayoutSubviews
- viewDidLyoutSubViews
AutoLayoutの制約式
基本は
A = aB + b
の連立方程式を解く。
Alignment Rectangle (外接矩形)
装飾(影、角丸等)を除いたビュー。AutoLayoutはフレームではなくAlignment Rectangleを用いている。
Alignment Rectangleの表示方法
Edit Scheme のRunのArgumentで起動オプションとして設定できる。
制約定義 NSLayoutConstraint
+ (instancetype)constraintWithItem:(id)view1 // 対象A first itemと呼ばれる。 attribute:(NSLayoutAttribute)attr1 // 対象Aに制約を追加する位置 Left,Right,Top,Bottom,Leading等 relatedBy:(NSLayoutRelation)relation // ビュー間の関係 =,>=,<= toItem:(nullable id)view2 // 対象B オプショナル, second itemと呼ばれる。 attribute:(NSLayoutAttribute)attr2 // 対象Bに制約を追加する位置 Left等 multiplier:(CGFloat)multiplier // 乗数 a constant:(CGFloat)c; // 加える定数 b
制約式を書くと
firstItem.attribute = a * secondItem.attribute + b
NSLayoutAttributte
typedef NS_ENUM(NSInteger, NSLayoutAttribute) { NSLayoutAttributeLeft = 1, NSLayoutAttributeRight, NSLayoutAttributeTop, NSLayoutAttributeBottom, NSLayoutAttributeLeading, // 先頭 NSLayoutAttributeTrailing, // 末尾 NSLayoutAttributeWidth, NSLayoutAttributeHeight, NSLayoutAttributeCenterX, NSLayoutAttributeCenterY, NSLayoutAttributeLastBaseline, NSLayoutAttributeBaseline NS_SWIFT_UNAVAILABLE("Use 'lastBaseline' instead") = NSLayoutAttributeLastBaseline, // テキストのベースライン NSLayoutAttributeFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0), // 複数行テキストの最初の行のベースライン NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0), // 左のマージン NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0), // 右のマージン NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0), // 上のマージン NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0), // 下のマージン NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),// オブジェクトの左と右のマージンの間のx軸に沿った中心。 UIViewオブジェクトの場合、マージンはlayoutMarginsプロパティで定義される。 NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0), NSLayoutAttributeNotAnAttribute = 0 };
Intrinsic Content Size
UILabel, UIButton, UIImageでコンテンツを圧縮、切り出しせずに表示するミニマムなサイズ。UILabelではテキストの内容によって変わる。
intrinsicContentSizeによって取得可能
Content Hugging Priority 大きくなりにくさ
縦横それぞれに指定可能。コンテンツに沿う優先度。Intrinsic Content Sizeにあわせてサイズが変化する。この優先度が低いと他の制約に従ってIntrinsic Content Sizeよりも大きくなる。
Content Compression Resistance Priority 小さくなりにくさ
前述の圧縮バージョン
Adaptive Layout
商品の値段とNSNumberFormatter
SKProductの価格の加工などで使う。
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; formatter.formatterBehavior = NSNumberFormatterBehavior10_4; formatter.numberStyle = NSNumberFormatterCurrencyStyle; formatter.locale = locale; NSString *string = [formatter stringFromNumber:price];
FutureパターンをSwiftで書く
Swiftで
まずリクエストプロトコルを作る。
public protocol Request { func getResult()->String }
それを実装するFurureを作る。
public class Future:Request { private let semaphore:DispatchSemaphore = DispatchSemaphore(value: 0) private var ready = false private var result:Result? = nil public func setResult(result:Result) { if ready { return } self.result = result self.ready = true semaphore.signal() } public func getResult() -> String { objc_sync_enter(self) while !ready { objc_sync_exit(self) semaphore.wait() objc_sync_enter(self) } objc_sync_exit(self) return self.result!.getResult() } }
つまり、getResultすると結果がsetResultされるまで待たされるという動きになる。
Resultは即に返答する。
public class Result : Request { private var content:String public init(_ command:Int) { print("request start \(command)") Thread.sleep(forTimeInterval: 1.0) content = "result = \(command)" print("request end \(command)") } public func getResult() -> String { return content } }
リクエストを受けつたマネージャはまずFurureを返し、
遅延して受け取る結果をsetResultで入れる。
public class RequestManager { public func request(command:Int) -> Future { let future = Future() DispatchQueue.global().async { let result = Result(command) future.setResult(result:result) } return future } }
ソースコードは以下に
github.com
回転させたときのレイアウト変更
XCode8で回転させた場合のレウアウト変更はSizeClassを使うとできる。
拘束条件を個別にinstallで設定する。全画面にしたい場合は上下左右にマージン0で設定してやる。
サイズの変更は以下の参考に書かれているような変更で呼び出しを受ける。
iOS8以降で、どうデバイスの回転を取り扱うかまとめてみる - Qiita