この文書の現在のバージョンと選択したバージョンの差分を表示します。
両方とも前のリビジョン 前のリビジョン 次のリビジョン | 前のリビジョン | ||
android:androidstudio2で始めるアプリ開発入門 [2019/10/13 07:48] ips [詳細が画面に収まらないためスクロールさせる] |
android:androidstudio2で始めるアプリ開発入門 [2019/12/01 09:11] (現在) ips |
||
---|---|---|---|
ライン 341: | ライン 341: | ||
</code> | </code> | ||
+ | ==== HTTPアクセスパターン1 ==== | ||
+ | <code> | ||
+ | val urlStr = "http://weather.livedoor.com/forecast/webservice/json/v1?city=${id}" | ||
+ | val url= URL(urlStr) | ||
+ | val con=url.openConnection() as HttpURLConnection | ||
+ | con.requestMethod="GET" | ||
+ | con.connect() | ||
+ | </code> | ||
+ | |||
+ | ==== HTTPアクセスパターン2==== | ||
+ | |||
+ | <code> | ||
+ | val apidata = URL("http://weather.livedoor.com/forecast/webservice/json/v1?city=${id}").readText() | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ===== 第12回.Androidアプリでのメディア再生 ===== | ||
+ | |||
+ | ==== String リソースから文字列の取得方法 ==== | ||
+ | |||
+ | <code kotlin> | ||
+ | btPlay.setText( R.string.bt_play_play) | ||
+ | btPlay.text= getString( R.string.bt_play_play) | ||
+ | </code> | ||
+ | |||
+ | ===== 第13回.Androidアプリでのバックグラウンド処理と通知機能 ===== | ||
+ | |||
+ | |||
+ | android:exported 属性を含めて false に設定すると、サービスを自身のアプリでしか利用できないようにすることができます。これにより、他のアプリによるサービスの開始を効果的に回避でき、たとえ明示的インテントを使用したとしても開始できなくなります。 | ||
+ | |||
+ | <code xml AndroidManifest.xml> | ||
+ | <service | ||
+ | android:name=".SoundManageService" | ||
+ | android:enabled="true" | ||
+ | android:exported="false"></service> | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ==== No Channel found for ==== | ||
+ | |||
+ | [[https://qiita.com/b_a_a_d_o/items/83c01942a348efd551d4|Android O(APIバージョン26)のPush通知]] | ||
+ | |||
+ | Android O(APIバージョン26)以上ではNotifcationの使用には「通知チャネル」の登録が必要。 | ||
+ | |||
+ | <code> | ||
+ | E/NotificationService: No Channel found for pkg=com.nekotype.ips.servicesample, channelId=null, id=1, tag=null, opPkg=com.nekotype.ips.servicesample, callingUid=10091, userId=0, incomingUserId=0, notificationUid=10091, notification=Notification(channel=null pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x0 color=0x00000000 vis=PRIVATE) | ||
+ | </code> | ||
+ | |||
+ | <code kotlin> | ||
+ | _player.setOnCompletionListener { | ||
+ | Log.d("service","setOnCompletionListener is called") | ||
+ | |||
+ | val manager:NotificationManager=getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager | ||
+ | |||
+ | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ | ||
+ | |||
+ | val channel = NotificationChannel("channel_id_sample","プッシュ通知",NotificationManager.IMPORTANCE_DEFAULT) | ||
+ | channel.apply { | ||
+ | enableLights(true) // 通知時にライトを有効にする | ||
+ | lightColor = Color.WHITE // 通知時のライトの色 | ||
+ | lockscreenVisibility = Notification.VISIBILITY_PUBLIC //ロック画面での表示レベル | ||
+ | } | ||
+ | manager.createNotificationChannel(channel) | ||
+ | } | ||
+ | |||
+ | val builder=NotificationCompat.Builder(this) | ||
+ | builder.apply { | ||
+ | setSmallIcon(android.R.drawable.ic_dialog_info) | ||
+ | setContentTitle("再生終了") | ||
+ | setContentText("音声ファイルの再生が終了しました") | ||
+ | } | ||
+ | |||
+ | if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ | ||
+ | builder.setChannelId("channel_id_sample"); | ||
+ | } | ||
+ | |||
+ | val notification = builder.build() | ||
+ | manager.notify(1, notification) | ||
+ | stopSelf() | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | {{:kotlin:pasted:20191018-062650.png}} | ||
+ | |||
+ | ==== startForegroundService ==== | ||
+ | |||
+ | [[https://qiita.com/gksdyd88/items/30df1f220001fad69d9e|Android Oreoでサービスを使ってみる]] | ||
+ | [[https://qiita.com/naoi/items/03e76d10948fe0d45597|Foreground Serviceの基本]] | ||
+ | [[https://qiita.com/nukka123/items/791bc4f9043764789ee6|Android Oからのバックグラウンド・サービスの制限事項を実演する。]] | ||
+ | [[https://www.muaaru.com/2018/11/17/post-254/|[Android]バックグラウンドでセンサーなどのログを取得し続けるには]] | ||
+ | |||
+ | [[https://developer.android.com/about/versions/oreo/background|バックグラウンド実行制限]] | ||
+ | [[https://developer.android.com/about/versions/oreo/background-location-limits|バックグラウンド位置情報の制限]] | ||
+ | |||
+ | Foreground(=前)Service(バックグラウンド)と思っていたので矛盾を感じていた。 | ||
+ | しかし、Foregroundはユーザーが確認できること。 | ||
+ | Serivceは画面がないことを意味する。 | ||
+ | つまり画面はないが、ユーザーが確認できるのがForegroundService。 | ||
+ | ユーザーは通知で動作を確認できるが画面がない。 | ||
+ | |||
+ | 設定ポイントは下記3点 | ||
+ | |||
+ | - startForegroundService(intent)<呼出側> | ||
+ | - 5秒以内にstartForeground(1, notification)<呼ばれ側> | ||
+ | - <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> 追加 | ||
+ | |||
+ | 下記エラーは3つ目のuses-permissionの追加で解消した。 | ||
+ | <code> | ||
+ | 2019-10-18 23:51:22.643 4837-4837/com.nekotype.ips.servicesample E/AndroidRuntime: FATAL EXCEPTION: main | ||
+ | Process: com.nekotype.ips.servicesample, PID: 4837 | ||
+ | java.lang.SecurityException: Permission Denial: startForeground from pid=4837, uid=10091 requires android.permission.FOREGROUND_SERVICE | ||
+ | </code> | ||
+ | |||
+ | ===== 第14回.Android地図アプリとの連携とGPS機能の利用 ===== | ||
+ | |||
+ | ==== Locationのパーミッション ==== | ||
+ | |||
+ | [[https://developer.android.com/guide/topics/security/permissions.html?hl=ja|システム パーミッション]] | ||
+ | |||
+ | <code xml> | ||
+ | <!-- GPSから取得 --> | ||
+ | <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> | ||
+ | |||
+ | <!-- Networkから取得 --> | ||
+ | <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> | ||
+ | </code> | ||
+ | GPS:高精度な位置情報を取得できるが、電力消費が激しい。屋内では使えない。 | ||
+ | Network:位置情報の精度は落ちるが、電力消費が少ない。屋内でも取得できる | ||
+ | |||
+ | ==== Parameter 'xxxx' is never used ==== | ||
+ | |||
+ | {{:kotlin:pasted:20191019-105034.png}} | ||
+ | ↓ | ||
+ | <code kotlin> | ||
+ | fun onMapShowCurrentButtonClick(@Suppress("UNUSED_PARAMETER")vieie: View){ | ||
+ | val uriStr="geo:${latitude},${longitude}" | ||
+ | val uri=Uri.parse(uriStr) | ||
+ | val intent = Intent(Intent.ACTION_VIEW,uri) | ||
+ | startActivity(intent) | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ==== ユーザー許可を取得する ==== | ||
+ | |||
+ | <code> | ||
+ | override fun onCreate(savedInstanceState: Bundle?) { | ||
+ | super.onCreate(savedInstanceState) | ||
+ | setContentView(R.layout.activity_main) | ||
+ | |||
+ | val locationManager=getSystemService(Context.LOCATION_SERVICE)as LocationManager | ||
+ | |||
+ | // 位置情報のユーザー許可を確認 | ||
+ | if (ActivityCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION ) | ||
+ | != PackageManager.PERMISSION_GRANTED | ||
+ | ) { | ||
+ | // 許可がなければ許可を求める | ||
+ | val permissions = arrayOf<String>(Manifest.permission.ACCESS_FINE_LOCATION) | ||
+ | ActivityCompat.requestPermissions(this, permissions, 1000) | ||
+ | // 求めた結果はonRequestPermissionsResult | ||
+ | return | ||
+ | } | ||
+ | |||
+ | locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,0L,0F,GPSLocationListener()) | ||
+ | |||
+ | } | ||
+ | |||
+ | // パーミッション許可を求めた結果 | ||
+ | override fun onRequestPermissionsResult( | ||
+ | requestCode: Int, | ||
+ | permissions: Array<out String>, | ||
+ | grantResults: IntArray | ||
+ | ) { | ||
+ | super.onRequestPermissionsResult(requestCode, permissions, grantResults) | ||
+ | |||
+ | if (requestCode==1000 && grantResults[0]==PackageManager.PERMISSION_GRANTED){ | ||
+ | |||
+ | //パーミッションが許可された場合 | ||
+ | val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager | ||
+ | val locationListener = GPSLocationListener() | ||
+ | |||
+ | // パーミッション許可を求めた結果を確認しているので不要に思えるが、 | ||
+ | // ↓ locationManager.requestLocationUpdates を呼ぶ前にかならず必要なだけ | ||
+ | if (ActivityCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){ | ||
+ | return | ||
+ | } | ||
+ | locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,0L,0F,locationListener) | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | </code> | ||
+ | ===== 第16回.Androidのマテリアルデザイン ~マテリアルデザインとツールバー~ ===== | ||
+ | |||
+ | ==== ツールバー ==== | ||
+ | <code xml> | ||
+ | ・・・ | ||
+ | <!-- <android.support.v7.widget.Toolbar--> | ||
+ | <android.widget.Toolbar | ||
+ | android:id="@+id/toolbar" | ||
+ | android:layout_width="match_parent" | ||
+ | android:layout_height="?attr/actionBarSize" | ||
+ | android:background="@color/colorPrimary" | ||
+ | android:elevation="10dp"/> | ||
+ | ・・・ | ||
+ | </code> | ||
+ | |||
+ | ===== 第17回.Androidのマテリアルデザイン ~スクロール連動~ ===== | ||
+ | |||
+ | AndroidXを使用できるようにする。 | ||
+ | {{:kotlin:pasted:20191015-065512.png}} | ||
+ | |||
+ | <code xml> | ||
+ | <?xml version="1.0" encoding="utf-8"?> | ||
+ | |||
+ | <androidx.coordinatorlayout.widget.CoordinatorLayout | ||
+ | android:layout_width="match_parent" | ||
+ | android:layout_height="match_parent" | ||
+ | xmlns:app="http://schemas.android.com/apk/res-auto" //app名前空間の読み込み | ||
+ | xmlns:android="http://schemas.android.com/apk/res/android"> //andorid名前空間の読み込み | ||
+ | |||
+ | |||
+ | <com.google.android.material.appbar.AppBarLayout | ||
+ | android:id="@+id/app_bar" | ||
+ | android:layout_width="match_parent" | ||
+ | android:layout_height="wrap_content"> | ||
+ | |||
+ | <androidx.appcompat.widget.Toolbar | ||
+ | android:layout_width="match_parent" | ||
+ | android:layout_height="?attr/actionBarSize" | ||
+ | app:layout_scrollFlags="scroll|enterAlways" | ||
+ | android:background="@color/colorPrimary" | ||
+ | android:id="@+id/toolbar"/> | ||
+ | |||
+ | </com.google.android.material.appbar.AppBarLayout> | ||
+ | |||
+ | <androidx.core.widget.NestedScrollView | ||
+ | app:layout_behavior="@string/appbar_scrolling_view_behavior" | ||
+ | android:layout_width="match_parent" | ||
+ | android:layout_height="match_parent"> | ||
+ | |||
+ | <TextView | ||
+ | android:layout_width="wrap_content" | ||
+ | android:layout_height="wrap_content" | ||
+ | android:text="@string/tv_article"/> | ||
+ | |||
+ | </androidx.core.widget.NestedScrollView> | ||
+ | |||
+ | |||
+ | </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||
+ | </code> | ||
+ | |||
+ | <code xml build.gradle(Module.app)> | ||
+ | AAPT: error: attribute layout_scrollFlags (aka com.nekotype.ips.toolbar2:layout_scrollFlags) not found. | ||
+ | ↓ | ||
+ | build.gradle(Modlue.app)に追加 | ||
+ | |||
+ | implementation 'com.google.android.material:material:1.1.0-beta01' | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ==== enterAlwaysモードでスクロール連動させたい場合まとめ ==== | ||
+ | |||
+ | <code xml styles.xml> | ||
+ | 標準のアクションバーを非表示 | ||
+ | <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> | ||
+ | ↓ | ||
+ | <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> | ||
+ | </code> | ||
+ | |||
+ | <code xml build.gradle(Module.app)> | ||
+ | implementation 'com.google.android.material:material:1.1.0-beta01' | ||
+ | </code> | ||
+ | |||
+ | <code xml activity_main.xml> | ||
+ | <CoordinatorLayout> | ||
+ | <AppBarLayout> | ||
+ | <Toolbar /> | ||
+ | </AppBarLayout> | ||
+ | <NestedScrollView> | ||
+ | … | ||
+ | </NestedScrollView> | ||
+ | </CoordinatorLayout> | ||
+ | |||
+ | <toolbar>に「app:layout_scrollFlags="scroll|enterAlways"」を記述 | ||
+ | <nestedscrollview>に「app:layout_behavior="@string/appbar_scrolling_view_behavior"」 | ||
+ | </code> | ||
+ | |||
+ | ==== FloatingActionButton(FAB) ==== | ||
+ | |||
+ | <code xml activity_main.xml> | ||
+ | <?xml version="1.0" encoding="utf-8"?> | ||
+ | |||
+ | <androidx.coordinatorlayout.widget.CoordinatorLayout | ||
+ | android:layout_width="match_parent" | ||
+ | android:layout_height="match_parent" | ||
+ | xmlns:app="http://schemas.android.com/apk/res-auto" | ||
+ | xmlns:android="http://schemas.android.com/apk/res/android"> | ||
+ | |||
+ | <com.google.android.material.floatingactionbutton.FloatingActionButton | ||
+ | android:layout_width="wrap_content" | ||
+ | android:layout_height="wrap_content" | ||
+ | android:layout_margin="20dp" | ||
+ | app:layout_anchor="@id/app_bar" | ||
+ | app:layout_anchorGravity="bottom|end" | ||
+ | app:srcCompat="@android:drawable/ic_dialog_email"/> | ||
+ | |||
+ | <com.google.android.material.appbar.AppBarLayout | ||
+ | android:id="@+id/app_bar" | ||
+ | android:layout_width="match_parent" | ||
+ | android:layout_height="180dp"> | ||
+ | |||
+ | <com.google.android.material.appbar.CollapsingToolbarLayout | ||
+ | android:id="@+id/toolbarLayout" | ||
+ | android:layout_width="match_parent" | ||
+ | android:layout_height="match_parent" | ||
+ | app:layout_scrollFlags="scroll|exitUntilCollapsed"> | ||
+ | |||
+ | |||
+ | <androidx.appcompat.widget.Toolbar | ||
+ | android:layout_width="match_parent" | ||
+ | android:layout_height="?attr/actionBarSize" | ||
+ | app:layout_scrollFlags="scroll|enterAlways" | ||
+ | android:background="@color/colorPrimary" | ||
+ | app:layout_collapseMode="pin" | ||
+ | android:id="@+id/toolbar"/> | ||
+ | |||
+ | </com.google.android.material.appbar.CollapsingToolbarLayout> | ||
+ | |||
+ | </com.google.android.material.appbar.AppBarLayout> | ||
+ | |||
+ | <androidx.core.widget.NestedScrollView | ||
+ | app:layout_behavior="@string/appbar_scrolling_view_behavior" | ||
+ | android:layout_width="match_parent" | ||
+ | android:layout_height="match_parent"> | ||
+ | |||
+ | <TextView | ||
+ | android:layout_width="wrap_content" | ||
+ | android:layout_height="wrap_content" | ||
+ | android:text="@string/tv_article"/> | ||
+ | |||
+ | </androidx.core.widget.NestedScrollView> | ||
+ | |||
+ | |||
+ | </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||
+ | |||
+ | |||
+ | </code> | ||
+ | {{:kotlin:scroll.gif?direct|}} | ||