ServiceからActivityに値を投げたり画面を更新したりする方法

Android

ServiceからActivityに値を投げたり画面を更新したりする方法 サンプルソースコードあり

Androidで、バックグラウンドで動かしたり、長い時間がかかる処理をさせたり、待機させたりする動作はService(サービス)で動作させます。

たとえば、一時的に何かの情報が取得できないんだけど、何か情報が取得できたら、動かしているActivityの画面に表示したり、Activityの値を書き変えたい、ということはよくあると思います。

サービスは、Activityの裏で動いているので、一工夫しないとActivityを操作できません。
ちなみに、この方法はReceiver(レシーバー)でも使えます。

ここでは、一番簡単なサービスからActivytyに値を投げる方法を書いておきます。

以下、やり方です。

Handlerというものを使います。
Handlerが色々な値を、サービスやレシーバーからActivityに渡してくれます。

 /* Activity内 */
 
 public class MainActivity extends Activity {
 
 	private UpdateReceiver upReceiver;
 	private IntentFilter intentFilter;
 	private TextView message_tv;
 
 	@Override
 	protected void onCreate(Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.activity_main);
 
 		Context context = this;
 		Intent update_service = new Intent(context , UpdateService.class);
 		startService(update_service);
 		
 		upReceiver = new UpdateReceiver();
 		intentFilter = new IntentFilter();
 		intentFilter.addAction("UPDATE_ACTION");
 		registerReceiver(upReceiver, intentFilter);
 
 		upReceiver.registerHandler(updateHandler);
 		message_tv = (TextView)findViewById(R.id.message_tv);
 
 	}
 	
 	// サービスから値を受け取ったら動かしたい内容を書く
 	private Handler updateHandler = new Handler() {
 		@Override
 		public void handleMessage(Message msg) {
 
 			Bundle bundle = msg.getData();
 			String message = bundle.getString("message");
 
 			Log.d("Activityの名前", "はんどらーだよ" + message);
 			message_tv.setText(message);
 
 		}
 	};
 }
 /* Service内*/
 public class UpdateService extends Service {
 
 	private Handler handler;
 	private UpdateService context;
 
 	@Override
 	public int onStartCommand(Intent intent, int flags, int startid) {
 
 		super.onStartCommand(intent, flags, startid);
 
 		Log.d("UpdateService", "サービススタート");
 		sleep(4000);
 		
 		String message = "さーびすからのメッセージ";
 		sendBroadCast(message); 
 
 		return START_STICKY;
 	}
 
 	public void registerHandler(Handler UpdateHandler) {
 		handler = UpdateHandler;
 	}
 
 	@Override
 	public IBinder onBind(Intent arg0) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	public synchronized void sleep(long msec) {
 		try {
 			wait(msec);
 		} catch (InterruptedException e) {
 		}
 	}
 	
 	protected void sendBroadCast(String message) {
 
 		Intent broadcastIntent = new Intent();
 		broadcastIntent.putExtra("message", message);
 		broadcastIntent.setAction("UPDATE_ACTION");
 		getBaseContext().sendBroadcast(broadcastIntent);
 
 	}
 }
 /* Receiver内*/
  public class UpdateReceiver extends BroadcastReceiver {
 
 	public static Handler handler;
 
 	@Override
 	public void onReceive(Context context, Intent intent) {
 
 		Bundle bundle = intent.getExtras();
 		String message = bundle.getString("message");
 		
 		if(handler !=null){
 			Message msg = new Message();
 			
 			Bundle data = new Bundle();
 			data.putString("message", message);
 			msg.setData(data);
 			handler.sendMessage(msg);
 		}
 	}
 	
  	/**
  	 * メイン画面の表示を更新
 	 */
 	public void registerHandler(Handler locationUpdateHandler) {
 	    handler = locationUpdateHandler;
 	}
 
 }

AndroidManifest.xmlにサービスを書いておかないと、サービスが動きません。
エラーも出ないので、これは失敗しやすいポイントですね。

 /* AndroidManifest.xml内 <application>タグ内に記述 */
 <service android:name=".UpdateService"/> 

全部のサンプルコードはこちらで公開しています。
https://github.com/onlineco/serviceStudy/tree/0243c7ce917bc3f077e4fd54952f7fec22b6cb65 

  • 疑問点が1点あります。Service 内の registerHandler は必要なのでしょうか、実際に動かせばわかるのかもしれませんが、どこで呼ばれているのかわからず、 疑問に思った次第です。初歩的な質問で申し訳ありません。 — まにまに {2015-12-04 (金) 11:25:01}
  • まにまにさん、ずっと質問に気づかず申し訳ありません。Service 内の registerHandlerは、MainActivityのonCreate内にある、upReceiver.registerHandler(updateHandler);で呼んでいます。ここで紹介している方法だと、必要です。 — 中の人 {2016-02-02 (火) 13:21:12}
  • 横から失礼します。MainActivityのonCreate内にある、upReceiver.registerHandler(updateHandler);で呼んでるのは、UpdateReceiver の registerHandler ですよね?まにまに さんの質問は、UpdateService の registerHandler は必要ですか?とのことです。 — ひで太朗 {2016-06-19 (日) 19:20:56}
  • 横から失礼します。MainActivityのonCreate内にある、upReceiver.registerHandler(updateHandler);で呼んでるのは、UpdateReceiver の registerHandler ですよね?まにまに さんの質問は、UpdateService の registerHandler は必要ですか?とのことです。 — ひで太朗 {2016-06-21 (火) 21:41:04}
  • 横から失礼します。本文を読むと、サービスからActivityに値を投げる紹介とあります。これにはHandlerを使う方法があると紹介し、一例としてReceiver(レシーバー)でも使えます。と書かれています。さらに、ここでは、一番簡単なサービスからActivytyに値を投げる方法を書いておきます。とされているのに、コードの内容はレシーバーからActivityに値を渡す紹介コードとなっております。おそらくそれらがごっちゃになっているのかと思います。レシーバーと、サービスからのActivityへ値を渡す紹介ページを2つに分けたほうが分かりやすいかと思いますm..m以下、やり方です。Handlerというものを使います。Handlerが色々な値を、サービスやレシーバーからActivityに渡してくれます。 — {2016-07-06 (水) 11:23:03}

Selenim IDEを試してみる

PHP
Webサイト作成 TIPS

Seleniumは、主にJavascriptで実装されたWebアプリケーションのテストツールです。FireFoxのアドオンSelenium IDEで、Seleniumのテストケース作成を支援するツールですが、単体でもブラウザー操作を記録・再生するツールとして利用できます。

インストール

下記サイトから、ダウンロードしてインストールできます。
https://addons.mozilla.org/ja/firefox/addon/2079

ブラウザー操作の記録

  1. Firefoxの「ツール」→「Selenium」から、SeleniumIDEを起動します。
  2. SeleniumIDE起動後は、ブラウザー操作を記録するモードになっています。
  3. 適当なサイトを開いて、フォームに何か入力したり、リンクやボタンをクリックすると操作した内容が記録されます。
  4. ブラウザー操作の記録を停止します。
    • 下図の矢印にあるボタンで、ブラウザー操作の記録のオン・オフ切り替わります。

    selenium-ide-record.png

    ブラウザー操作の再生

    1. 「現在のテストケースを実行」ボタンをクリックすると、先ほど記録した操作が自動で再生されます。
      • 下図の矢印にあるボタンが、「現在のテストケースを実行」ボタンです。

      selenium-ide-replay.png

      • Seleniumから派生したもので、Molybdenumというものもあります。 — AG {2010-08-23 (月) 18:21:57}

ScrollViewの子LinearLayoutでheightをmatch_parentにできない

ScrollViewの子のLinearLayoutでheightをmatch_parentにできない時

Androidアプリの画面レイアウトをxmlで編集しているとき、
ScrollViewの入れ子にしてあるLinearLayoutでどうしてもheightをmatch_parentにしたいことがありました。

 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </LinearLayout>
 
 </ScrollView>

しかし、子のLinearLayoutでandroid:layout_height=”match_parent”とすると、
「This LinearLayout should use android:layout_height=”wrap_content”」という警告が出ます…
警告を無視してレイアウトの編集を断行しても、heightがmatch_parentになってくれてないことに気付きます(T_T)

結論としてはScrollViewの子のLinearLayoutでは性質上、heightをmatch_parentにすることはできないようです。

なので親のScrollViewをLinearLayout等に変えるか、もしくはmatch_parentにするのを諦めるかという選択になります。

今思うと、ページをスクロールさせるんだからwrap_contentでページの高さを自動調整させるのが普通だろという気がします。
どうしてもmatch_parentにしたかった理由は以下に書きますので興味のある方は読んでみてください。

理由

画面がPortraitの時はスクロールバーを使わず、どんな端末でも画面の左下にボタンを表示させ、画面がLandscapeの時はスクロールバーを使って画面が切れないようにしたかった。

結局match_parentを使うのを諦めたのですが、「横画面にした時だけスクロールバーを表示させる」という手法があると思います。
まだ調べられていませんが、発見し次第追記したいと思います。

※追記(5/29)
画面の向きによってレイアウト変更する方法がありました。
手順↓

プロジェクトのresの中に「layout-land」というフォルダを作り、そのフォルダ内にLandscapeの時用のxmlファイルを作ります。
尚、Landscape用のxmlファイルの名前は、「layout」フォルダに入っている該当のxmlファイルと同じにします。

以上でとても簡単に問題が解決できました..!

by 芦野輝明
twitter→https://twitter.com/teriyakiegg

Android

ScrollView can host only one direct child

Android 画面がスクロールしない

ScrollView can host only one direct child

Androidの画面をスクロールさせる、ScrollViewを利用すると、上記のエラーが発生してしまうことがあります。
ScrollViewの中には、子要素を一つしか入れられないのです。

でも、TextViewとかLinearLayoutとか、いっぱい子要素あるじゃん…とか思いますが、要は一つのLinearLayoutで囲んであげれば解決です。

 例)
 <ScrollView
        android:id="@+id/ScrollView01"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
 
     <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:baselineAligned="false"
            android:gravity="center_horizontal"
            android:orientation="vertical" >
             <TextView
                android:id="@+id/explanation"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:text="@string/account_explanation"
                android:textColor="@color/green_text" >
            </TextView>
           …以下中身
      </LinearLayout>
 </ScrollView>