2011年9月22日木曜日

[Android] GridView内に非同期で画像を表示する方法

このエントリーをはてなブックマークに追加


【構成】

【AsyncGridViewActivity.java -メインアクティビティー】
package com.ayakix;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.widget.GridView;

public class AsyncGridViewActivity extends Activity {
private GridView gv;
private List urlList;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// グリッドビュー
gv = (GridView) findViewById(R.id.gv);
// ダウンロードURLリスト
urlList = new ArrayList();

urlList.add("http://example.com/test.png");

gv.setAdapter(new ImageGridViewAdapter(this, urlList));
gv.invalidate();
}
}


【ImageDownloadTask.java -非同期の画像ダウンローダー】
package com.ayakix;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.URL;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.util.Log;
import android.widget.ImageView;

public class ImageDownloadTask {
private ImageView iv;
private String url;
private Bitmap bitmap;

public ImageDownloadTask(ImageView iv, String url){
this.iv = iv;
this.url = url;
}

public void execute(){
final Handler mHandler = new Handler();
new Thread(new Runnable() {
public void run() {
bitmap = getBitmap(url);
if(bitmap == null) return;
// ポスト処理
mHandler.post(new Runnable() {
public void run() {
// 画像のセット
iv.setImageBitmap(bitmap);
}
});
}
}).start();
}

/*
* 画像のダウンロード
*/
public Bitmap getBitmap(String url){
Bitmap bitmap = null;
try{
BufferedInputStream in = new BufferedInputStream((InputStream) (new URL(url)).getContent());
bitmap = BitmapFactory.decodeStream(in);
in.close();
} catch (Exception ex){
return null;
}
return bitmap;
}
}


【ImageGridViewAdapter - グリッドビューアダプター】
package com.ayakix;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;

public class ImageGridViewAdapter extends BaseAdapter {
private List urlList;
private LayoutInflater inflater;

static class ViewHolder{
ImageView iv_image;
}

public ImageGridViewAdapter(Context context, List urlList) {
this.urlList = urlList;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder holder;
if (view == null) {
holder = new ViewHolder();
view = inflater.inflate(R.layout.imagegridview, null);
// 画像
holder.iv_image = (ImageView) view.findViewById(R.id.imagegridview_iv_image);
// 画像の非同期DL
new ImageDownloadTask(holder.iv_image, urlList.get(position)).execute();
// 登録
view.setTag(holder);
} else {
holder = (ViewHolder)view.getTag();
}
return view;
}

@Override
public long getItemId(int position) {
return 0;
}

@Override
public Object getItem(int position) {
return null;
}

@Override
public int getCount() {
return urlList.size();
}
}


【imagegridview.xml -グリッドビューの各要素のレイアウト定義】
<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- 写真 -->
<ImageView
android:id="@+id/imagegridview_iv_image"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:adjustViewBounds="true"
/>
</RelativeLayout>


【main.xml - メインアクティビティーのレイアウト定義】
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<GridView
android:id="@+id/gv"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:verticalSpacing="10sp"
android:horizontalSpacing="10sp"
android:numColumns="4"
android:stretchMode="columnWidth"
android:gravity="center"
/>
</LinearLayout>


【AndroidManifest.xml】
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ayakix"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="4" />

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

<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".AsyncGridViewActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>
</manifest>

ポイントとなるImageDownloadTask.java内では、画像のダウンロード処理に対して、Handlerを使用しています。このプログラムを作成する前にはAsyncTaskを利用していたのですが、AsyncTaskは同時に処理できるタスク数が標準では5になっているみたいです(プールサイズが5)。そのため、多くの処理を並列に行えないため、Handlerを使いました。

AndroidのカメラプレビューでOverlayを表示する方法

このエントリーをはてなブックマークに追加

【構成】

【MainActivity.java - メインアクティビティー】

package com.ayakix;

import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new CameraView(this));
addContentView(new CameraOverlayView(this), new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
}
}


【CameraView.java - カメラサーフェースビュー】

package com.ayakix;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.hardware.Camera;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraView extends SurfaceView implements SurfaceHolder.Callback, Camera.PictureCallback, Camera.AutoFocusCallback {
private SurfaceHolder holder;
private Camera camera;

public CameraView(Context context) {
super(context);
holder = getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera = Camera.open();
camera.setPreviewDisplay(holder);
} catch (Exception e) {
}
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
camera.startPreview();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction()==MotionEvent.ACTION_DOWN) {
camera.autoFocus(this);
}
return true;
}

@Override
public void onAutoFocus(boolean success, Camera camera) {
camera.autoFocus(null);
camera.takePicture(null, null, this);
}

@Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);
// bitmap画像をカットする処理など
int w = bitmap.getWidth();
int h = bitmap.getHeight();
// 中央部分の四角形をカット
bitmap = cutBitmap(bitmap, (w-h)/2, 0, h, h);
// 90度回転
bitmap = rotateBitmap90(bitmap);

// とりあえずもう一度カメラプレビュー
camera.startPreview();
} catch (Exception e) {
}
}

/*
* カット
*/
public Bitmap cutBitmap(Bitmap bmp,int x,int y,int w,int h) {
Bitmap result=Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas(result);
canvas.drawBitmap(bmp,-x,-y,null);
return result;
}
/*
* 画像の回転 90度
*/
public Bitmap rotateBitmap90(Bitmap bmp) {
int w=bmp.getWidth();
int h=bmp.getHeight();
Bitmap result=Bitmap.createBitmap(h,w,Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas(result);
canvas.rotate(90,0,0);
canvas.drawBitmap(bmp,0,-h,null);
return result;
}
}


【CameraOverlayView.java - オーバーレイビュー】

package com.ayakix;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;

public class CameraOverlayView extends View {
private int width, height;

public CameraOverlayView(Context context) {
super(context);
setFocusable(true);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh){
// ビューのサイズを取得
width= w;
height= h;
}


/**
* 描画処理
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 背景色を設定
canvas.drawColor(Color.TRANSPARENT);

// 描画するための線の色を設定
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setARGB(100, 0, 0, 0);

// 上枠表示
canvas.drawRect(0, 0, (width-height)/2, height, paint);
// 下枠表示
canvas.drawRect((width-height)/2 + height, 0, width, height, paint);

// 中央十字憑依時
int len = height / 10;
paint.setARGB(255, 255, 0, 0);
canvas.drawLine(width/2, height/2-len, width/2, height/2+len, paint);
canvas.drawLine(width/2-len, height/2, width/2+len, height/2, paint);
// 円表示
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(width/2, height/2, len*5, paint);
}
}


【AndroidManifest.xml】

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ayakix"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="4" />

<uses-permission android:name="android.permission.CAMERA" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.CameraActivity" android:screenOrientation="landscape"></activity>

</application>
</manifest>

2011年9月13日火曜日

Androidでシンプルなタブを作る方法

このエントリーをはてなブックマークに追加
【構成】

【SimpleCustomTabActivity.java - メインとなるActivity】
package com.ayakix;

import android.app.TabActivity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TabHost;
import android.widget.TextView;

public class SimpleCustomTabActivity extends TabActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

TabHost tabHost = getTabHost();
tabHost.getTabWidget().setDividerDrawable(R.drawable.tabdivider);

// タブの初期化
View v1 = new MyView(this, "AA");
tabHost.addTab(tabHost.newTabSpec("AA")
.setIndicator(v1)
.setContent(new Intent(this, SubActivity.class)));
View v2 = new MyView(this, "BB");
tabHost.addTab(tabHost.newTabSpec("BB")
.setIndicator(v2)
.setContent(new Intent(this, SubActivity.class)));
View v3 = new MyView(this, "CC");
tabHost.addTab(tabHost.newTabSpec("CC")
.setIndicator(v3)
.setContent(new Intent(this, SubActivity.class)));
}

private class MyView extends FrameLayout {
private LayoutInflater inflater;
public MyView(Context context) {
super(context);
inflater = LayoutInflater.from(context);
}

public MyView(Context context, String title) {
this(context);
View v = inflater.inflate(R.layout.tabwidget, null);
TextView tv = (TextView) v.findViewById(R.id.tabwidget_tv);
tv.setText(title);
addView(v);
}
}
}


【SubActivity - タブコンテンツとなるActivity】
package com.ayakix;

import android.app.Activity;
import android.os.Bundle;

public class SubActivity extends Activity{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sub);
}
}


【tabback.xml - タブの背景】
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 選択時 -->
<item android:state_selected="true">
<shape android:shape="rectangle">
<solid
android:color="#FF33CC33"
/>
<!-- 角を丸める -->
<corners
android:radius="50sp"
/>
</shape>
</item>
<!-- 非選択時 -->
<item android:state_selected="false">
<shape android:shape="rectangle">
<solid
android:color="#FF3333CC"
/>
</shape>
</item>
</selector>


【tabdivider.xml - タブ間の調整】
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<size
android:width="20px"
/>
<solid
android:color="#00000000"
/>
</shape>


【main.xml - SimpleCustomTabActivityの画面構成】
<?xml version="1.0" encoding="utf-8"?>
<TabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="50sp"
android:layout_marginTop="10sp"
android:layout_marginLeft="10sp"
android:layout_marginRight="10sp"/>
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
</TabHost>


【sub.xml - SubActivityの画面構成】
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FF33CC33">
</LinearLayout>


【tabwidget.xml - タブの画面構成】
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:background="@drawable/tabback">

<TextView
android:id="@+id/tabwidget_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>


【styles.xml - スタイルの定義】
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NoShadowTheme" parent="@android:style/Theme.Light">
<item name="android:windowContentOverlay">@null</item>
</style>
</resources>


【AndroidManifest.xml - マニフェストファイル】
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ayakix"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="4" />

<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@style/NoShadowTheme">
<activity
android:name=".SimpleCustomTabActivity"
android:label="@string/app_name">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SubActivity">
</activity>
</application>
</manifest>



ポイントとなるのは、テーマを設定しているところ。デフォルトのテーマでは、タブとコンテンツの間にドロップシャドウがついてしまう。android:windowContentOverlayをnullにしたテーマを適応することで、ドロップシャドウを無効にする。

2011年9月6日火曜日

Androidで複数ボタンから1つだけ選択するUIの実現

このエントリーをはてなブックマークに追加

少しタイトルがわかりにくいですが、複数のボタンから一つ選択すると、他のボタンは非選択状態になると言ったUIの実現方法です。
これは、ラジオボタンからチェックするボタンを消し、背景画像を用意することで実現できます。

【構成】


【MainActivity.java】
package com.ayakix;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}


【main.xml】
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RadioGroup
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:orientation="horizontal">
<RadioButton android:id="@+id/rb_left"
android:text="Left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:layout_weight="1"
android:button="@null"
android:background="@drawable/back" />
<RadioButton android:id="@+id/rb_center"
android:text="Center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:button="@null"
android:background="@drawable/back" />
<RadioButton android:id="@+id/rb_right"
android:text="Right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:button="@null"
android:background="@drawable/back" />
</RadioGroup>
</LinearLayout>

※android:button="@null"とすることで、ラジオボタンのチェックするボタンが表示されなくなり、自分で設定する背景であるandroid:background="@drawable/back"のみが表示されるようになります。

【back.xml】
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="false">
<color android:color="#FFFF0000" />
</item>
<item android:state_checked="true">
<color android:color="#FF00FF00" />
</item>
</selector>

※falseの場合には非選択状態の背景を、trueの場合には選択状態の背景を設定します。

Androidのタブ間スペースを調整する方法

このエントリーをはてなブックマークに追加


Androidでタブを使う際に、タブ間のスペースを調整する方法についてのメモ。

Androidでデフォルトのタブウィジェットを使うとタブ間適度なスペースを設けてくれますが、カスタムタブを利用すると、スペースがなくなり、タブ同士がくっついてしまいます。
TabWidgetに対してDividerをセットすることで調節可能となります。

【構成】

【MainActivity.java】
package com.ayakix;

import android.app.TabActivity;
import android.os.Bundle;
import android.widget.TabHost;

public class MainActivity extends TabActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

TabHost tabHost = getTabHost();
// Dividerをセットする
tabHost.getTabWidget().setDividerDrawable(R.drawable.tabdivider);

// タブの初期化
tabHost.addTab(tabHost.newTabSpec("AA")
.setIndicator("AA")
.setContent(R.id.content));
tabHost.addTab(tabHost.newTabSpec("BB")
.setIndicator("BB")
.setContent(R.id.content));
tabHost.addTab(tabHost.newTabSpec("CC")
.setIndicator("CC")
.setContent(R.id.content));
}
}


【main.xml】
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:id="@+id/content"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</LinearLayout>
</FrameLayout>>
</TabHost>
</LinearLayout>


【tabdivider.xml】
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<size
android:width="20px"
/>
<solid
android:color="#00000000"
/>
</shape>

※widthの値を変更することにより、タブ間のスペースを調整できます。また、colorを変更することにより、色を変更できます。