Android でラインアートを表示するアプリを書いてみた。
Android端末にインストール可能なapkファイル(たぶんAndroid4.0.3以降じゃないとインストールできない): Lineart.apk
開発環境は MacBook Air + Mac OS X Lion + Eclipse 4.2 Juno.
3つのクラスを作成。
- MainActivity: アプリのエントリポイント的なアクティビティ クラス。
- LineartView クラス: ラインアートの表示をするビュークラス。
- Lineart クラス: ラインアートの動きを制御するクラス。
MainActivity クラス
アプリのエントリポイント的なアクティビティ クラス。
package info.nilab.lineart;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
public class MainActivity extends Activity {
private LineartView lineartView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// layout
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
setContentView(layout);
// speed up button
Button speedUpButton = new Button(this);
speedUpButton.setText("speed up↑");
speedUpButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MainActivity.this.lineartView.upSpeed();
}
});
layout.addView(speedUpButton);
// speed down button
Button speedDownButton = new Button(this);
speedDownButton.setText("speed down↓");
speedDownButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MainActivity.this.lineartView.downSpeed();
}
});
layout.addView(speedDownButton);
// lineart view
lineartView = new LineartView(this);
layout.addView(lineartView);
//Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//Toast.makeText(this, "onCreateOptionsMenu", Toast.LENGTH_SHORT).show();
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
@Override
protected void onStart() {
super.onStart();
//Toast.makeText(this, "onStart", Toast.LENGTH_SHORT).show();
}
@Override
protected void onRestart() {
super.onRestart();
//Toast.makeText(this, "onRestart", Toast.LENGTH_SHORT).show();
}
@Override
protected void onResume() {
super.onResume();
//Toast.makeText(this, "onResume", Toast.LENGTH_SHORT).show();
}
@Override
protected void onPause() {
super.onPause();
//Toast.makeText(this, "onPause", Toast.LENGTH_SHORT).show();
}
@Override
protected void onStop() {
super.onStop();
//Toast.makeText(this, "onStop", Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
//Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
}
}
LineartView クラス
ラインアートの表示をするビュークラス。
package info.nilab.lineart;
import java.lang.ref.WeakReference;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
public class LineartView extends View {
private boolean initialized = false;
private Lineart lineart;
private IntervalDrawHandler handler;
public LineartView(Context context) {
super(context);
}
private void init(){
lineart = new Lineart(5, getWidth(), getHeight());
handler = new IntervalDrawHandler(this);
handler.sendEmptyMessageDelayed(0, handler.getDelayMillis());
}
private static class IntervalDrawHandler extends Handler{
private final WeakReference<View> viewReference;
private long delayMillis = 100L;
IntervalDrawHandler(View view){
viewReference = new WeakReference<View>(view);
}
long getDelayMillis(){
return delayMillis;
}
void setDelayMillis(long delayMillis){
this.delayMillis = delayMillis;
}
@Override
public void handleMessage(Message msg) {
View view = viewReference.get();
if(view != null){
view.invalidate();
sendEmptyMessageDelayed(0, delayMillis);
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(!initialized){
initialized = true;
init();
}
lineart.next();
// 背景を塗りつぶす
canvas.drawColor(Color.WHITE);
// ラインアートを描画する
lineart.draw(canvas);
}
private Float lastTouchX = null;
private Float lastTouchY = null;
private long lastChangeTime = System.currentTimeMillis();
@Override
public boolean onTouchEvent(MotionEvent event) {
// ラインアートの移動速度を変える
switch(event.getAction()){
case MotionEvent.ACTION_UP:
lastTouchX = null;
lastTouchY = null;
break;
case MotionEvent.ACTION_MOVE:
float x = event.getX();
float y = event.getY();
// 移動速度を変えてから0.5秒以内なら、速度を変えない
if(System.currentTimeMillis() - lastChangeTime > 500){
if(lastTouchY != null && x < lastTouchY){
// 上方向に指を動かしたら加速
upSpeed();
lastChangeTime = System.currentTimeMillis();
}else if(lastTouchY != null && x > lastTouchY){
// 下方向に指を動かしたら減速
downSpeed();
lastChangeTime = System.currentTimeMillis();
}
}
lastTouchX = x;
lastTouchY = y;
break;
}
return true;
}
public void upSpeed(){
long delayMillis = (long)(handler.getDelayMillis() / 1.5);
if(delayMillis < 10){
delayMillis = 10;
}
handler.setDelayMillis(delayMillis);
}
public void downSpeed(){
long delayMillis = (long)(handler.getDelayMillis() * 1.5);
if(delayMillis > 10000){
delayMillis = 10000;
}
handler.setDelayMillis(delayMillis);
}
}
Lineart クラス
ラインアートの動きを制御するクラス。
package info.nilab.lineart;
import java.util.*;
import android.graphics.*;
public class Lineart {
private final ArrayList<History<MovingPoint>> point_histories = new ArrayList<History<MovingPoint>>();
private final History<ChangingColor> color_history;
private static class ChangingColor{
private static final Random r = new Random();
private final int value;
ChangingColor(int color){
this.value = color;
}
int value(){
return value;
}
ChangingColor next(){
return new ChangingColor(Color.argb(r.nextInt(256), r.nextInt(256), r.nextInt(256), r.nextInt(256)));
}
}
private static class History<T> {
private final LinkedList<T> list;
private final int max;
History(int max){
this.max = max;
this.list = new LinkedList<T>();
}
void add(T t){
list.add(t);
if(list.size() > max){
list.remove(0); // 先頭を削除
}
}
T get(int location){
return list.get(location);
}
T last(){
return list.get(list.size()-1);
}
int size(){
return list.size();
}
}
private static class MovingPoint{
private static Random random = new Random();
private float x;
private float y;
private float a;
private float b;
private float width;
private float height;
MovingPoint(float x, float y, float a, float b, float width, float height){
this.x = x;
this.y = y;
this.a = a;
this.b = b;
this.width = width;
this.height = height;
}
void random(){
this.x = random.nextFloat() * width;
this.y = random.nextFloat() * height;
this.a = random.nextFloat() < 0.5 ? -1 : 1;
this.b = random.nextFloat() < 0.5 ? -1 : 1;
}
MovingPoint next(float dist){
float x = this.x + this.a * dist;
float y = this.y + this.b * dist;
float a = this.a;
float b = this.b;
if(x < 0){
x = 0;
a = 1;
}
if(x > this.width){
x = this.width;
a = -1;
}
if(y < 0){
y = 0;
b = 1;
}
if(y > this.height){
y = this.height;
b = -1;
}
return new MovingPoint(x, y, a, b, this.width, this.height);
}
}
public Lineart(int historySize, float canvasWidth, float canvasHeight){
// points
for(int i=0; i<historySize; i++){
History<MovingPoint> h = new History<MovingPoint>(historySize);
MovingPoint mp = new MovingPoint(0, 0, 0, 0, canvasWidth, canvasHeight);
mp.random();
h.add(mp);
point_histories.add(h);
}
// color
color_history = new History<ChangingColor>(historySize);
color_history.add(new ChangingColor(Color.argb(255, 255, 0, 0)));
}
public void next(){
// next points
float dist = 10;
for(int i=0; i<point_histories.size(); i++){
MovingPoint nextp = point_histories.get(i).last().next(dist);
point_histories.get(i).add(nextp);
}
// next color
color_history.add(color_history.last().next());
}
public void draw(Canvas canvas){
// drawing lines
for(int i=0; i<point_histories.size()-1; i++){
History<MovingPoint> h1 = point_histories.get(i);
History<MovingPoint> h2 = point_histories.get(i+1);
int size = color_history.size();
for(int j=0; j<size; j++){
MovingPoint p1 = h1.get(j);
MovingPoint p2 = h2.get(j);
Paint paint = new Paint();
paint.setColor(color_history.get(j).value());
paint.setStrokeWidth(4.0f);
paint.setAntiAlias(true);
canvas.drawLine(p1.x, p1.y, p2.x, p2.y, paint);
}
}
}
}
Ref.
- Androidアプリ作成の基本“Activity”とは何か? (1/2) - @IT
- グラフィックス(1)-Viewクラスへの描画 - 愚鈍人
- the moon at dawn: handleleakがうざったい。
- android - This Handler class should be static or leaks might occur: IncomingHandler - Stack Overflow
- Java弱参照メモ(Hishidama's Java Weak reference Memo)
- Android -MotionEventクラス- <MitoRoid>
- タッチイベントを取得する(onTouchEventとMotionEvent) « Tech Booster
tags: android
Posted by NI-Lab. (@nilab)