【プログラミング】美人時計のようなもの(Android編)

Tech commitの挑戦状課題で「美人時計のようなもの」があったので、Androidアプリで作成しました。

http://taki-lab.site/meshitero/app_debug.apk

https://github.com/takishita2nd/meshitero_timer_android

コード解説

まずはマニフェストファイルから

  
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.meshitero">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <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" />
</manifest>

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

この一行を書き足します。

これが無いと、AndroidアプリはWebアクセスできません。

package com.example.meshitero;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class DownloadTask extends AsyncTask<String, Void, Bitmap> {
    private Listener listener = null;

    @Override
    protected Bitmap doInBackground(String... param) {
        return downloadImage(param[0]);
    }

    @Override
    protected void onProgressUpdate(Void... progress) {

    }

    @Override
    protected void onPostExecute(Bitmap bmp) {
        if (listener != null) {
            listener.onSuccess(bmp);
        }
    }

    private Bitmap downloadImage(String address) {
        Bitmap bmp = null;

        HttpURLConnection urlConnection = null;

        try {
            URL url = new URL( address );

            // HttpURLConnection インスタンス生成
            urlConnection = (HttpURLConnection) url.openConnection();

            // タイムアウト設定
            urlConnection.setReadTimeout(10000);
            urlConnection.setConnectTimeout(20000);

            // リクエストメソッド
            urlConnection.setRequestMethod("GET");

            // リダイレクトを自動で許可しない設定
            urlConnection.setInstanceFollowRedirects(false);

            // ヘッダーの設定(複数設定可能)
            urlConnection.setRequestProperty("Accept-Language", "jp");

            // 接続
            urlConnection.connect();

            int resp = urlConnection.getResponseCode();

            switch (resp){
                case HttpURLConnection.HTTP_OK:
                    try(InputStream is = urlConnection.getInputStream()){
                        bmp = BitmapFactory.decodeStream(is);
                        is.close();
                    } catch(IOException e){
                        e.printStackTrace();
                    }
                    break;
                case HttpURLConnection.HTTP_UNAUTHORIZED:
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            Log.d("debug", "downloadImage error");
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }

        return bmp;
    }

    void setListener(Listener listener) {
        this.listener = listener;
    }

    interface Listener {
        void onSuccess(Bitmap bmp);
    }
}

AndroidでWebアクセスするには、本体スレッドでは動かすことはできないので、非同期タスクをつかって、こちらで動かす必要があります。

AsyncTaskインターフェースを実装したクラスを新規に作成して、こちらに実際に画像をダウンロードする処理を作成します。

doInBackground()では、実際に行うタスク処理を書きます。ここでは実際に画像ファイルをダウンロードする処理を記載します。

onProgressUpdate()では、タスク状態が変わったときに実行される処理を書きます。ここでは特に何もしていません。

onPostExecute()では、タスク処理終了後に実行する処理を書きます。ここでは、setListener()にて設定したコールバック処理を実行させています。

package com.example.meshitero;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity {
    private DownloadTask task = null;
    private ImageView imageView = null;
    private Timer timer = null;
    private String url = "https://taki-lab.site/meshitero/img/time_%02d_%02d.jpg";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = findViewById(R.id.imageView);
        timer = new Timer();
        timer.schedule(new MyTimerTask(), 0, 60000);
    }

    @Override
    protected void onDestroy() {
        task.setListener(null);
        super.onDestroy();
    }

    private DownloadTask.Listener createListener() {
        return new DownloadTask.Listener() {
            @Override
            public void onSuccess(Bitmap bmp) {
                imageView.setImageBitmap(bmp);
            }
        };
    }

    private String getTimer() {
        Calendar cTime = Calendar.getInstance();
        int min = cTime.get(Calendar.MINUTE);
        if(min < 30) {
            min = 0;
        } else {
            min = 30;
        }
        Log.d("debug",String.format(url, cTime.get(Calendar.HOUR_OF_DAY), min));
        return String.format(url, cTime.get(Calendar.HOUR_OF_DAY), min);
    }

    class MyTimerTask extends TimerTask {

        @Override
        public void run() {
            task = new DownloadTask();
            task.setListener(createListener());
            task.execute(getTimer());
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@android:color/background_light" />
</androidx.constraintlayout.widget.ConstraintLayout>

MainlyActivityでは、内部クラスにTimerTask を実装したMyTimerTaskクラスを作成しています。

タイマー処理はこれがないとダメらしい。

run()に、実際にタイマー処理で実行される処理を書きます。ここでは、DownloadTask()を実行させています。

onCreate()では、timerクラスを使って MyTimerTask をスケジューリングしています。第二引数は初回実行までの時間、第三引数には、実行間隔を指定します。

setListener()にcreateListener()メソッドを渡すことで、 MyTimerTaskのタスク処理が実行されたあと、 createListener()が実行されるように設定しています。

ここでは、ImageViewに読み込んだ画像を設定しています。

時間取得はCalendarクラスを使用します。こちらの方が推奨された処理らしいです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください