DialogFragmentのshowとdismiss方法はコレに落ち着いた
ちょっと仕事でアプリのクラッシュログがすごい量出てきて調べてほしいと言われてとりあえずFabricのスタックトレースをみてみたら
「java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState」で落ちまくっている。なんとなく予想はしていたが
Fragmentの貼り付けているWebViewのonPageStartedでDialogFragmentをshow()して、onPageFinished()でdismiss()している箇所があり
ここの部分が半端無く落ちてる。まぁそうだろうな…WebViewのロードが終わる前に画面回転とかしたらこのExceptionが発生してそのまま即死コース一直線だ
kotlinでAndroidアプリを試しに作った
よくお世話になるクローディアのアプリのAndroid版をkotlinで書き直し中 最近あまりネイティブアプリを書くことがなくなってきたのでリハビリを兼ねて作ってみました。
とにかく最近のトレンドを追いかけるために
- retrofit
- databinding
- realm
- rxJava
この辺りは入れてみようと思いましたが、realmだけまだ入れてない。。。
Dagger2もまだ使ったことないので使ってみたかったが後回し
設計もいまトレンドのMVPでActivityを極力Viewの操作のみに専念し
PresenterがRepository層のクラスを使ってAPI通信を行ったり、PreferenceFileに書き込みを行ったりということをしています。少々雑ですが…
更に細かくやると、UseCase層も作ってそこにRepositoryとのやりとりをまとめたほうが抽象度は上がるのでしょうが、個人でやるにはそこまではやりすぎ感があったので
Presenterが色んなRepositoryとやりとりして結果をViewに知らせる形にしました。
テストコードもビジネスロジックはPresenter側に押し込めるのでPresenterを抑えておけばとりあえず大丈夫なはず。
View側のテストはInstrumentテストで頑張ればいいかなぁという感じ。
RepositoryとViewはInterfaceで実装を抽出してPresenterに渡しているのでmockを差し込むのもやりやすくて良かったです。
Androidのアレ!iOSでいうと
メモ書き程度のもの そのうち追加したり、もう少し詳しく書く
画面遷移(startActivity)
Android
Intent intent = new Intent(this,NextActivity.class); startActivity(intent);
iOS
stroyboardでViewControllerを「Control + ドラッグ」でつなげて SegueのIdentifierを定義する「toNextViewController」
self.performSegueWithIdentifier("toNextViewController",sender:nil)
画面遷移with値(intent.putExtra)
Android
Intent intent = new Intent(this,NextActivity.class); intent.putExtra("param","hoge"); startActivity(intent); //NextActivityで getIntent().getExtras().getString("param") //hoge
iOS
prepareForSegueを使います
class NextViewController:UIViewController{ var value1:Int = 0 var value2:String = "" }
func hoge(){ self.performSegueWithIdentifier("toNextViewController",sender:nil) } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { let nextViewController = segue.destinationViewController as! NextViewController nextViewController.value1 = 123 nextViewController.value2 = "hogehoge" }
画面から戻ってきたとき(onActivityResult)
Android
public class FugaActivity extends Activity { private static final int REQUEST_CODE = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this,NextActivity.class); startActivityForResult(intent, REQUEST_CODE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(resultCode == RESULT_OK && requestCode == REQUEST_CODE){ //doSomething } } }
iOS
StoryBorad Unwind segue でググる
pull refresh
Android
AndroidではSwipeRefreshLayoutの中にListViewを入れるのが一般的です
http://developer.android.com/intl/ja/samples/SwipeRefreshListFragment/index.html より
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/swiperefresh" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.v4.widget.SwipeRefreshLayout>
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swiperefresh); mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { //必要に応じてtrue,falseを設定してあげる mSwipeRefreshLayout.setRefreshing(true); //何かしらの処理。多くの場合はAPI叩く } });
iOS
tableViewControllerを使わない場合
@IBOutlet weak var tableView: UITableView! let refreshControl:UIRefreshControl = UIRefreshControl() override func viewDidLoad() { super.viewDidLoad() //pull to refreshで実行するメソッド名を指定します refreshControl.addTarget(self, action: Selector("refresh"), forControlEvents: UIControlEvents.ValueChanged) tableView.addSubview(refreshControl) } func refresh(){ //doSomething self.refreshControl.endRefreshing() }
tableViewControllerを使っている場合 storyboardからTableViewのRefreshを「enable」にして
override func viewDidLoad() { super.viewDidLoad() self.refreshControl?.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged) } func refresh(){ //doSomething self.refreshControl.endRefreshing() }
これでいける模様
バックグラウンドから復帰したとき
Android
onResumeやonRestartなど、その時の用途にあったライフサイクルを選ぶ
iOS
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/
override func viewDidLoad() { let notificationCenter = NSNotificationCenter.defaultCenter() //アプリがアクティブになったとき notificationCenter.addObserver( self, selector: "doHoge", name:UIApplicationDidBecomeActiveNotification, object: nil) } func doHoge(){ //doSomething }
そのほかにバックグラウンドに移行したときなど、notificaitonのtypeがいろいろあります
リストビューの最下部までスクロールしたときをハンドリング
Android
listView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (totalItemCount == firstVisibleItem + visibleItemCount) { //doSomething } } });
iOS
func scrollViewDidScroll(scrollView: UIScrollView) { //一番下までスクロールしたかどうか if(tableView.contentOffset.y >= (tableView.contentSize.height - tableView.bounds.size.height) && !nowLoading) { //doSomething } }