[AS3] interface について 2

2011/11/30

こんにちは、きんくまです。

前回interfaceについて書いたのですけど、書いてて最後に思い出したのでそれをこのエントリーに載せようと思います。

前回の最後にiOSやAndroidのネイティブアプリの開発だとinterfaceを使って、イベントが通知されることが多いことを
思いだしたのでした。
それで、これをAS3で似たように作ってみるとどうなるかです。

下の図のような地図を見せるアプリを作るとします。

地図の部分をMapViewとして、これがドラッグを始めたときと、ドラッグし終わったときに、
別のクラスからそのイベントを取りたいとします。

そこで、そのイベントをinterfaceで定義します。

package
{
	public interface IMapViewDelegate
	{
		function willStartScroll(mapView:MapView,scrollPositionX:Number, scrollPositionY:Number):void;
		function didStopScroll(mapView:MapView,scrollPositionX:Number, scrollPositionY:Number):void;
	}
}

willStartScrollはスクロールを始めたとき、didStopScrollはスクロールが終わったときに実行されます。

MapViewのクラスはこうします。

package
{
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	public class MapView extends Sprite
	{
		private var _mapMask:Shape;
		private var _mapSprite:Sprite;
		public var delegate:IMapViewDelegate;
		
		public function MapView()
		{
			createView();
			_mapSprite.addEventListener(MouseEvent.MOUSE_DOWN, onViewMouseDown);
		}
		
		protected function onViewMouseDown(event:MouseEvent):void
		{
			stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
			_mapSprite.startDrag(false);
			if(delegate != null){
				//delegateに伝える
				delegate.willStartScroll(this, _mapSprite.x, _mapSprite.y);
			}
		}
		
		protected function onStageMouseUp(event:MouseEvent):void
		{
			stage.removeEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
			_mapSprite.stopDrag();
			if(delegate != null){
				//delegateに伝える
				delegate.didStopScroll(this, _mapSprite.x, _mapSprite.y);
			}
		}
		
		private function createView():void
		{
			_mapSprite = new Sprite();
			addChild(_mapSprite);
			var g:Graphics = _mapSprite.graphics;
			g.beginFill(0xaaaaaa, 1);
			g.drawRect(0,0,4000,4000);
			g.endFill();
			g.lineStyle(1,0);
			for(var i:int = 1; i < 40; i++){
				g.moveTo(i * 100, 0);
				g.lineTo(i * 100, 4000);
				g.moveTo(0, i * 100);
				g.lineTo(4000, i * 100);
			}
			_mapSprite.x = -2000;
			_mapSprite.y = -2000;
			
			_mapMask = new Shape();
			addChild(_mapMask);
			g = _mapMask.graphics;
			g.beginFill(0xff0000,1);
			g.drawRect(0,0,450,450);
			g.endFill();
			
			_mapSprite.mask = _mapMask;
		}
	}
}

中にdelegateというプロパティを用意して、その型をさきほど定義したIMapViewDelegateにします。
それで、delegateにイベントを伝えたいときに、その関数を実行します。

このMapViewを使うメインクラスはこうなります。

package
{
	import flash.display.Sprite;
	
	[SWF(width="450",height="450",frameRate="30")]
	public class Interface2 extends Sprite implements IMapViewDelegate
	{
		private var _mapView:MapView;
		
		public function Interface2()
		{
			_mapView = new MapView();
			_mapView.delegate = this; //ここでひもづけ
			addChild(_mapView);
		}
		
		public function willStartScroll(mapView:MapView,scrollPositionX:Number, scrollPositionY:Number):void
		{
			trace("start x:" + scrollPositionX + ", y:" +scrollPositionY);	
		}
		
		public function didStopScroll(mapView:MapView,scrollPositionX:Number, scrollPositionY:Number):void
		{
			trace("stop x:" + scrollPositionX + ", y:" +scrollPositionY);
		}
	}
}

mapViewを作ったあとに、自分自身の参照をdelegateに代入しています。
これをすると、mapViewのイベントをInterfaceに定義された関数経由で受け取ることができます。
mapView側はdelegateの実体がどんなクラスかを知らないのですが、IMapViewDelegateを実装していることを
知っているので、その関数を実行することができます。
delegateのクラスはどんなクラスでもよいので、柔軟性が高いやり方だと思います。

でも、これと同じようなことは、EventDispatcherですることができます。
その場合interfaceは必要ありません。

MapViewは

package
{
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	public class MapView extends Sprite
	{
		public static const SCROLL_START:String = "scroll_start";
		public static const SCROLL_STOP:String = "scroll_stop";
		private var _mapMask:Shape;
		private var _mapSprite:Sprite;
		
		public function MapView()
		{
			createView();
			_mapSprite.addEventListener(MouseEvent.MOUSE_DOWN, onViewMouseDown);
		}
		
		protected function onViewMouseDown(event:MouseEvent):void
		{
			stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
			_mapSprite.startDrag(false);
			dispatchEvent(new Event(SCROLL_START));
		}
		
		protected function onStageMouseUp(event:MouseEvent):void
		{
			stage.removeEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
			_mapSprite.stopDrag();
			dispatchEvent(new Event(SCROLL_STOP));
		}
		
		public function get scrollPositionX():Number
		{
			return _mapSprite.x;
		}
		
		public function get scrollPositionY():Number
		{
			return _mapSprite.y;
		}
		
		private function createView():void
		{
			_mapSprite = new Sprite();
			addChild(_mapSprite);
			var g:Graphics = _mapSprite.graphics;
			g.beginFill(0xaaaaaa, 1);
			g.drawRect(0,0,4000,4000);
			g.endFill();
			g.lineStyle(1,0);
			for(var i:int = 1; i < 40; i++){
				g.moveTo(i * 100, 0);
				g.lineTo(i * 100, 4000);
				g.moveTo(0, i * 100);
				g.lineTo(4000, i * 100);
			}
			_mapSprite.x = -2000;
			_mapSprite.y = -2000;
			
			_mapMask = new Shape();
			addChild(_mapMask);
			g = _mapMask.graphics;
			g.beginFill(0xff0000,1);
			g.drawRect(0,0,450,450);
			g.endFill();
			
			_mapSprite.mask = _mapMask;
		}
	}
}

イベントの文字列 SCROLL_STARTとSCROLL_STOPを用意してあげて、イベントを発行したくなったらdispatchします。

それを受け取る側はこうです。

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	
	[SWF(width="450",height="450",frameRate="30")]
	public class Interface2 extends Sprite
	{
		private var _mapView:MapView;
		
		public function Interface2()
		{
			_mapView = new MapView();
			_mapView.addEventListener(MapView.SCROLL_START, onMapScrollStart);
			_mapView.addEventListener(MapView.SCROLL_STOP, onMapScrollStop);
			addChild(_mapView);
		}
		
		protected function onMapScrollStart(event:Event):void
		{
			var map:MapView = event.currentTarget as MapView;
			trace("start x:" + map.scrollPositionX + ", y:" +map.scrollPositionY);	
		}
		
		protected function onMapScrollStop(event:Event):void
		{
			var map:MapView = event.currentTarget as MapView;
			trace("stop x:" + map.scrollPositionX + ", y:" +map.scrollPositionY);	
		}
	}
}

前回はinterfaceを実装していたのですが、今回は不必要になったのでとってしまいました。
代わりにaddEventListenerをして、イベントを登録しました。

今回はinterfaceとEventDispatcherの2つの仕組みで同じ機能を作ってみました。
これの使い分けなんですが、AS3ではEventDispatcherだけで問題ないかと思います。
ただ、interfaceを使うとEventDisptacherという別のクラスを使わなくてよいので、実行速度が早くなるんじゃないかなと思います。あ、実測したわけじゃないので、適当なこと書いたかも。

では。

LINEで送る
Pocket

[AS3] interface について

2011/11/30

こんにちは。きんくまです。

AS3を勉強しはじめたころ、interfaceの役割が全然わかりませんでした。
それで、最近わかってきたと思うので、書いてみようと思います。

interfaceって何?

簡単にいうと、「合い言葉(interfaceの関数名)を決めておくから、合い言葉を言われたら、自分で決めておいたこと(各自の実装)をそれぞれしてね。」
って言う感じでしょうか。
それで「これらのバラバラに実装されたオブジェクト同士をまとめて動作させたい時」に非常に有効だということです。Arrayとかに全部ごっちゃまぜにつっこんで、interfaceで決めた関数を呼び出すときに非常に役に立ちます。
あと、「interfaceに書いてあること以外に何ができるか知らないけど、interfaceに書いてあることは絶対にできるはずだから、やらせちゃえ!」みたいなことも可能です。これはあとで説明します。

例えば、IMovableっていうinterfaceを作るとします。
これは動くことができるのを定義します。今回はmoveという1つの関数だけを定義しましたが、いくつ入れてもOKです。

package
{
	public interface IMovable
	{
		function move():void;
	}
}

車、飛行機、船の3つのクラスがこのinterfaceを実装するとします。

package
{
	import flash.display.Sprite;
	
	public class Car extends Sprite implements IMovable
	{
		public function Car()
		{
			super();
		}
		
		public function move():void
		{
			trace("地上を走っちゃう実装だよ!");
		}
	}
}

package
{
	import flash.display.Sprite;
	
	public class Airplane extends Sprite implements IMovable
	{
		public function Airplane()
		{
			super();
		}
		
		public function move():void
		{
			trace("空を飛んじゃう実装なんだ!");
		}
	}
}

package
{
	import flash.display.Sprite;
	
	public class Ship extends Sprite implements IMovable
	{
		public function Ship()
		{
			super();
		}
		
		public function move():void
		{
			trace("海の上をすーいすーい!!");
		}
	}
}

moveの中身はtraceの文字列を書き換えただけですが、実際には画面を書き換えたり、プロパティをいじったりする実装が入ると思ってください。
大事なのはこれらをまとめる段階です。

package
{
	import flash.display.Sprite;
	
	public class InterfaceExtends extends Sprite
	{
		private var _movables:Array;
		
		public function InterfaceExtends()
		{
			_movables = [];
			var car:Car = new Car();
			var plane:Airplane = new Airplane();
			var ship:Ship = new Ship();
			_movables.push(car, plane, ship);
			
			
			//ここが大事なところ
			var movable:IMovable;
			for(var i:int = 0; i < _movables.length; i++){
				movable = _movables[i];
				movable.move(); //これがやりたいだけ
			}
		}
	}
}

最後のところで、IMovable型のmovableという変数を宣言して、movable.move()を呼んでいます。
これがキモなのかなと。
Car,Airplane,Shipの3つのクラスはinterfaceが共通という他に、共通点はありません。
(Spriteだっていうのはおいておくとして)
こういう一見関係性がないようなクラスをまとめて動作させたいときがあります。

例えば、ツールアイコンがいくつかならんでいて、そのアイコンを押すと、そのツールのパネルが開くとします。
パネルの操作中の動作の実装はそれぞれに違うんですが、パネルを「開く」のと「閉じる」という動作は共通です。
それで、「開く」と「閉じる」をinterfaceに定義して(IToolPanel)、それらのツールパネルはそれらを実装する(implement)とします。
それらをArrayにキープしておけば、そのArrayの中に入っているものは、確実に「開いて」「閉じる」ことができるわけです。
また、Arrayにキープしないで、ある変数をどこかに持つとして、その変数の型をIToolPanel型にすれば、IToolPanel型を実装しているクラスであれば、どんなクラスでも代入ができるんです。

これは便利なことです。その代入されてるオブジェクト自身は、IToolPanelを実装しているものが代入されているっていうこと以外、実は何が代入されているかわからないのです。

さっきの例に戻ると

package
{
	import flash.display.Sprite;
	
	public class InterfaceExtends extends Sprite
	{
		private var _movables:Array;
		
		public function InterfaceExtends()
		{
			_movables = [];
			var car:Car = new Car();
			var plane:Airplane = new Airplane();
			var ship:Ship = new Ship();
			_movables.push(car, plane, ship);
			
			var movable:IMovable;
			movable = car; //これでも
			movable.move();
			movable = plane; //これでも
			movable.move();
			movable = ship; //これでもOK
			movable.move();
		}
	}
}

それで、何が良いかというと「知らなくていい情報は知らない方がいい。その方がお互いにいい距離を保てる」ということです。なんだか男女の仲のようですが、個人的にはオブジェクト指向の目的の1つはこれなんじゃないかと思っています。
個別の情報を知っているよりも、抽象化された情報だけを知っている方がオブジェクト同士の関係は疎結合になります。
疎結合になればなるほど、他のオブジェクトに適用しやすくなり、汎用性が高まります。
逆に細かい情報を載せれば載せるほど、そのオブジェクトの動きは固くなっていき、汎用性は下がります。

だから、平たく言うと汎用性の高いものは、他のときにも使えていいよね!ということとも言えます。
ベースとなる汎用性の高いものをあらかじめ作っておけば、あとは個別の条件とその間を埋めるものに手をかければよいと。

なんだか話がそれましたけど、最近「なるべく疎結合になるように」という方針をどこかで考えながらプログラムしています。

さて、interfaceについていいよ!と書いてきましたけど、使い過ぎもダメかと。無理矢理適用させるとかダメかと。
「実装の中身がバラバラだけど、こいつらまとめてなんとかしないと!」という時に、「そういえば、interfaceってあったっけ?」ぐらいかなと思ってます。

あ、そうだ、あとiOSとAndroidのネイティブアプリの開発してて思ったのだけど、これもinterfaceが出てきてて(iOSの場合はprotocol)それの場合は、今回紹介したのとはちょっと違う使い方です。感覚的にはAS3のEventDispatcherみたいなのだけれど、、。ある特別なクラスがあって、そこからイベントを受け取ろうとしたときに、そのクラスで定義されているinterfaceを実装してあげるとそれが呼ばれてヤッホーいみたいな。とにかくそんな感覚です。
こういう使い方は、自分はASでは全然したことないけど、ひょっとしてこっちがinterfaceの機能の本命だったりして。
なんか自信ありげにエントリ書いてみたけど、後から不安になってきた。

とにかく、、それで、もう気がついている方もいらっしゃると思いますが、オブジェクト指向の「継承 extend」を使うことでinterfaceと同じような目的を果たすことができます。これは次回のエントリーで書きます。

ではでは。

LINEで送る
Pocket

[JavaScript] AS3のEventDispatcherみたいにObserverパターン

2011/11/29

こんにちは。きんくまです。

AS3のEventDispatcherは便利だと思います。JSでは特に用意されていないので作りました。
こんな感じです。

var kinkuma = {}; //package name
(function(pkg){
	pkg.Event = function(){
		this.target;
		this.context;
	};
	pkg.Observer = function(){
		this.listeners = {};
	};
	pkg.Observer.prototype = {
		addObserver:function(type, listener, context){
			var listeners = this.listeners;
			if(!listeners[type]){
				listeners[type] = [];
			}
			listeners[type].push([listener, context]);
		},
		removeObserver:function(type, listener){
			var listeners = this.listeners;
			if(listeners[type]){
				var i;
				var len = listeners[type].length;
				for(i = len - 1; i >= 0; i--){
					var arr = listeners[type][i];
					if(arr[0] == listener){
						listeners[type].splice(i, 1);
					}
				}
			}
		},
		notify:function(type){
			var listeners = this.listeners;
			var e = new pkg.Event();
			e.target = this;
			if(listeners[type]){
				var i;
				var len = listeners[type].length;
				for(i = 0; i < len; i++){
					var arr = listeners[type][i];
					e.context = arr[1];
					arr[0](e);
				}
			}
		}
	}
})(kinkuma);

使い方1 簡単に使ってみる

簡単なクラスを1つ作りました。

var MyData = function(){
	this.change = function(){
		this.notify("CHANGE"); //trigger
	};
};
MyData.prototype = new kinkuma.Observer(); //extends Observer

function hello(){
	alert('hello');
}

var d = new MyData();
d.addObserver("CHANGE", hello); //linkage with hello function
d.change(); //let's try

使い方 2 MVCパターンでAS3のようにやってみる

>> このページを見てみてください。

ボタンを押すと、車が右に動きます。
でも、200pxを超えると、動くのをやめます。

MVCパターンを使ってみました。
MVCは知っていると便利な考え方なので、今度記事を書いてみたいと思っています。

<html>
<head>
<meta charset="UTF-8" />
<script type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script type="text/javascript">

var kinkuma = {}; //package name
(function(pkg){
	pkg.Event = function(){
		this.target;
		this.context;
	};
	pkg.Observer = function(){
		this.listeners = {};
	};
	pkg.Observer.prototype = {
		addObserver:function(type, listener, context){
			var listeners = this.listeners;
			if(!listeners[type]){
				listeners[type] = [];
			}
			listeners[type].push([listener, context]);
		},
		removeObserver:function(type, listener){
			var listeners = this.listeners;
			if(listeners[type]){
				var i;
				var len = listeners[type].length;
				for(i = len - 1; i >= 0; i--){
					var arr = listeners[type][i];
					if(arr[0] == listener){
						listeners[type].splice(i, 1);
					}
				}
			}
		},
		notify:function(type){
			var listeners = this.listeners;
			var e = new pkg.Event();
			e.target = this;
			if(listeners[type]){
				var i;
				var len = listeners[type].length;
				for(i = 0; i < len; i++){
					var arr = listeners[type][i];
					e.context = arr[1];
					arr[0](e);
				}
			}
		}
	}
})(kinkuma);

function trace(str){
	console.log(str);
}

var DataEvent = {
	CHANGE:"change"
}

//Model
var CarData = function(){
	this.posX = 0;
};
CarData.prototype = new kinkuma.Observer();
CarData.prototype.setPosX = function(aPosX){
	this.posX = aPosX;
	this.notify(DataEvent.CHANGE);
};

//View
var CarView = function(data){
	this.cardata = data;
	this.view;
	this.cardata.addObserver(DataEvent.CHANGE, this.onCardataChange, this);
	this.createView();
};
CarView.prototype.createView = function(){
	$('body').append('<div id="carview" style="width:60px;height:30px;background-color:#aaa;position:absolute;top:100px;left:0px;font-size:12px;text-align:center;font-family:sans-serif">mycar</div>');
	this.view = $('#carview');
};
CarView.prototype.onCardataChange = function(e){
	var self = e.context; //CarView instance
	var data = e.target; //observer instance
	self.view.css({left:data.posX + 'px'});
	if(data.posX > 200){
		self.cardata.removeObserver(DataEvent.CHANGE, self.onCardataChange);
	}
};

//Main
var Main = function(){
	this.cardata;
	this.carView;
};
Main.prototype = {
	init:function(){
		var self = this;
		this.cardata = new CarData();
		this.carView = new CarView(this.cardata);
		$('#pushBtn').click(function(){
			self.cardata.setPosX(self.cardata.posX + 20);
		});
	}
}

$(function(){
	var m = new Main();
	m.init();
});

</script>
</head>
<body>

<input id="pushBtn" type="button" value="pushme" />
</body>
</html>
LINEで送る
Pocket

[Android] 非同期処理でネットワークからファイルをダウンロードして外部SDカードに保存する

2011/11/15

非同期処理でネットワークからファイルをダウンロードして、指定のFileに保存するクラスを作りましたです。

今回の使い方アプリは、

ダウンロードボタンを押すと、ネットワークからダウンロードがスタート

終わったら、SDカードに保存します。

■作り方
AndroidManifest.xml にユーザー許可を追加します。

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

main.xml

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

    <Button
        android:id="@+id/loadStartButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Download" />

</LinearLayout>

AsyncFileLoader.java

AsyncTaskを使っています。
あと、InputStreamとOutputStreamを使って
少しずつ入力ファイルを読み込んで、
それを出力ファイルに書き込みます。


package com.kinkuma.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import android.os.AsyncTask;
import android.util.Log;

public class AsyncFileLoader extends AsyncTask<Void, Void, Boolean> {
	private final String TAG = "AsyncFileLoader";
	private String _urlStr;
	private URL _url;
	private URLConnection _urlConnection;
	private final int TIMEOUT_READ = 5000;
	private final int TIMEOUT_CONNECT = 30000;
	private InputStream _inputStream;
	private BufferedInputStream _bufferedInputStream;
	private FileOutputStream _fileOutputStream;
	private File _outputFile;
	private byte[] buff = new byte[5 * 1024];
	
	private int _totalByte = 0;
	private int _currentByte = 0;
	
	public AsyncFileLoader(String urlStr, File outputFile){
		this._urlStr = urlStr;
		this._outputFile = outputFile;
	}
	
	@Override
	protected Boolean doInBackground(Void... params) {
		
		if(isCancelled()){
			return false;
		}
		
		try{
			int len;
			while((len = _bufferedInputStream.read(buff)) != -1){
				_fileOutputStream.write(buff, 0, len);
				_currentByte += len;
				//publishProgress();
				if(isCancelled()){
					break;
				}
			}
	
		}catch(IOException e){
			Log.d(TAG, "error on read file:" + e.toString());
			return false;
		}
		return true;
	}
	
	@Override
	protected void onPreExecute(){
		try{
			connect();
		}catch(IOException e){
			Log.d(TAG, "error on preExecute:" + e.toString());
			cancel(true);
		}
	}
	/*
	@Override
	protected void onProgressUpdate(Void... progress){
		
	}
	*/
	@Override
	protected void onPostExecute(Boolean result){
		if(result == true){
			try{
				close();
			}catch(IOException e){
				Log.d(TAG, "error on postExecute:" + e.toString());
			}
		}else{
			Log.d(TAG, "result: load error");
		}
	}
	
	private void connect() throws IOException
	{
		_url = new URL(_urlStr);
		_urlConnection = _url.openConnection();
		_urlConnection.setReadTimeout(TIMEOUT_READ);
		_urlConnection.setConnectTimeout(TIMEOUT_CONNECT);
		_inputStream = _urlConnection.getInputStream();
		_bufferedInputStream = new BufferedInputStream(_inputStream, 1024 * 5);
		_fileOutputStream = new FileOutputStream(_outputFile);
		
		//_totalByte = _bufferedInputStream.available(); //this is not work
		_totalByte = _urlConnection.getContentLength();
		_currentByte = 0;
	}
	
	private void close() throws IOException
	{
		_fileOutputStream.flush();
		_fileOutputStream.close();
		_bufferedInputStream.close();
	}
	
	public int getLoadedBytePercent()
	{
		if(_totalByte <= 0){
			return 0;
		}
		//Log.d(TAG, Integer.toString(_currentByte) + ":" + Integer.toString(_totalByte));
		return (int)Math.floor(100 * _currentByte/_totalByte);
	}
}

main activity

SDカードのパスを拾うために、Environment.getExternalStorageDirectory()を使いました。

package com.myprogress;

import java.io.File;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.kinkuma.util.AsyncFileLoader;

public class DownloadingAndProgressbarActivity extends Activity {
    private final String TAG = "DownloadSample";
	private Button _loadStartButton;
	private ProgressDialog _progressDialog;
	private Handler _progressHandler;
	private final String VIDEO_URL = "http://192.168.1.0/video/sample_movie.mp4";
	private AsyncFileLoader _fileLoader;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        _loadStartButton = (Button)findViewById(R.id.loadStartButton);
        _loadStartButton.setOnClickListener(new View.OnClickListener() {
		
			@Override
			public void onClick(View v) {
				initFileLoader();
				showDialog(0);
				_progressDialog.setProgress(0);
				_progressHandler.sendEmptyMessage(0);
			}
		});
        
        _progressHandler = new Handler(){
        	public void handleMessage(Message msg){
        		super.handleMessage(msg);
        		if(_fileLoader.isCancelled()){
        			_progressDialog.dismiss();
        			Log.d(TAG, "load canceled");
        		}
        		else if(_fileLoader.getStatus() == AsyncTask.Status.FINISHED){
        			_progressDialog.dismiss();
        		}else{
        			_progressDialog.setProgress(_fileLoader.getLoadedBytePercent());
        			_progressHandler.sendEmptyMessageDelayed(0, 100);
        		}
        	}
        };
    }
    
    @Override
    protected void onPause(){
    	Log.d(TAG, "onPause");
    	super.onPause();
    	cancelLoad();
    }
    
    @Override
    protected void onStop(){
    	Log.d(TAG, "onStop");
    	super.onStop();
    	cancelLoad();
    }
    
    private void cancelLoad()
    {
    	if(_fileLoader != null){
    		_fileLoader.cancel(true);
    	}
    }
    
    private void initFileLoader()
    {
    	File sdCard = Environment.getExternalStorageDirectory();
    	File directory = new File(sdCard.getAbsolutePath() + "/SampleFolder");
    	if(directory.exists() == false){
    		directory.mkdir();
    	}
    	File outputFile = new File(directory, "myvideo.mp4");
    	_fileLoader = new AsyncFileLoader(VIDEO_URL, outputFile);
    	_fileLoader.execute();
    }
    
    @Override
    protected Dialog onCreateDialog(int id){
    	switch(id){
    		case 0:
    			_progressDialog = new ProgressDialog(this);
    			_progressDialog.setIcon(R.drawable.ic_launcher);
    			_progressDialog.setTitle("Downloading files..");
    			_progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    			_progressDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Hide", new DialogInterface.OnClickListener() {
					
				@Override
				public void onClick(DialogInterface dialog, int which) {
						Log.d(TAG, "hide");
					}
				});
    			_progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
					
					@Override
					public void onClick(DialogInterface dialog, int which) {
						Log.d(TAG, "cancel");
						cancelLoad();
					}
				});
    	}
		return _progressDialog;
    }
}
LINEで送る
Pocket

[Android] シンプルなリピートカウント

2011/11/15

AndroidでFlashのenterframeやりたかったのです。

それでTimerとTimerTaskというのを使ったら、エラーになったのです。
どうやらViewと別のスレッドからViewの中をいじろうとするとエラーになるらしい。

HandlerとRunnableを使うとOKみたいです。
あと、ずっとリピートできないんで、中でもう一回HandlerのsetDelayを呼んでます。

スタートボタンを押せば、カウントアップしてラベルに値が出ます。
ストップボタンを押せば、カウントアップが止まります。

main.xml

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

    <TextView
        android:id="@+id/currentTimeLabel"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/startButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Start" />

        <Button
            android:id="@+id/stopButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Stop" />

    </LinearLayout>

</LinearLayout>

TimerSampleActivity.java

package com.mytimer;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class TimerSampleActivity extends Activity implements View.OnClickListener{
    private final String TAG = "TimerSample";  
	
	private Button mStartButton;
	private Button mStopButton;
	public int currentTime;
	private Handler _myHandler;
	private TextView mCurrentTimeLabel;
	private Runnable _myTask;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        currentTime = 0;
        mStartButton = (Button)findViewById(R.id.startButton);
        mStopButton = (Button)findViewById(R.id.stopButton);
        mCurrentTimeLabel = (TextView)findViewById(R.id.currentTimeLabel);
        mStartButton.setOnClickListener(this);
        mStopButton.setOnClickListener(this);
    }

	@Override
	public void onClick(View v) {
		if(v.getId() == R.id.startButton){
			Log.d(TAG, "start");
			cancelTimer();
			_myTask = new MyTimerTask();
			_myHandler = new Handler();
			_myHandler.postDelayed(_myTask, 100);
		}
		else if(v.getId() == R.id.stopButton){
			Log.d(TAG, "stop");
			mCurrentTimeLabel.setText("stop");
			cancelTimer();
		}
		
	}
	
	private void cancelTimer()
	{
		if(_myHandler != null){
			_myHandler.removeCallbacks(_myTask);
		}
	}
	
	private class MyTimerTask implements Runnable
	{
		@Override
		public void run() {
			currentTime++;
			mCurrentTimeLabel.setText(Integer.toString(currentTime));
			
			_myHandler.postDelayed(this, 100);
		}
	}
}

もと記事です。公式ページです。
Updating the UI from a Timer

LINEで送る
Pocket

[Android] assets や res/raw にあるメディアデータ(動画や音声ファイル)のアクセス方法

2011/11/14

カスタム動画プレイヤーを作ろうとして、MediaPlayerを使ったところ、
うまくいきませんでした。

動画ファイルを assets/ か res/raw に置いていました。

ググったところ。解決方法を発見!

assets/video_file.mp4にファイルを置いていたら

_player = new MediaPlayer();
AssetFileDescriptor afd = getAssets().openFd("video_file.mp4");
_player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
    		
_player.setDisplay(_holder); //_holder is SurfaceHolder of SurfaceView
_player.prepare();

もしくは、res/raw/video_file.mp4にファイルを置いていたら

_player = new MediaPlayer();
 String fileName = "android.resource://" + getPackageName() + "/" + R.raw.video_file;
_player.setDataSource(this, Uri.parse(fileName));
    		
_player.setDisplay(_holder); //_holder is SurfaceHolder of SurfaceView
_player.prepare();

下のページたちがすごく役にたちました。ありがとうございますです。

Discovering Android – Embedding Video in an Android Application
Play audio file from the assets directory

LINEで送る
Pocket

[Android] フルスクリーンの方法

2011/11/14

フルスクリーンにするにはいくつかの方法があります。

method 1)
この方法のポイントはrequestWindowFeatureをonCreateとsetContentViewの間に置くことです。

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE); // to fullscreen
        setContentView(R.layout.main);
    }

method 2)
AndroidManifest.xmlの中のapplicationかactivityにプロパティを追加します。



このページを参考にします。
http://developer.android.com/guide/topics/ui/themes.html#ApplyATheme

LINEで送る
Pocket

ページトップへ戻る