Hatena::ブログ(Diary)

Android開発覚書:Android Cafe

2011-04-28 アナログ時計のウィジェットの作り方

アナログ時計のウィジェットの作り方


Androidでも特徴的なウィジェットについて。

Androidウィジェットは画面に常駐してくれる便利なアプリです。

作り方は通常の「Activity」ではなく「AppWidgetProvider」のクラスを利用して作ります。

「Activity」とは作法も随分違い制限もありますが、使い方によっては色々なものが作れます。

「AppWidgetProvider」は「BroadcastReceiver」の派生クラスで、端末のイベントなどを様々な形で受け取ることが出来ます。

とはいえ、今回はあまりその辺りを意識しないアナログ時計のウィジェットを作ってみます。

開発環境は以下で作ります。


設定項目設定値
Project name WidgetSample01
Application name WidgetSample01
Package name net.android_cafe.WidgetSample01
Create Activity なし
Min SDK Version 4

「Activity」は作成しません。

あとで、「AppWidgetProvider」のクラスを作ります。

Eclipseの「src」の部分で右クリックをして、新規クラスの作成を行います。

Nameでは「WidgetSample01」とし、Superclassを「android.appwidget.AppWidgetProvider」とします。

こちらには今回のプロジェクトではこの後、記述や修正いりません。

開発環境ができたら、まずマニフェストファイルにウィジェットであるという行を追加します。

<receiver android:name=".WidgetSample01" 
    android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider" 
        android:resource="@xml/appwidget" />
</receiver>

ここでの注目は「android:resource="@xml/appwidget" />」の行です。

ウィジェットリソースファイルを、「xmlディレクトリ以下の「appwidget」に置いてあるよという記述になります。

そこで、「res」ディレクトリ以下に「xml」というファイルを作成し、以下の「appwidget」という名前のxmlファイルを作成します。

<?xml version="1.0" encoding="UTF-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="72dip"
    android:minHeight="72dip"
    android:initialLayout="@layout/main"
    android:updatePeriodMillis="0"
    />

ウィジェットでは定期的に繰り返しの作業を行う為に、「updatePeriodMillis」という値を設定して、定期的なupdateをかけさせることが出来ます。

ただ、推奨は30分以上なので(小さい値をセットしても30分になってしいます。)あまり頻繁に繰り返しを行うことはできません。
(実際にはデジタル時計などは「updatePeriodMillis」を使わずにupdateをかけさせていますが。)

今回はupdateでの処理は必要ないので、「0」となっています。

android:minWidth」と「android:minHeight」はウィジェットの大きさです。

Androidではひとつの画面が4×4で構成されています。
セル数の幅は「(74×セル数)- 2」で計算します。

縦1セル、横2セルの時は以下の値になります。

android:minWidth="146dip"
android:minHeight="72dip"



次にレイアウトですが、初期のTextViewを置き換えます。

<AnalogClock
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:dial="@drawable/appwidget_clock_dial"
    android:hand_hour="@drawable/appwidget_clock_hour"
    android:hand_minute="@drawable/appwidget_clock_minute" />

ここで時計の背景、時間の短針、分の長針を画像で指定しています。

「res」フォルダ以下に「drawable」のフォルダを作成し、それぞれの画像ファイルを置きます。

ちなみに今回の画像ファイルは以下のような適当なものを置きました。

f:id:android-cafe:20110428121359p:image   f:id:android-cafe:20110428121357p:image   f:id:android-cafe:20110428121356p:image

左から、「back」、「hour」、「minute」の画像です。


Androidの画面作成の注意ですが、実際にdipという単位で指定していますが、この単位がピクセル単位になるわけではありません。

液晶はもっと細かい単位での表示が出来るので、具体的には2倍程度の大きさのピクセル数で作らないと、画像がぼやけて表示されます。


さて、最終的には以下のような設定ファイルになっています。

★AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="net.android_cafe.WidgetSample01"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="4" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <receiver android:name=".WidgetSample01" 
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data android:name="android.appwidget.provider" 
                android:resource="@xml/appwidget" />
        </receiver>


    </application>
</manifest>


★main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <AnalogClock
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:dial="@drawable/back"
        android:hand_hour="@drawable/hour"
        android:hand_minute="@drawable/minute" />
</LinearLayout>


xml/appwidget.xml

<?xml version="1.0" encoding="UTF-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="72dip"
    android:minHeight="72dip"
    android:initialLayout="@layout/main"
    android:updatePeriodMillis="0"
    />


★WidgetSample01.java

package net.android_cafe.WidgetSample01;

import android.appwidget.AppWidgetProvider;

public class WidgetSample01 extends AppWidgetProvider {

}

2011-04-26 Android開発での画面の向き

Android開発での画面の向き


Android開発で画面の横回転などさせずに、固定させたい時があります。

画面の向きは「AndroidManifest.xml」内で「Activity」に対して「android:screenOrientation」で設定します。

画面の向き(ScreenOrientation) - Android Wiki

縦画面で固定の時は以下のような行を追記します。

android:screenOrientation="portrait"

Activity単位で出来るので、画面単位での実施が出来ます。

アプリにもよりますが、無理には切り替え画面の設定をする必要が無いときも多いです。

特にレイアウトが致命的に崩れるものもありますので・・・

そういうものは最初は縦画面のみで作成し、後に横向きのレイアウトを追加する方法もありますね。

2011-04-12 Android開発:ビデオビューを使った動画の表示(インターネットから

ビデオビューを使った動画の表示(インターネットから取得)


ビデオビューを使って、インターネットから動画を再生するプログラムです。
もの凄くシンプルに作っていますが、動画再生の基本になります。

Androidで再生できる動画としては、「MPEG-4 / H.264 / 3GPP」と考えるのが無難です。

Androidプラットフォームでの公式サポートのガイドラインは以下から確認できます。
http://developer.android.com/intl/ja/guide/appendix/media-formats.html

ネットワークで使えるのは以下ですので注意が必要です。
・RTSP (RTP、SDP)
HTTP progressive streaming
HTTP live streaming draft protocol (Android 3.0 and above)

HTTPで使う際にはプログレッシブダウンロードが出来る必要があります。

ffmpegなどを利用してエンコードを行った場合には、ヘッダ構造がおかしくなっているので正しくしてあげる必要があります。
http://labs.unoh.net/2007/11/ffmpeg3gppntti_for_linux.html

この当たりを上手く設定しつつ、動画の設定をする必要があります。

開発環境は以下で作ります。


設定項目設定値
Project name VideoViewSample01
Application name VideoViewSample01
Package name net.android_cafe.VideoViewSample01
Create Activity VideoViewSample01
Min SDK Version 4

Min SDKは「4」にしてますが、7以上の方が無難と言えば無難です。


開発環境ができたら、まずマニフェストファイルにインターネット接続をするという一行を追加します。

ネット経由なんでこれが必要です。

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

次にレイアウトですが、初期のTextViewを置き換えます。

<VideoView android:id="@+id/VideoView01"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"/>

このままでは表示が上に偏ってしまうので、表示を気にするのであれば「LinearLayout」に「android:gravity="center"」などを追加した方が良いでしょう。

さて、次にjavaのファイルです。
BrowserSample01という名前のClassでActivityをスーパークラスとしています。

まずは、オブジェクトの宣言から。

private VideoView video = null;
private String video_url;


続いて、「onCreate()」メソッドに以下のような記述をします。

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
        
    // 動画再生
    video_url = "動画が置いてあるURL";

    video = (VideoView) findViewById(R.id.VideoView01);
    video.setMediaController(new MediaController(this));

    video.setVideoURI(Uri.parse(video_url));
}

完全に「VideoView」任せですが、これだけで動画の再生が可能です。
プログレッシブ再生なので、意外とこの状態でもきちんと動画が再生されます。


さて、最終的には以下のような設定ファイルになっています。

★AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="net.android_cafe.VideoViewSample01"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="4" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".VideoViewSample01"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>

★main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
  >
  <VideoView android:id="@+id/VideoView01"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"/>
</LinearLayout>

★VideoViewSample01.java

package net.android_cafe.VideoViewSample01;

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.widget.MediaController;
import android.widget.VideoView;

public class VideoViewSample01 extends Activity {
    private VideoView video = null;
    private String video_url;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // 動画再生
        video_url = "動画が置いてあるURL";
        
        video = (VideoView) findViewById(R.id.VideoView01);
        video.setMediaController(new MediaController(this));
                
        video.setVideoURI(Uri.parse(video_url));
    }
}

2011-04-11 Android開発:アプリ内でのブラウザの表示 その2(ブラウザバック、

アプリ内でのブラウザの表示 その2(ブラウザバック、プログレスバー)



前回の記事:アプリ内でのブラウザの表示

前回のところでは本当に簡単なブラウザ表示だけでしたが、あまりにシンプルすぎたので、機能の追加です。

今回の開発環境は以下で行っています。


設定項目設定値
Project name BrowserSample02
Application name BrowserSample02
Package name net.android_cafe.BrowserSample02
Create Activity BrowserSample02
Min SDK Version 4

今回追加する機能は以下の二つです。

・「ブラウザバック」の機能
プログレスバーの表示


アプリ内でWebの表示を行っている時に、Androidの「Back」ボタンを押してしまうと、前回の記事のままでは、Activityごと戻ってしまいます。
Webの閲覧履歴を無視して、Activityの表示前まで戻ってしまうような、若干イケていないインターフェースとなってしまいます。

そこで、javaファイルに以下の記述を加えます。

// 戻るボタンを押したときの動作
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
        webView.goBack();
        return true;
    }
    return super.onKeyDown(keyCode, event);
}


また、重いページの読み込みなどを行っている時に前回のままではブラウザの読み込みをしているかよくわかりません。

そこで、プログレスバーを使って、ページ読み込みをしている時に「読み込んでいる」というバーの表示を行います。


まず、レイアウト側の編集。

「LinearLayout」を使ってしまうとWebViewの下にプログレスバーが表示されてしまうので、重ねて表示するため、「RelativeLayout」を使用します。

そちらを置き換えて、WebViewの下に以下の行を追加します。

<FrameLayout 
    android:id="@+id/ProgressBarWrapper01"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:visibility="gone"
    android:paddingLeft="1dip"
    android:paddingRight="1dip">

    <ProgressBar
        android:id="@+id/ProgressBar01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    </ProgressBar>
</FrameLayout>


さて、次にjavaのファイルです。

webViewのオブジェクトに対して、以下の追加をして読み込み中はプログレスバーを出す表示を行います。

// プログレスバーの表示
webView.setWebChromeClient(new WebChromeClient() {
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        View pBarWrapper = findViewById(R.id.ProgressBarWrapper01);
        ProgressBar pBar = (ProgressBar) findViewById(R.id.ProgressBar01);
        
        
        pBar.setProgress(newProgress);
     
        if (newProgress == 100) {
            pBarWrapper.setVisibility(View.GONE);
        } else {
            pBarWrapper.setVisibility(View.VISIBLE);
        }
    }
});


一応、この記述でWebの表示は行えます。

アプリ内で使うにはもう少し動作の追加をしたいところですが、この2つを入れるだけで、シンプルな表示の時に比べて動作が随分ましになります。


さて、最終的には以下のような設定ファイルになっています。

★AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="net.android_cafe.BrowserSample01"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="4" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".BrowserSample01"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>

★main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <WebView
        android:id="@+id/WebView01"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"/>
        

    <FrameLayout 
        android:id="@+id/ProgressBarWrapper01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:visibility="gone"
        android:paddingLeft="1dip"
        android:paddingRight="1dip">

        <ProgressBar
            android:id="@+id/ProgressBar01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
        </ProgressBar>
    </FrameLayout>
</RelativeLayout>

★BrowserSample02.java

package net.android_cafe.BrowserSample02;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;

public class BrowserSample02 extends Activity {

    private WebView webView;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        webView = (WebView) findViewById(R.id.WebView01);
        webView.setWebViewClient(new WebViewClient());
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setBuiltInZoomControls(true);

        // プログレスバーの表示
        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                View pBarWrapper = findViewById(R.id.ProgressBarWrapper01);
                ProgressBar pBar = (ProgressBar) findViewById(R.id.ProgressBar01);
                
                
                pBar.setProgress(newProgress);
             
                if (newProgress == 100) {
                    pBarWrapper.setVisibility(View.GONE);
                } else {
                    pBarWrapper.setVisibility(View.VISIBLE);
                }
            }
        });
        
        webView.loadUrl("http://www.google.com");
    }
    
    // 戻るボタンを押したときの動作
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
            webView.goBack();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
    
}

2011-04-10 Android開発:アプリ内でのブラウザの表示

アプリ内でのブラウザの表示


Androidアプリ開発をしていると、情報系のものですと、ものによってはWebで表示がどうしても欲しくなってくるときがあります。

あと、アプリ内のlayoutで作るよりもHTMLでページ作るほうが、製作できる人いっぱいいますし、更新などの管理もしやすいですし。

Androidアプリ内では比較的よく使われている、ブラウザの表示をここではあげておきます。

ブラウザの表示は「Web View」を使って行います。

ここでの例は、まず動くだけのシンプルな形ですが。

開発環境は以下で行っています。


設定項目設定値
Project name BrowserSample01
Application name BrowserSample01
Package name net.android_cafe.BrowserSample01
Create Activity BrowserSample01
Min SDK Version 4

開発環境が整ったら、まずマニフェストファイルにインターネット接続をするという一行を追加します。

これが無いと端末エミュレーターの起動時に接続が失敗しますし、忘れにくい部分でもあります。

<uses-permission android:name="android.permission.INTERNET"></uses-permission>


次にレイアウト

初期のHelloWorldではTextViewになっているところを以下に置き換えます。

<WebView
    android:id="@+id/WebView01"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"/>


さて、次にjavaのファイルです。

BrowserSample01という名前のClassでActivityをスーパークラスとしています。

まずは、オブジェクトの宣言から。

private WebView webView;

続いて、「onCreate()」メソッドに以下のような記述をします。

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
        
    webView = (WebView) findViewById(R.id.WebView01);
    webView.setWebViewClient(new WebViewClient());
    webView.getSettings().setJavaScriptEnabled(true);
    webView.getSettings().setBuiltInZoomControls(true);

    webView.loadUrl("http://www.google.com");
}

「webView.getSettings().setJavaScriptEnabled(true);」の行で、JavaScriptを有効にしています。
「webView.getSettings().setBuiltInZoomControls(true)」の行で、Webブラウザでのズーム機能を有効にしています。

この2行は無しでも動くのですが、ここでは一応つけておきます。

こちらでビルドして端末エミュレーターで起動すれば無事googleの画面が出てくるはずです。

WebViewは比較的実装しやすく、ページの見栄えをつけるにはとても便利な機能になります。

ただ、ネットが使えない環境(電車とか)ではページが表示されませんし、重いページの表示ではとても時間がかかってしまいます。

ですので、アプリで使うには多少検討が必要かもしれません。


さて、最終的には以下のような設定ファイルになっています。

★AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="net.android_cafe.BrowserSample01"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="4" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".BrowserSample01"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>

★main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <WebView
        android:id="@+id/WebView01"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"/>
</LinearLayout>

★BrowserSample01.java

package net.android_cafe.BrowserSample01;

import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class BrowserSample01 extends Activity {
    
    private WebView webView;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        webView = (WebView) findViewById(R.id.WebView01);
        webView.setWebViewClient(new WebViewClient());
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setBuiltInZoomControls(true);

        webView.loadUrl("http://www.google.com");
    }
}