Andriod : xmlで光る文字を表示する
皆さんこんにちは!
大学院生のきゅうりです。
アプリの次回作は絶賛開発中ですので、
しばしお待ちください!
光る文字を表示する
本日はandroidアプリで
光る文字を
表示させる方法を
TIPSとして紹介します!
画像のような
光るように見える文字
を表示する方法です!
さて、androidで開発する際に
文字の色やサイズを変更する方法として、
以下の二つの方法があります。
・プログラムファイル(.java)を編集
・レイアウトファイル(.xml)を編集
後者の方が見た目に分かりやすく、
初心者でも編集しやすくなっています!
というわけで、今回は
xmlファイルを編集することによって
行います!!
新規プロジェクト作成
まず、Android Studioで
Empty Activityの新規プロジェクトを作成します。
(方法がわからない人はググってください><)
activity_main.xmlのTextivewの<>内は
次のようになっていると思います!
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />
実行するとこんな感じです!
下準備
違いを見やすくするために
①文字を大きくする
②文字色を緑色にする
③背景を黒にする
以上の作業を行います
Text Viewの<>内にこれらを追加します。
android:textSize="64sp" android:textColor="#95CC1F" android:background="#000000"
実行するとこうなります!
これで準備が整いました!
光らせよう!
さて、ここから文字を光らせていきたいと思います!
本来は影を付けるために使用する属性を
使用します!
先ほど同様、
以下の部分をTextviewの<>内に追加します!
android:shadowColor="#BAFF26" android:shadowRadius="40.0" android:shadowDx="0.0" android:shadowDy="0.0"
shadowColorは影の色、
shadowRadiusは影のぼやけ具合、
shadowDx,shadowDyは元の文字の座標からの移動位置を
それぞれ表します。
これを実行すると、、、
どうでしょう?
不思議と文字が光っているように、
蛍光色に輝いているように見えます!
仕掛けとしては、
文字の色より明度の高い色影として設定し、
同じ位置でぼやかすと
光って見えるといった感じです!
shaowRadiusの値は、
文字の大きさやフォントに応じて
調整してください。
また、今回影の色を決める際には
こちらのサイトを参考にしました。
明度の違う色を簡単に選べて便利でした!
まとめ
以上いかがだったでしょうか!
影をうまく活用することで
文字を光ったように見せることができました!
僕たちCheerAppsが開発したアプリ
WidgetCounterの、テーマ”デジタル”でも
光る文字が表示されるように工夫しています!
(アプリでは別の方法によって実現しています)
是非アプリをインストールしてご確認ください!
AppWidgetProviderで複数のクリックイベントを扱う
はじめに
皆さんこんにちは,shiitaです.
今回は,ウィジェット実装で私が詰まった部分についての話をしたいと思います.
ウィジェットのクリックイベントの処理は特殊で,ブロードキャストを行うことで実装します.この記事では複数のクリックイベントを扱う際の注意点と,Oreoからの暗黙的なブロードキャストの制限についての対策を書いていきます.
※ ウィジェットのクリックイベントの処理方法をある程度知っていることが前提の記事です
PendingIntentの作成
ここで注意すべきことは次の3つです.
Intent
を明示的にするrequestCode
を一意にする- ブロードキャスト受け取り時の処理分岐を可能にする
以下のコードでIntent
とrequestCode
の作成をします.
private enum class ClickType { TYPE1, TYPE2, TYPE3 } private fun createIntentAndRequestCode(context: Context, appWidgetId: Int, type: ClickType): Pair<Intent, Int>{ val intent = Intent(context, WidgetCounter::class.java).apply { putExtra(APP_WIDGET_ID, appWidgetId) putExtra(CLICK_TYPE, type) } val requestCode = appWidgetId * ClickType.values().size + type.ordinal return Pair(intent, requestCode) } private const val CLICK_TYPE = "clickType" private const val APP_WIDGET_ID = "appWidgetId"
その後,PendingIntent.getBroadcast()
でブロードキャストするPendingIntent
を生成し,RemoteViews.setOnClickPendingIntent()
でクリックが発生した時の処理をセットします.
val (intent, code) = createIntentAndRequestCode(context, appWidgetId, ClickType.TYPE1) // viewsはウィジェット本体(RemoteViews) views.setOnClickPendingIntent(R.id.layout_id, // クリックイベントを設定するviewのID PendingIntent.getBroadcast(context, code, intent, PendingIntent.FLAG_UPDATE_CURRENT))
Intentを明示的にする
Oreoから暗黙的なブロードキャストに制限がかかるため,自身(AppWidgetProvider
)に対して明示的なブロードキャストする必要があります.Intent(context, WidgetCounter::class.java)
の様にクラスを直接指定します.
requestCodeを一意にする
ブロードキャストするPendingIntent
には一意なrequestCode
を割り当てる必要があります.ウィジェットの一意なIDであるappWidgetId
を利用することで,一意なrequestCode
を生成することが出来ます.
val requestCode = appWidgetId * ClickType.values().size + type.ordinal
後述するClickType
が異なればappWidgetId
が同じでもrequestCode
が区別出来るようにするため,ClickType
ごとにtype.ordinal
で値をずらしています.
ブロードキャスト受け取り時の処理分岐を可能にする
処理分岐のためにenum class
であるClickType
を定義します.Int
の定数を複数定義して,その値によって処理を分岐させても良いのですが,enum class
の方が便利なのでこちらを利用しました.Intent
にputExtra(CLICK_TYPE, type)
でClickType
を渡して,ブロードキャスト受け取り時に分岐が出来るようにします.
onReceiveでイベント処理
onReceive
では,クリックイベント以外のブロードキャストも受け取ってしまう点に注意します.ウィジェットの更新を行うACTION_APPWIDGET_UPDATE
などのIntent
も受け取り,スーパークラスで処理されるため,フィルタリングをする必要があります.
override fun onReceive(context: Context, intent: Intent) { super.onReceive(context, intent) // ACTION_APPWIDGET_UPDATEなどのIntentが処理される if (!intent.hasExtra(APP_WIDGET_ID) || !intent.hasExtra(CLICK_TYPE)) return val appWidgetId = intent.getIntExtra(APP_WIDGET_ID, 0) when (intent.getSerializableExtra(CLICK_TYPE) as ClickType) { ClickType.TYPE1 -> { // 処理1 } ClickType.TYPE2 -> { // 処理2 } ClickType.TYPE3 -> { // 処理3 } } }
おわりに
明示的なブロードキャストという不思議なことをしていますが,これでOreoでも動くようになります.同じrequestCode
を利用したために,処理分岐が出来ないというミスで数時間無駄にしてしまったので,同様の原因で詰まっている人の助けになればいいなと思います.
質問などありましたら,コメントして頂けると嬉しいです.
この記事の内容を活用してCheerAppsが作成した「ウィジェットカウンター」は
こちらからダウンロードできます!
ではでは.
AndroidとiOSどっちで開発した方がおトクか?
皆さん、こんにちは!
ご無沙汰してます!
大学院生のきゅうりです!
更新お待たせしました!!
本日は個人でアプリを開発する際に、
お金の面で見ると
どちらのOSで開発したほうがお得なのか??
ってことについてまとめていきます!
なお今回は、
多くの個人の開発者がとるであろう、
広告収入型の無料アプリのケースを想定しています!
アカウント登録料
アプリを公開するためにはアカウントに登録する必要があります
Androidでは「Google Play デベロッパーアカウント」、
iOSでは「iOS Developer Proglam」に登録する必要があります。
それぞれの費用は、、、
Google Play デベロッパーアカウント
登録料$25(約2750円)初回のみ
iOS Developer Proglam
参加費$99(約10900円)/年
となっています。
Androidの場合初めの登録料のみ支払えば良いですが、
iOSだと毎年参加費が必要です。
圧倒的にAndroidの方が低コストで開発できますね!
勝者:Android
シェアについて
OSの使用者が多い方が、当たり前ですが、
アプリのユーザも増え、広告収入も増えます。
ではシェアはどちらの方が高いのでしょうか??
2017年12月時点で
となっています。
参考:
https://www.kantarworldpanel.com/global
http://gs.statcounter.com/os-market-share/mobile/
日本国内ではほぼ半々ですが、
世界的にみるとAndroid方が圧倒的に高いシェアです。
日本限定のアプリなら:引き分け
世界でリリースするなら:Android
広告単価
1広告あたりの収入が高い方が
アプリの広告による収入に関する指標として
eCPMというものがあります。
eCPMとは、、、広告1000回表示あたりの収益
です!
使用する広告によっても異なりますが、
今回は最もメジャーな広告であるAdMobのケースで
eCPMを比較してみます!
2018年3月地点で
Android $2.01(約220円)
iOS $4.45(約490円)
となっています
貼られている広告の種類によって価格が異なるので、
(例えば、動画広告の方が画像の広告より単価が高いなど)
一概には言えませんがこの指標で判断するとiOSの方が
1ダウンロードあたりの広告収入は期待できそうですね。
勝者:iOS
まとめ
以上のように、個人で無料アプリを作るなら、、、どっちのOS??
という話について
金銭的な観点からAndroidとiOSどちらがおトクか比較してみました!!
総合的に判断すると
Androidの方が敷居は低いのかな!って印象です。
僕らも、まずはAndroidで開発をしています!
6行書くだけで誰でもできるウィジェット開発
皆さん,はじめまして.
CheerAppsで主にAndroidアプリ開発をしているshiitaです.
Androidアプリ開発で役立つTIPSなどについて、
伝えていけたらいいなと思います!
しばらくの間は、ウィジェットカウンターの開発で学んだことについて,
記事をいくつか書いていきます!
今回は簡単なウィジェットの開発について話したいと思います.
具体的には付箋のようにメモができるウィジェットを作ります!
使用するプログラミング言語は,私の一番好きなKotlinで書いていきたいと思います.
AndroidStudioで自動生成されるサンプルのプログラムをうまく活用して,
数行のコード編集だけで作れるものを紹介します.
ウィジェットの追加
右クリックをして出てくるメニューから,Widgetを選択し,追加します.
下の画像のように,クラス名やウィジェットのサイズなどを設定していくのですが,
ここで1つポイントがあります.
Configuration Screenにチェック
これを行うことで,ウィジェットの設定画面もAndroidStudioが自動生成してくれます!
できるだけ簡単にウィジェットを作って行きたいため,
楽できるところは楽して行きましょうwww
コードの編集
付箋のようにメモができるウィジェットを作るために,
コードの編集をしていきます.
編集する箇所はたったの6行です!!!
エラーの除去
AndroidStudioで自動生成されるコードはJavaで書かれたもので,
その後にKotlinへのコンバートが行われます.
このときに入り込んでしまうエラーを解消していきます.
class NoteConfigureActivity : Activity() { internal var mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID internal lateinit var mAppWidgetText: EditText // onCreateで初期化するのでlateinitをつける internal var mOnClickListener: View.OnClickListener = View.OnClickListener { val context = this@NoteConfigureActivity // When the button is clicked, store the string locally val widgetText = mAppWidgetText.text.toString() saveTitlePref(context, mAppWidgetId, widgetText) // It is the responsibility of the configuration activity to update the app widget val appWidgetManager = AppWidgetManager.getInstance(context) Note.updateAppWidget(context, appWidgetManager, mAppWidgetId) // Make sure we pass back the original appWidgetId val resultValue = Intent() resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId) setResult(Activity.RESULT_OK, resultValue) finish() } public override fun onCreate(icicle: Bundle?) { super.onCreate(icicle) // Set the result to CANCELED. This will cause the widget host to cancel // out of the widget placement if the user presses the back button. setResult(Activity.RESULT_CANCELED) setContentView(R.layout.note_configure) mAppWidgetText = findViewById<View>(R.id.appwidget_text) as EditText findViewById<View>(R.id.add_button).setOnClickListener(mOnClickListener) // Find the widget id from the intent. val intent = intent val extras = intent.extras if (extras != null) { mAppWidgetId = extras.getInt( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) } // If this activity was started with an intent without an app widget ID, finish with an error. if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { finish() return } mAppWidgetText.setText(loadTitlePref(this@NoteConfigureActivity, mAppWidgetId)) } companion object { private val PREFS_NAME = "jp.cheerapps.note.Note" private val PREF_PREFIX_KEY = "appwidget_" // Write the prefix to the SharedPreferences object for this widget internal fun saveTitlePref(context: Context, appWidgetId: Int, text: String) { val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit() prefs.putString(PREF_PREFIX_KEY + appWidgetId, text) prefs.apply() } // Read the prefix from the SharedPreferences object for this widget. // If there is no preference saved, get the default from a resource internal fun loadTitlePref(context: Context, appWidgetId: Int): String { val prefs = context.getSharedPreferences(PREFS_NAME, 0) val titleValue = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null) return titleValue ?: context.getString(R.string.appwidget_text) } internal fun deleteTitlePref(context: Context, appWidgetId: Int) { val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit() prefs.remove(PREF_PREFIX_KEY + appWidgetId) prefs.apply() } } }
コードを全て載せましたが,この部分で変更した箇所は
internal var mAppWidgetText: EditText
を
internal lateinit var mAppWidgetText: EditText
のようにlateinitを追加しただけです!
mAppWidgetTextの初期化はonCreate()で行うため,
後から初期化をすることを,明示的に示す必要があるのです.
設定画面の呼び出し
現在,設定画面はウィジェット追加時だけしか表示されません.
そこで,ウィジェットに記述したテキストを後から編集できるようにします.
具体的な処理は,ウィジェットをタップすると設定画面を呼び出すようにしたいと思います.
class Note : AppWidgetProvider() { override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { // There may be multiple widgets active, so update all of them for (appWidgetId in appWidgetIds) { updateAppWidget(context, appWidgetManager, appWidgetId) } } override fun onDeleted(context: Context, appWidgetIds: IntArray) { // When the user deletes the widget, delete the preference associated with it. for (appWidgetId in appWidgetIds) { NoteConfigureActivity.deleteTitlePref(context, appWidgetId) } } override fun onEnabled(context: Context) { // Enter relevant functionality for when the first widget is created } override fun onDisabled(context: Context) { // Enter relevant functionality for when the last widget is disabled } companion object { internal fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) { val widgetText = NoteConfigureActivity.loadTitlePref(context, appWidgetId) // Construct the RemoteViews object val views = RemoteViews(context.packageName, R.layout.note) views.setTextViewText(R.id.appwidget_text, widgetText) // クリックで設定画面を開く val intent = Intent(context, NoteConfigureActivity::class.java).apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) } val pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT) views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent) // Instruct the widget manager to update the widget appWidgetManager.updateAppWidget(appWidgetId, views) } } }
追加部分は以下の5行です.
val intent = Intent(context, NoteConfigureActivity::class.java).apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) } val pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT) views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent)
設定画面であるNoteConfigureActivityに対するPendingIntentを作成し,
ウィジェットのViewであるRemoteViewsに対してsetOnClickPendingIntent()
でクリック時の処理を追加します.
RemoteViewsはクリックイベントの処理は,通常のViewと異なることに注意が必要です.
実行結果
ウィジェット追加 | ウィジェット選択 |
---|---|
ウィジェット設定 | ウィジェット例 |
実行結果はこの表のようになっています.
ウィジェットの設定画面で書いたテキストが,
ウィジェットにちゃんと追加されていることが確認できます.
default activity not found エラー
default activity not foundのエラーが出て,以下の画像の様に実行できない場合があります.
これはConfigurationのLaunchをNothingにすることで解消することができます.
おわりに
ここまで読んでいただきありがとうございました!
いかがでしたか?記事の感想,コードへの指摘など頂けると嬉しいです.
CheerAppが開発した最初のアプリ,「ウィジェットカウンター」は
こちらからダウンロードできます!
ではでは.
gitについてのとても基礎的な話②〜アプリ開発への道 その3〜
皆さん、こんにちは!
大学院生のきゅうりです!
本日も読んでいただいてありがとうございます!!
今日は前回に引き続いて、
git
に関するごくごく簡単な話です!!
今日の目標は
「Aさんはlayoutのブランチを切ってレイアウトに関する作業を、
Bさんはmasterブランチでシステムに関する作業しよう」
「マージするときにコンフリクトしてないか確認してください」
これら二つが何を言っているのか、理解することです!
この2回の記事を読めば、
gitの基礎の基礎については
マスターしたと言っても過言ではないです!!(多分)
では、それぞれの用語について解説していきます!
ブランチ:
元のリポジトリに対して全く同じ複製を作成し、分岐を作ることです。
簡単にいうと、並行作業をする場所の作成です。
複数の作業を並列的に行いたい時に重宝します。
Aというブランチでは、レイアウトに関する作業を進める、
Bというブランチでは、システムに関する作業を進める
といった感じです。
gitでは初めから存在しているメインのブランチをmasterブランチと呼びます。
ウィジェットカウンターの場合
図で説明します。
この場合はlayoutブランチを作成し、
レイアウトやフォントに関する変更をlayoutブランチで、
カウンター機能に関する作業をmasterブランチで、
並列的に行っています。
最後のマージについては次で説明します、
新たなブランチを作ることをブランチを切るとも言います。
マージ:
複数のブランチのプログラムを統合することです。
複数のブランチそれぞれでの変更した部分を共に反映したリポジトリが作成されます。
具体的には、上の図のマージの部分ですね!
コンフリクト:
マージをする際に、
二つのブランチでそれぞれ同じ部分を変更してしまっていて
どっちをとったらいいのかわからない!!
という状況のことです。
つまり、衝突です(直訳ですが汗)。
ウィジェットカウンターを例にとって
図で説明すると次のような感じです!!
この場合は、
masterブランチでもlayoutブランチでも
カウンター機能の同じ部分を変更してしまったために、
コンフリクトが起きてしまったといった状況です。
コンフリクトしてしまった場合は、
自動でマージすることができず、
手動でマージすることになります。
以上です!
2回に分けて、gitを最近学んだ初心者が初心者に向けて
記事を書いてみました。
僕なりに分かりやすく説明したつもりですが、
いかがだったでしょうか?
コメント等々お待ちしています!
また、今回gitやGitHubについて学ぶきっかけなり、
例として挙げたウィジェットカウンターについては
google play で先日リリースしていますので、
是非ダウンロードお願いします!!
本日も読んでいただいて、
ありがとうございました!
それでは!