AndroidとFlickrで神経衰弱
息抜きがてら、前にActionScriptで作った神経衰弱をAndroidで作ってみた。
神経衰弱と言っても普通のではなく、絵柄の代わりにFlickrのタグを使います。
つまり2つの画像のタグに共通するタグがあれば当たり、なければはずれです。
といってもよくわからないので先に動作画面見るのがいいかも。
HitTagのとこに表示されるのが共通のタグです。ない場合は"NONE"が表示される。
で、作るときに一番悩んだのはどのカードがクリックされたかを取得する方法。
ActionScriptでやってたときはイベントリスナーが使えたから楽だったけど、
Android、というかJavaにはそういうのがないみたいで苦労した。
結局下のソースのようにフォーカス移動時のメソッドを設定した。
onCardClickメソッドは本体側の関数。
card.setOnFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChanged(View v, boolean hasFocus) {
onCardClick();
}
});
カードがクリックされたときはrequestFocusメソッドでそのカードにフォーカスを与え
本体側のonCardClickメソッドを呼び出してる。
なんともスマートでないやり方なのであんまり気に入ってないんですが。
こういう場合どうするのが良いんだろうか。
あと画像の読み込みに時間がかかる。
これはスレッドとかを使うべき?スレッドいまいち理解してないけども。
そのうちGridViewを使ったのとかも作るかも。
以下、動作画面とソース。
ソース
ShinkeiTest.java
package net.swelt.android.shinkeitest; import android.app.Activity; import android.os.Bundle; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.view.View; import android.widget.AbsoluteLayout; import android.widget.Button; import android.widget.TextView; import java.util.Vector; import java.net.*; import java.io.*; public class ShinkeiTest extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(new ShinkeiView(this)); } public class ShinkeiView extends AbsoluteLayout { private final int CARD_MAX = 6; private final String DEFAULT_TAG = "cat"; private Button mStartButton; private TextView mStateText; private TextView mInfoText; private Vector<Card> mCards = new Vector<Card>(); private int[] mTestCards = new int[] {-1, -1}; public ShinkeiView(Context context) { super(context); mStartButton = new Button(context); mStartButton.setLayoutParams(new AbsoluteLayout.LayoutParams(-2, -2, 0, 0)); this.addView(mStartButton); mStartButton.setText("Start"); mStartButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { loadImages(CARD_MAX); } }); mStateText = new TextView(context); mStateText.setLayoutParams(new AbsoluteLayout.LayoutParams(-2, -2, 60, 0)); this.addView(mStateText); mStateText.setText("HitTag : NONE"); mInfoText = new TextView(context); mInfoText.setLayoutParams(new AbsoluteLayout.LayoutParams(-2, -2, 0, 0)); this.addView(mInfoText); invalidate(); } private void loadImages(int num) { Card card; ImageInfo info; String url, nextTag = DEFAULT_TAG; Bitmap bmp; mCards.clear(); mTestCards[0] = -1; mTestCards[1] = -1; for( int i = 0; i < num; ++ i ) { info = loadImageInfo(nextTag); if( info == null ) { continue; } bmp = loadImage(info.getImageURL()); if( bmp == null ) { continue; } card = new Card(this.getContext()); mCards.add(card); this.addView(card); card.setImageInfo(info); card.setBitmap(bmp); card.move((i%3)*100 + 20, (i/3)*80 + 30); card.setOnFocusChangeListener(new View.OnFocusChangeListener() { public void onFocusChanged(View v, boolean hasFocus) { onCardClick(); } }); nextTag = info.getRandomTag(); if( nextTag.equals("") ) nextTag = DEFAULT_TAG; } invalidate(); } private Bitmap loadImage(String str) { Bitmap bmp = null; try { URL url = new URL(str); HttpURLConnection http = (HttpURLConnection) url.openConnection(); http.setRequestMethod("GET"); http.connect(); InputStream is = http.getInputStream(); bmp = BitmapFactory.decodeStream(is); if( bmp.width() == 0 || bmp.height() == 0 ) bmp = null; is.close(); } catch(Exception e) { } return bmp; } private ImageInfo loadImageInfo(String tags) { ImageInfo info = null; try { URL url = new URL("http://(プライベートIPアドレス)/loadimageinfo.php?tags="+tags); HttpURLConnection http = (HttpURLConnection) url.openConnection(); http.setRequestMethod("GET"); http.connect(); InputStream is = http.getInputStream(); String str = ""; int c; while( (c = is.read()) != -1 ) str = str.concat(""+(char)c); info = new ImageInfo(); info.convertToInfo(str); is.close(); } catch(Exception e) { } return info; } private void onCardClick() { for( int i = 0; i < mCards.size(); ++ i ) { if( mCards.elementAt(i).isClick() ) { int index = mTestCards[0] == -1 ? 0 : 1; mTestCards[index] = i; mCards.elementAt(i).setScale(0.6f); if( index == 1 ) { if( mTestCards[0] == mTestCards[1] ) { mTestCards[1] = -1; continue; } String tag = tagTest(mTestCards[0], mTestCards[1]); mStateText.setText("HitTag : " + (tag.equals("") ? "NONE" : tag)); mTestCards[0] = -1; mTestCards[1] = -1; } else { for( int j = 0; j < mCards.size(); ++ j ) { if( i == j ) continue; mCards.elementAt(j).setScale(1.0f); } } } } invalidate(); } private String tagTest(int index1, int index2) { Object[] tags1 = mCards.elementAt(index1).getTags(); Object[] tags2 = mCards.elementAt(index2).getTags(); if( tags1 == null || tags2 == null ) return ""; for( int i = 0; i < tags1.length; ++ i ) { String tag1 = (String) tags1[i]; for( int j = 0; j < tags2.length; ++ j ) { if( tags2[j].equals(tag1) ) return tag1; } } return ""; } @Override public void onDraw(Canvas canvas) { for( int i = 0; i < mCards.size(); ++ i ) { mCards.elementAt(i).draw(canvas); } } } }
DisplayObject.java
package net.swelt.android.shinkeitest; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.view.View; import android.widget.AbsoluteLayout; public class DisplayObject extends View { private Bitmap mBitmap = null; private Rect mSrcRect = null; private Rect mDstRect = null; public DisplayObject(Context context) { super(context); this.setLayoutParams(new AbsoluteLayout.LayoutParams(-2, -2, 0, 0)); } public void setBitmap(Bitmap bmp) { if( bmp == null ) return; mBitmap = bmp; mSrcRect = new Rect(0, 0, mBitmap.width(), mBitmap.height()); mDstRect = new Rect(mSrcRect); } public void move(int x, int y ) { ((AbsoluteLayout.LayoutParams) this.getLayoutParams()).x = x; ((AbsoluteLayout.LayoutParams) this.getLayoutParams()).y = y; } public int getImageWidth() { return mBitmap == null ? 0 : mDstRect.width(); } public int getImageHeight() { return mBitmap == null ? 0 : mDstRect.height(); } public void setScale(float scale) { if( scale == 1.0f ) { mDstRect.set(mSrcRect); } else { mDstRect.right = (int) (mSrcRect.right * scale); mDstRect.bottom = (int) (mSrcRect.bottom * scale); } } @Override public void onDraw(Canvas canvas) { if( mBitmap == null ) return; canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null); } }
Card.java
package net.swelt.android.shinkeitest; import android.content.Context; import android.view.MotionEvent; public class Card extends DisplayObject { private ImageInfo mInfo; private boolean mClickFlag = false; public Card(Context context) { super(context); this.setFocusable(true); } public void setImageInfo(ImageInfo info) { if( info == null ) return; mInfo = info; } public boolean isClick() { boolean b = mClickFlag; mClickFlag = false; return b; } public Object[] getTags() { return mInfo == null ? null : mInfo.getTags(); } @Override public boolean onMotionEvent(MotionEvent event) { switch( event.getAction() ) { case MotionEvent.ACTION_DOWN: mClickFlag = true; this.requestFocus(); return true; } return super.onMotionEvent(event); } }
ImageInfo.java
package net.swelt.android.shinkeitest; import java.util.Random; import java.util.StringTokenizer; import java.util.Vector; public class ImageInfo { private String imageURL; private Vector<String> tags; public void convertToInfo(String str) { StringTokenizer strToken = new StringTokenizer(str, "&"); while( strToken.hasMoreTokens() ) { String token = strToken.nextToken(); String key = token.substring(0, token.indexOf("=")); if( key.equals("imageURL")) imageURL = token.substring(token.indexOf("=")+1); else if( key.equals("tags") ) { tags = new Vector<String>(); token = token.substring(token.indexOf("=")+1); StringTokenizer tagsToken = new StringTokenizer(token, ","); while( tagsToken.hasMoreTokens() ) { tags.add(tagsToken.nextToken()); } } } } public String getImageURL() { return imageURL; }; public Object[] getTags() { return tags.toArray(); }; public String getRandomTag() { Random rand = new Random(); return tags.size() == 0 ? "" : tags.elementAt(rand.nextInt(tags.size())); } }
loadimageinfo.php
<?php include('xml.php'); $key = "*****"; $page = 1; $per_page = 100; $tags = isset($_GET['tags']) ? $_GET['tags'] : "cat"; $url = "http://www.flickr.com/services/rest?"; $url .= "method=flickr.photos.search"; $url .= "&api_key=".$key; $url .= "&page=".$page; $url .= "&per_page=".$per_page; $url .= "&tags=".$tags; $xml = file_get_contents($url); $data = XML_unserialize($xml); $photo = $data['rsp']['photos']['photo']; $index = rand(0, $per_page); $id = $photo[$index.' attr']['id']; $server = $photo[$index.' attr']['server']; $secret = $photo[$index.' attr']['secret']; $imageURL = "http://static.flickr.com/".$server."/".$id."_".$secret."_s.jpg"; $url = "http://www.flickr.com/services/rest?"; $url .= "method=flickr.photos.getInfo"; $url .= "&api_key=".$key; $url .= "&photo_id=".$id; $url .= "&secret=".$secret; $xml = file_get_contents($url); $data = XML_unserialize($xml); $tags = $data['rsp']['photo']['tags']['tag']; $tag = ""; for( $i = 0; $i < count($tags) / 2; ++ $i ) { $tag .= $tags[$i]; if( $i != count($tags) / 2 - 1 ) { $tag .= ","; } } echo "imageURL=".$imageURL; echo "&tags=".$tag; ?>