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クラスを使用します。こちらの方が推奨された処理らしいです。