Category: Android

洗濯表示アプリがR25スマホ情報局に紹介されました!

Android用のアプリ「洗濯表示」がR25スマホ情報局に紹介されました!

 

洗濯表示アプリ

 

衣類を傷めず自分で洗濯をする際にチェックしておく必要がある、タグに記載されたJISの「洗濯表示」。よく調べずに洗濯してしまい、衣服が縮んだり伸びたりしてしまった経験がある人も多いでしょう。そんな悲劇を繰り返さないために役立ててほしいのが、この「洗濯表示 -新洗濯マーク対応-」です。

 

2016年12月から採用される、新しい洗濯表示全41種類に対応している点が大きな特徴。アイコンをタップすると画面下段に意味が表示されるので、洗濯方法を間違えてしまうリスクが回避できます。特に、新しい洗濯表示は見慣れないアイコンが多いので、このアプリが頼りになるはずです。ちなみに、新しい洗濯表示は国際基準に則ったものなので、海外製衣類の洗濯法もわかるようになりますよ。

 

もちろん現行の洗濯表示(全22種)にも対応しているので、これまでに購入した衣類の洗濯法を調べることも可能。機能自体はシンプルですが、実用度はかなり高いこのアプリ。大切な衣類を洗濯する際は、ぜひ活用してください。

アプリでわかる!衣類の正しい洗濯法 | R25スマホ情報局
http://smartphone.r25.jp/app-use/141752

 

 

 

ハウツーアプリの方にも紹介いただきました。

R25スマホ情報局

冬の衣類や毛布などを自宅で洗濯しようと思った時、気になるのが洗濯方法。もしタグにわからない「洗濯表示」のアイコンがあったら「洗濯表示 -新洗濯マーク対応-」でチェックしてみましょう。アイコンをタップするだけで洗濯法を教えてくれるので、衣類を傷めてしまう心配ともおさらば。2016年から採用される新しい洗濯表示にも対応しています。

小さな疑問を解決!暮らしハウツーアプリ | R25スマホ情報局
http://smartphone.r25.jp/app-use/theme-141754

 

どうもご紹介ありがとうございます!

 

 

【洗濯表示 -新洗濯マーク対応-】アプリのダウンロードはGoogle Play Storeからもどぞーー


この記事のトラックバック用URL - http://mashi.exciton.jp/archives/225/trackback


ゴミ収集日

アプリ、リリースしました!

ゴミ収集日

https://play.google.com/store/apps/details?id=jp.exciton.gomiapp

今回もマテリアルデザイン意識してみました。
意識しただけなので細かいところは許してください!

これ作りながら、ちょっと気になったものを記事にしてたりしたので、
AndroidのUIスレッドTimerとか
PendingIntentとAlermManagerとか
あと今回ユニットテストもやったので、AndroidをUnitTestする
の内容も!

とくに、アラート系のアプリなので、開発の中でAlertManagerの特性についてはわかったことも多いです。
AlertManagerの説明に関しては
TechBoosterさんのところにわかりやすくまとまってます。

AlertManagerで時刻を予約してから、時計の設定を先に進めると一気にアラート来るんですね、
知らなかった。

このアプリではいくつかの予約(2,3個)をしているので、デバッグのために時計進めてみると、
いっきにIntentが飛んでくるんです。
なので、ほんとに今実行すべきか(アラートを出すか)をIntent受け取ってから判断する必要があります。
細かいけど、こういう細かいことをやってるアプリとそうじゃないアプリがあるってことが、時刻ずらしてみるとわかるのがおもしろい。

あとCrashlytics入れました。なので、クラッシュでてもご報告不要です!!!
ただしUIとか動きがおかしいのはCrashlyticsしてくれないのでご報告いただけたら嬉しいです!
Crashlyticsの方もそのうちレポートしたいなーとおもってます。



この記事のトラックバック用URL - http://mashi.exciton.jp/archives/124/trackback


カスタムビューとレイアウトエディタ

アンドロイドでカスタムビューを作るときのお話

カスタムビューを作った時にレイアウトエディタでの表示がカッコよくならなくて
諦めたりしてませんかー?

isInEditMode()

http://developer.android.com/reference/android/view/View.html

これが便利!


Viewを継承してなんかのCustomViewを作るんですが、
ActivityのonCreateなんかで

public void onCreate() {
   new MyCustomView(this, param1, param2);
}

とか

myview = findViewById(R.id.my_custom_view);
myview.setMyParameter("hoge");

とかって処理をして、表示内容を変えたりすると思うんです。
何も渡さない時のデフォルトをコンストラクタで用意してもいいんですが、
その処理がそれなりに重いものだったりすると無駄がおおい、、、、

isInEditMode()

そんなときisInEditMode()で判断して、レイアウトエディタで表示するときにだけの処理なんかが書けるようになります!

myview = findViewById(R.id.my_custom_view);
if( isInEditMode() ){
   myview.setMyParameter("レイアウトエディタ表示");
}else{
   myview.setMyParameter(message);
}

みたいな?
こういうのちゃんとやっとくとプログラマとUIデザイナーとの分業もやりやすいです!?

参考:
http://www.united-bears.co.jp/blog/archives/402
http://to40.blog55.fc2.com/tb.php/29-4af5d637


この記事のトラックバック用URL - http://mashi.exciton.jp/archives/117/trackback


AndroidをUnitTestする

Androidのテスト環境について

Unitテストをやりたい場合。
JUnitテストを行うやり方についてのメモ。
AndroidStudioを使ってることを前提とします。

まず、build.gradleのdependenciesですが、
これまでだとEspressoなどを使う必要があったのですが、
androidのsupport packageに追加されているtesting-support-libに統合されたようです。

Testing Support Library

support packageに追加されたtesting-support-libを使ってAndroidのテストをJUnit4で書く

ということなのでdependenciesには
androidTestCompile ‘com.android.support.test:testing-support-lib:0.+’
のみ追加。

それとdefaultConfig
testInstrumentationRunner “android.support.test.runner.AndroidJUnitRunner”
を追加してください。
これはJUnit4を動かすには必要なようです。
もしJUnit3だけであればなくても動くのですが、今回はJUnit4を使ってみるので入れておきます。

 

この状態で実行してみると、つぎのようなエラーが出るかもしれません。

Error:duplicate files during packaging of APK /Users/…/build/apk/app-debug-androidTest-unaligned.apk
Path in archive: LICENSE.txt

下記のようにbuild.gradleにのandroid{ のなかに追加してください
エラーに出てるファイル、もしくはパス名を下記のように付け加えればOK

android {
    ~~  
    packagingOptions {
        exclude 'LICENSE.txt'
    }

環境によっては’META-INF/LICENSE.txt’になるかも。

そうするとこんなかんじになります。↓

 android {
    ~~

    defaultConfig {
           ~~

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }


    packagingOptions {
        exclude 'LICENSE.txt'
    }
}

dependencies {
    ~~

    androidTestCompile 'com.android.support.test:testing-support-lib:0.+'
}

 

UnitTestの書き方

AndroidStudioがいろいろと手伝ってくれます。
テストしたいクラスにカーソルをあわせた状態で[ALT+Enter](デフォルト設定の場合)
ここではScheduleListItemという適当なクラスをテストしてみることにします。ただのプレインオブジェクトです。
キャプチャ
そのままCreate Test

 

ダイアログが出て、ここで好きなテストフレームワークをとオプションが選べます。
testing-support-libで、JUnit4が使えるようになったということでJUnit4にしてみましょう。
JUnit4の場合、クラスの継承はしないのでSuperClassが空になりますが、ご心配なく。
setUptearDownはテストの最初と最後に実行される関数です。
とりあえず作ってもらったら楽なのでチェック。
下のメンバー関数は特にチェックしなくてもよいかも。
キャプチャ2

保存先を聞かれるけどそのままでOKでしょう。
src/androidTest/java/~~/**Test.javaが作られます。

public class ScheduleListItemTest {
	@Before
	public void setUp() throws Exception {

	}

	@After
	public void tearDown() throws Exception {

	}
}

テストを書く

テストを追加したい場合はメンバ関数を以下のように追加して
assert文を書けばOK

	@Test
	public void foo() throws Exception {
             assertEquals("31", day);
	}

JUnit4の使い方についてはここでは詳しく説明しませんが他のサイトで勉強してみてください。
参考:http://d.hatena.ne.jp/megascus/20130622/1371879646

 

assert文を書く場合、static importで
import static junit.framework.Assert.assertEquals;
を追加するといいです。これがないとAssert.assertEquals()のように書かないといけないので。

 

 

かんたんなテストのサンプルを書いてみます。
あんまり良いサンプルではないですが許してください。

import static junit.framework.Assert.assertEquals;

public class ScheduleListItemTest {

	private ScheduleListItem item;
	@Before
	public void setUp() throws Exception {
		item = new ScheduleListItem();
	}

	@After
	public void tearDown() throws Exception {
		//最後になんかする場合はここで。
	}

	@Test
	public void foo(){
		item.set(5, 8, "5/8日のすけじゅーる");
		
		assertEquals(5, item.getMonth());
		assertEquals(8, item.getDay());
		assertEquals("5/8日のすけじゅーる", item.getTitle());
	}
}

最初にsetUp()が実行され、itemがnewされます。
次にfoo()が実行され、各assert文が実行されます。
itemにセットした値が正しく入ってるかチェックしてるって感じです。

 

実行の設定

テストの実行に当たっての設定です。
まず実行ボタンの左のメニューよりEditConfigurationsを選んでください。
キャプチャ3

 

次に+ボタンからAndroid Testsを選択!
キャプチャ4

 

新しいテストの実行の設定が作られました。
キャプチャ5
名前がUnnamedになっているので、好きな名前に変えてOKです。
Moduleになってるので、テストするモジュールを選択してください。

テスト範囲が選べます
[All in Module]AndroidTestに入ってるモジュール全部。実行
[All in Package]パッケージを指定して、実行
[Class]クラスをひとつ指定して実行
[Method]メソッドを指定して実行

ここでは全部ということでAll in Moduleを選択しました。

 

TargetDevice
テストを実行するのにも、Androidデバイスが必要なので、
それぞれ実機を使うか、エミュレーターを使うか、ダイアログでその都度選ぶかするか、
などの選択肢です。
いつも実行するときに選択しているものにしておけばよいでしょう。

設定が終わったらOK

テスト実行!

今作成した実行設定を選んで、RUN!!!
キャプチャ6

こんなかんじでテストが実行されます。
オールグリーン!
キャプチャ7

コンテキストが必要な場合は・・・?

例えばActivityをテストしたい場合や、ファイルの読み書きのテストをしたい場合、
androidのAPIにアクセスするためにcontextを渡さなければいけないので、今回のようにはできません。
ActivityInstrumentationTestCase2というクラスなどを継承して作らなければいけないのですが、
それはまた次回に・・・・



この記事のトラックバック用URL - http://mashi.exciton.jp/archives/69/trackback


洗濯表示アプリ

洗濯表示が変わるそうですね。
そんなニュースを見たので、ちょっと簡単なアプリ作ってみました。

対し他アプリではないんだけど、
マテリアルデザインっぽくしてみよう~ということで
あんまりデザインはやらないんですが、
デザインとアニメーション少し頑張ってみました。

一番力はいってるのは新洗濯表示と旧洗濯表示の切り替えの部分で、
Meaningful transitionsで紹介されてるHierarchical timingを実装してみました。
意外にめんどくさかった!!!

グーグルのHierarchical timing

グーグルのHierarchical timing

GridViewで並べたアイコンの左上から順に切り替わっていくような形になってます。
これが実装されてるものあんまりないかもしんないから珍しいかもしれないです。
良かったらチェックしてみてください~

洗濯表示のHierarchical timing

洗濯表示のHierarchical timing

洗濯表示アプリ

洗濯表示アプリ



洗濯表示 – https://play.google.com/store/apps/details?id=exciton.jp.laundrysymbol



この記事のトラックバック用URL - http://mashi.exciton.jp/archives/61/trackback


PendingIntentとAlermManager

ActivityやServiceを時刻指定で起動させたいときは、PendingIntentを作成し、Alermマネージャーに登録する
この2つのクラスをなんとなくセットで使っていると動作がよくわからず、複数のタイマーをセットした時におかしなことになります。

この2つのクラスはどう働いているか、別々に考えるとわかりやすいです。

まず、よくあるサンプルコード


public foobarTimer(Context context, Calendar when, String message){
  //起動させたいActivity, ServiceなどのIntentを指定
  Intent intent = new Intent(context, MainActivity.class);
  intent.putExtra("tag_name", "hello");//起動時に渡したい値をセット

  //intentを指定してPendingIntentを作成する。
  PendingIntent pIntent = PendingIntent.getService(context, 0, intent, 0);

  //AlermManagerをコンテキストより取得
  AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

  //AlermManagerに起動する時間と起動したいIntentをセットしたpIntentを指定
  am.set(AlarmManager.RTC_WAKEUP, when.getTimeInMillis(), pIntent);
}

よくあるサンプルだし、通常のタイマーとしてはこれで問題なく動くんだけど、

PendingIntent pIntent = PendingIntent.getService(context, 0, intent, 0);

よくわかんないから0でいいや!ってなってしまいがち


Android ReferenceのPendingIntentをみると
getActivity(Context context, int requestCode, Intent intent, int flags)
第2と第4引数はそれぞれrequestCodeとflagsとやらを指定しろということになってる。

まず第2引数のrequestCodeは、そのアプリケーション内でPendingIntentを区別するためにユニークな値を入れる必要がある
たとえば幾つかのタイマーをセットできるアプリを考える。
先ほどのfoobarTimer関数を利用してこのように3つのタイマーを同時にセットした場合、これらはすべてrequestCode=0と指定されている

Calendar after10 = Calendar.getInstance().add(Calendar.SECOND, 10);
Calendar after20 = Calendar.getInstance().add(Calendar.SECOND, 20);
Calendar after30 = Calendar.getInstance().add(Calendar.SECOND, 30);
foobarTimer(this, after10, "10秒後です");
foobarTimer(this, after20, "20秒後です");
foobarTimer(this, after30, "30秒後です");

この場合、同じrequestCodeであることが原因で、3つが正しくセットされない。
結果としては30秒後に「10秒後です」と表示される。
というとんでもない事になります。


なぜこのようなことが起きるか掘り下げてみると、まずこの関数
PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags)
必ず新しくPendingIntentを作り出すわけではありません。
登録されているPendingIntentを再び返したり、なければ新しく作成したり、などをしているようです。
それを判断するのが requestCodeflagsなわけです。

PendingIntentのFlagsの説明をみると

FLAG_CANCEL_CURRENT Flag indicating that if the described PendingIntent already exists, the current one should be canceled before generating a new one.
FLAG_NO_CREATE Flag indicating that if the described PendingIntent does not already exist, then simply return null instead of creating it.
FLAG_ONE_SHOT Flag indicating that this PendingIntent can be used only once.
FLAG_UPDATE_CURRENT Flag indicating that if the described PendingIntent already exists, then keep it but replace its extra data with what is in this new Intent.

4つのFLAGが表記されてます。

詳しく説明すると

FLAG_CANCEL_CURRENT: 同じrequestCodeのPendingIntentが存在したら、それをキャンセルして新しく作ります。
FLAG_NO_CREATE: 同じrequestCodeのPendingIntentが存在すればそれを返します。そうでなければNullを返します。
strong>FLAG_ONE_SHOT: 一度特定のrequestCodeを持つPendingIntentが作られたら、それ以降で同じrequestCodeを指定された場合は最初に作られたPendingIntentを返し続けます。
FLAG_UPDATE_CURRENT: 同じrequestCodeのPendingIntentが存在するばあい、それをつかうが、putExtraの値は新しく更新します。

という指定になっています。
flagsに0を指定した場合にどうなるか、見つけられなかったのですが、FLAG_ONE_SHOTと同じ動きをしているとおもわれます。


そしてAlermManager.set()ですがこれは単純に、指定された時間にPendingIntentを実行する。
ただし、すでに同じrequestCodeのPendingIntentがセットされていたら、それを更新する。と考えればよいです。

30秒後に「10秒後です」と表示されたのは、2回めのPendingIntent.getActivity()で同じrequestCodeが指定されたため、
1回めに作成された「10秒後です」がセットされたPendingIntentが返されて、それが30秒後の指定でAlermManager.set()されてしまったために起きた現象です。


PendingIntentとAlermManagerを使う場合は
requestCodeを意識して、書き換えなのか、新規登録なのかを考えながら書く必要があります。

以下のサイト参考にさせていただきました。
http://d.hatena.ne.jp/yujimny/20110303/1299115905



この記事のトラックバック用URL - http://mashi.exciton.jp/archives/45/trackback


AndroidのUIスレッドTimer

Androidでポップアップウィンドウとか出した時に、数秒後に閉じるみたいなことって結構あるけど、
普通のTimerで↓みたいにやるとうまく行かない。


    new Timer(true).schedule(new TimerTask() {
      @Override
      public void run() {
        hidePopupWindow();
      }
    }, delay);

Activityに属するウィジェットの表示なんかを変更しようとする場合は
Handlerを通じてUIスレッドから実行しないといけない。

んだもんだから、下記のようなコードになる


final Handler handler = new Handler();
new Timer.schedule( new TimerTask(){
        @Override
        public void run() {
            handler.post( new Runnable() {
                public void run() {
                     hidePopupWindow();
                }
            });
        }
    }, delay);

hidePopupWindow()したいだだけなのにずいぶんと大げさなコードになっちゃう。
こりゃいかん!

ということでUIスレッドタイマー作った!


public class UITimer {
  private Timer timer;

  public UITimer(final Runnable run, long delay){
    this(run, delay, 0L);
  }
  public UITimer(final Runnable run, long delay, long period){
    timer = new Timer(true);
    TimerTask task = new TimerTask() {
      @Override
      public void run() {
        new Handler().post(run);
      }
    };
    if(period > 0){
      timer.schedule(task, delay, period);
    }else{
      timer.schedule(task, delay);
    }
  }

  public void cancel(){
    if(timer != null)
      timer.cancel();
  }
}

使い方はこんな感じ。
どや、これでかんたんやろ!


    new UITimer(new Runnable() {
      @Override
      public void run() {
        hideUndoPopup();
      }
    },3000);

あぁ、関数ポインタが欲しくなってくる、、、、

=====================
TimerやTimerTaskを使わないで
Handlerだけで実現する方法もあるようです。

[AndroidでタイマーからUIを操作するAdd Star]
http://d.hatena.ne.jp/seinzumtode/20130504/1367662040



この記事のトラックバック用URL - http://mashi.exciton.jp/archives/37/trackback