2012年8月29日水曜日

フォトギャラリーの実装について(Android)

色々とやり方はあるみたいですが大きく2つのやり方がありそうなんで試してみました。


まずひとつ目。
Intentからギャラリーを呼び出してそこから画像を選択する方法です。
これはAndroidの機能をそのまま使ってフォトギャラリーを呼び出し選択させる。というやり方。
ギャラリーの呼び出しは
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_PICK);
startActivityForResult(intent, REQUEST_GALLEY);

これだけです。
簡単に説明すると
setType("image/*) と指定することで対象を保存されている画像にします。
setAction(Intent.ACTION_PICK) と指定することでデータを対象データを取得するためのアプリを絞ることができます。
ここで使うとすると ACTION_PICK か ACTION_GET_CONTENT の2種類があると思いますがここでは ACTION_PICK を使用しています。

違いは下図のようなかんじ。
ACTION_PICK      

 ACTION_GET_CONTENT


あとは onActivityResult メソッドをオーバライドして画像を選択したときの処理を加えるだけです。



次にふたつ目。
ContentResolver で画像データを読み込む方法です。
ContentResolver で外部メディアの画像データにアクセスし情報を取得し、あとが GridView とかを使って画面に表示するやり方。

ここではサムネイル表示(GridView)を使用して表示したいので BaseAdapter を拡張して実装しました。

mResolver = getContentResolver();
Cursor cursor = mResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null);

while (cursor.moveToNext()) {
    String name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.TITLE));
    long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID));

    (省略)
}

cursor.close();

ContentResolver#query を使用してメディから情報を取得してあとはぐるぐる回しながらファイル名とIDを取得します。

これらを用いて GridView に表示すればOK。




この2つで参考にした記事


一応、すべてのソースをGitHubにも上げてますので興味のある方はダウンロードしてみてください。

ひとつ目のサンプル(PhotoGalleySample)
https://github.com/hayashida/PhotoGalleySample

ふたつ目のサンプル(PhotoGridSample)
https://github.com/hayashida/PhotoGridSample




2012年8月22日水曜日

AndroidでFacebookやGoogle+みたいなメニューを表示する(続)

昨日書いた記事の続きです。
AndroidでFacebookやGoogle+みたいなメニューを表示する

昨日までのはただ左にメニューを表示するだけだったので
実際にメニューを押したあとに画面を切り替えるところまで実装してみました。

まず、メニューをListViewに書き直します。
<!-- Menu Layout -->
<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:id="@+id/menuLayout"
    android:background="#666666" >

    <ListView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/menuList" >

    </ListView>

</LinearLayout>

次にメインとなる画面の切り替えです。
画面の切り替えですが、レイアウトファイルを切り替えるやり方でなくアクティビティを切り替えるようなやり方で作ってみました。
ここらへんの内容は以下の記事が参考になりました。


ざっくり言うと、あらかじめコンテナという枠を作っておいて
そこに選択したメニューにあったActivityを埋め込むかんじです。
まずは、メインとなる画面となるActivityをActivityGroupに変更して、以下のような関数を作ります。
private void changeActivity(int position) {
    Intent intent = new Intent();
    String activeId = "";

    switch (position) {
        case 1:
            intent.setClass(this, Content1Activity.class);
            activeId = "content1Activity";
            break;
        case 2:
            intent.setClass(this, Content2Activity.class);
            activeId = "content2Activity";
            break;
        case 3:
            intent.setClass(this, Content3Activity.class);
            activeId = "content3Activity";
            break;
        default:
            intent.setClass(this, HomeActivity.class);
            activeId = "homeActivity";
            break;
    }

    // 表示されているビューを削除
    container.removeAllViewsInLayout();

    // 指定されたビューを表示
    Window containerActivity = getLocalActivityManager().startActivity(activeId, intent);
    container.addView(containerActivity.getDecorView());
}

関数の引数 positionがメニューが押された時の番号になりそれに合わせて各Activityを割り当てています。

実際にメニューがクリックされた時の処理は以下。
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
    // メニューを表示・非表示を切り替える
    animMainLayout();

    // ビューを切り替える
    changeActivity(position);
}

これだけです。
animMainLayout()はメニューの表示・非表示を状態に合わせて切り替えるような関数です。

いくつかファイルを分割しながら作ったのでここではすべてのソースを載せてませんが、一応GitHubにもソース置いています。
興味がある方は是非試してみて下さい。
https://github.com/hayashida/LeftMenuSample

一応、以下のような画面になります。


AndroidでFacebookやGoogle+みたいなメニューを表示する

題名の通りです。
FacebookやGoogle+みたいにヘッダーのアイコンを押したら画面の左側にメニューを表示するやり方。

参考記事

上の記事のままでももちろん動作しますが、ちょっと手を加えました。
記事では画面をどこかタッチしたらメニューを表示するというサンプルでしたが、
ヘッダーにボタンを配置して、そのボタンをタッチしたらメニューに表示するというやり方に変えました。

先にレイアウト(main.xml)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
        
    <-- Menu Layout -->
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical"
        android:id="@+id/menuLayout"
        android:background="#666666" >
        
        <TextView
            android:id="@+id/textview01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="@+string/menutext01" />
        
        <View
            android:layout_width="fill_parent"
            android:layout_height="1dp"
            android:background="#ffffff" />
        
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="@+string/menutext02" />
            
        <View
            android:layout_width="fill_parent"
            android:layout_height="1dp"
            android:background="#ffffff" />
        
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="@+string/menutext03" />
        
    </LinearLayout>
    
    <-- Main Layout -->
    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/mainLayout"
        android:background="#ffffff" >
        
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:orientation="horizontal"
            android:background="#aaaaaa"
            android:id="@+id/header" >
            
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="15sp"
                android:id="@+id/headerButton"
                android:text="@+string/headerbutton" />
            
            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:textSize="25sp"
                android:textColor="#4f4f4f"
                android:text="@+string/headertext" />
            
        </LinearLayout>
        
        <ScrollView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/header" >
            
            <LinearLayout
                android:layout_width="fill_parent"
                android:layout_height="wrap_content" >

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="30sp"
                    android:textColor="#4f4f4f"
                    android:text="@+string/maintext" />
                
            </LinearLayout>
            
        </ScrollView>
        
    </RelativeLayout>

    
</FrameLayout>

一応、リソースファイルも(string.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">LeftMenuSample</string>
    
    <string name="menutext01">Menu1</string>
    <string name="menutext02">Menu2</string>
    <string name="menutext03">Menu3</string>
    
    <string name="headertext">サンプルアプリ</string>
    <string name="headerbutton"><</string>
    
    <string name="maintext">メインのレイアウト</string>

</resources>

Activityのソース(LeftMenuSampleActivity.java)
package jp.kuseful.leftmenusample;

import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.RelativeLayout;
public class LeftMenuSampleActivity extends Activity
    implements OnClickListener {
    
    private int displayWidth = 0;
    private int displayHeight = 0;
    private int scrollX = 0;
    private boolean isOpenMenu = false;
    private RelativeLayout mainLayout = null;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // メインレイアウトを取得
        mainLayout = (RelativeLayout)findViewById(R.id.mainLayout);
        
        // メニュー表示ボタンを取得しクリックイベントを追加
        Button headerButton = (Button)findViewById(R.id.headerButton);
        headerButton.setOnClickListener(this);
        
        // ディスプレイ情報を取得
        DisplayMetrics metrics = new DisplayMetrics();
        
        Display display = getWindowManager().getDefaultDisplay();
        display.getMetrics(metrics);
        displayWidth = display.getWidth();
        displayHeight = display.getHeight();
        
        // メニューをどのくらい表示するか
        scrollX = displayWidth - (int)(metrics.scaledDensity * 200);
    }

    @Override
    public void onClick(View v) {
        if (!isOpenMenu) {
            isOpenMenu = true;
            
            // メニューを表示するアニメーションを設定
            TranslateAnimation anim = new TranslateAnimation(0, scrollX, 0, 0);
            anim.setAnimationListener(new AnimationListener() {
                
                public void onAnimationStart(Animation animation) {}
                
                public void onAnimationRepeat(Animation animation) {}
                
                public void onAnimationEnd(Animation animation) {
                    mainLayout.layout(scrollX, 0, displayWidth + scrollX, displayHeight);
                    mainLayout.setAnimation(null);
                }
            });
            
            anim.setDuration(300);
            mainLayout.startAnimation(anim);
        } else {
            isOpenMenu = false;
            
            // メニューを非表示するアニメーションを設定
            mainLayout.layout(0, 0, displayWidth, displayHeight);
            TranslateAnimation anim = new TranslateAnimation(scrollX, 0, 0, 0);
            anim.setAnimationListener(new AnimationListener() {
                
                public void onAnimationStart(Animation animation) {}
                
                public void onAnimationRepeat(Animation animation) {}
                
                public void onAnimationEnd(Animation animation) {
                    mainLayout.setAnimation(null);
                }
            });
            
            anim.setDuration(300);
            mainLayout.startAnimation(anim);
        }
    }
}


こんなかんじです。
長くはなりましたが何も難しいことはしてないので簡単に試すことができると思います。

実行イメージは、こんなかんじ。

続きも書いたんで参考までにどうぞ
http://teru2-bo2.blogspot.jp/2012/08/androidfacebookgoogle_22.html




2012年8月21日火曜日

Androidでページめくりを実装する

フリック操作でページめくりを実装するのに「ViewFlipper」クラスを使うといいという記事を見たので試してみました。
というか参考にした記事をそのまま書いていけば動きました。


参考記事

1つ目の方はViewFlipperの使い方が説明されています。
けっこうわかりやすくてまずはここからってかんじ。

2つ目の方は同じくViewFlipperも実装していますがそれとは別にタッチ操作を取得してそこからページめくりを実装するコードも載っています。

どちらも参考になるページでしたので興味のある方は試してみて下さい。


で、私ですが2つ目の方のコードを少しさわってタッチ操作でページめくりするときにアニメーションを追加してみました。

コードはけっこう長くなってるんでGitHubに。。。
https://github.com/hayashida/ViewFlipperSample



ページめくりといえばiBooksみたいに本当に本をめくっているかのような動きだったり
Flipboardみたいにちょっと違った動きだったりというのが格好いいですが
いきなりは無理なんでぼちぼち勉強していきます。



2012年8月17日金曜日

FuelPHPをWindowsで動作させるときにハマったこと

FuelPHPでスタイルとかイメージとかを出力するとき
<?php echo echo Asset::css('layout.css'); ?>

という書き方をすると思います。
出力されたソースを見てみると
<link type="text/css" rel="stylesheet" href="http://localhost/fuelphp/public/assets/css/layout.css?1344505211" />

特に問題ありません。

が、これをWindows環境(Windows Server 2008 R2 + Apache)で動かしてみると
<link type="text/css" rel="stylesheet" href="http://localhost/fuelphp/public/assets//css//layout.css?1344587285">

こうなってしまいました。

こうなることで何が問題になるかですが
CSSの中でimageのパスを相対で指定している場合
例えば
background: #fff url(../img/logo.png) no-repeat;

CSSファイルの前の // が邪魔してCSSフォルダと同じ階層にあるはずのimgフォルダまでたどり着きません。

一般的?には css で使うイメージは css フォルダに image フォルダを設けると思うので特別問題にもならないことかもしれませんが css 以外でも使ってるような画像があれば不都合が生じます。

しかしなぜWindows環境だけでしか起こらない現象か。
Asset のソースを眺めてたらソースを出力する際に、DIRECTORY_SEPARATORを使用しているのがわかりました。
DIRECTORY_SEPARATORはディレクトリの区切り文字でWindowsでは「¥」、Linuxでは「/」となります。
処理中でトリムして置換して・・・見たいなことをやってるのでそのあたりが問題なような気もしつつ・・・

とりあえずこれを解決する方法としては以下を修正します。

fuel/core/bootstrap.php
<?php

// define('DS', DIRECTORY_SEPARATOR);
define('DS', '/');

(以下省略)
?>

こうすることでとりあえずはOK。
しかし、この DS 自体がほかにどう使われているかわからないで修正する場合は注意が必要かなと思います。