2015年3月23日月曜日

AndroidのAnimationクラスを使ってみる


初めまして、イトナブ石巻の武山(日出)です。今回の滝沢合宿は各々が決めた1つのテーマに沿って2日間で開発、研究するというイベントでした。今までその時に必要なコードを調べて開発するだけだったので、1つのテーマを時間をかけてやることができて新しい発見ができてとても良かったです。

イトナブ開発合宿のことを知らない方はまずこちらをご覧になってください。
イトナブ石巻開発合宿in滝沢市を開催しました。


自分の今回の開発テーマはAnimationクラスでした。一度Animationクラスのコードに触っていて、応用次第では色々な表現が出来ると思いこの機会にもっと試してみたいと考えこのテーマにしました。



まずAnimationの4つの基本動作を並べてみました。




・フェイドイン・アウトのアニメーション

ImageView alphaImage = (ImageView)findViewById(R.id.alphaimage); AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0.2f); alphaAnimation.setDuration(3000); alphaImage.startAnimation(alphaAnimation);


・移動のアニメーション

ImageView transImage = (ImageView)findViewById(R.id.transimage); TranslateAnimation translateAnimation = new TranslateAnimation(0, 0, -50, 50); translateAnimation.setDuration(3000); transImage.startAnimation(translateAnimation)


・回転のアニメーション

 ImageView rotateImage = (ImageView)findViewById(R.id.rotateimage); RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotateAnimation.setDuration(3000); rotateImage.startAnimation(rotateAnimation);


 ・拡大・縮小のアニメーション

ImageView scaleImage = (ImageView)findViewById(R.id.scaleimage); ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0.5f, 1, 0.5f); scaleAnimation.setDuration(3000); scaleImage.startAnimation(scaleAnimation); 




また、これらのアニメーションを合成してみます

ImageView imageView = (ImageView)findViewById(R.id.setAnim); //インスタンスを生成
AnimationSet set = new AnimationSet(true);

 ・基本のアニメーションを生成

AlphaAnimation alphaAnimation = new AlphaAnimation(0.9f, 0.2f); RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); ScaleAnimation scaleAnimation = new ScaleAnimation(0.1f, 1, 0.1f, 1); TranslateAnimation translateAnimation = new TranslateAnimation(50, 0, 200, 0);

・生成したアニメーションを追加

set.addAnimation(alphaAnimation);
set.addAnimation(rotateAnimation);
set.addAnimation(scaleAnimation);
set.addAnimation(translateAnimation);

・アニメーション時間を設定して動作開始

set.setDuration(3000);
 imageView.startAnimation(set);




 アニメーションの合成は比較的簡単に出来たので、
それらを応用してロケットが飛び立つようなアニメーションを作ってみました。
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        imageView1 = (ImageView)findViewById(R.id.fire);
        imageView1.setVisibility(View.INVISIBLE);

        imageView = (ImageView)findViewById(R.id.rocket);
        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startAnim();


            }
        });

    private void startAnim() {

        TranslateAnimation translateAnimation1 = new TranslateAnimation(0, 10, 0, 5);
        translateAnimation1.setDuration(30);
        translateAnimation1.setRepeatMode(Animation.REVERSE);
        translateAnimation1.setRepeatCount(50);
        imageView.startAnimation(translateAnimation1);
        translateAnimation1.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                imageView1.setVisibility(View.VISIBLE);

                AnimationSet set = new AnimationSet(true);
                AnimationSet set1 = new AnimationSet(true);

                TranslateAnimation translateAnimation = new TranslateAnimation(0, 2, 0, -1500);
                translateAnimation.setDuration(2500);
                final RotateAnimation rotateAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, -3.5f);
                rotateAnimation.setDuration(300);
                rotateAnimation.setStartOffset(2500);

                TranslateAnimation translateAnimation2 = new TranslateAnimation(0, -70, 0, 770);
                translateAnimation2.setDuration(2500);
                translateAnimation2.setStartOffset(2800);
                translateAnimation2.setInterpolator(new BounceInterpolator());

                TranslateAnimation translateAnimation3 = new TranslateAnimation(0, 10, 0, 0);
                translateAnimation3.setDuration(25);
                translateAnimation3.setRepeatMode(Animation.REVERSE);
                translateAnimation3.setRepeatCount(100);

                RotateAnimation rotateAnimation1 = new RotateAnimation(0, 90, 20, 160);
                rotateAnimation1.setDuration(2000);
                rotateAnimation1.setStartOffset(5500);

                TranslateAnimation translateJet = new TranslateAnimation(0, 2, 0, -1500);
                translateJet.setDuration(2500);

                AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
                alphaAnimation.setDuration(2500);

                set.addAnimation(translateAnimation);
                set.addAnimation(rotateAnimation);
                set.addAnimation(translateAnimation2);
                set.addAnimation(translateAnimation3);
                set.addAnimation(rotateAnimation1);
                set.setFillAfter(true);
                set.setFillEnabled(true);
                imageView.startAnimation(set);

                set1.addAnimation(translateJet);
                set1.addAnimation(translateAnimation3);
                set1.addAnimation(alphaAnimation);
                set1.setFillAfter(true);
                set1.setFillEnabled(true);
                imageView1.startAnimation(set1);

                translateAnimation3.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {

                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {

                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {

                    }
                });
            }


            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
    }


画像では分からないと思いますが、ロケットが小刻みに震えながら炎を出して登って行きます。
そして画面から出た後にUターンして墜落します。
ただし、これだと端末依存で使用する機種によっては反転ポイントなどが激しくズレることがあるので、もし参考にされる場合は反転位置や墜落位置を端末に依存しないような工夫が必要だと思います。
これまでAndroidのアニメーションはあまり使っていなかったのですが、基本的な4つのアニメーションを組み合わせただけで様々な表現ができてなかなか楽しかったです。
また、今回のコードでちょこっと使ってますがAnimationのオプションが色々備わっているのでそれらを組み合わせるとかなり表現の幅が広がると思います。
アニメーションはアプリを作る上でページ移動時、ボタンをタップした時など、様々な場面で使用できると思います。
今回の開発合宿では自分が興味を持ったAnimationクラスを2日に渡って弄れたのでとても楽しく、また有意義なものでした。ここには載せきれていませんが、他にも色んな動きをするアニメーションを作れたので、今後の開発の参考にしていきたいと思います。
参考サイト:
Androidアプリで”アニメーション”するための基礎知識:
Animation | Android Developers:

Javaによる画像処理

この記事はイトナブ開発合宿にて作成されました。
イトナブ開発合宿を知らない方はまずこちらを御覧ください。
イトナブ開発合宿in滝沢市を開催しました。

自己紹介+合宿の感想
初めまして!
岩手県立大学ソフトウェア情報学部2年
溝渕俊樹です。
このたびは、イトナブ開発合宿に参加させていただき貴重な体験をさせていただきました。
各、一人一人が独自のテーマを決め、深く研究するというもので開発というものが初体験の私は緊張しながらの参加でしたが、みなさん温かく個性豊かなニックネームなどもあり、楽しむことができました。
そこで私が決めたテーマはImage processing(画像処理)です。
画像処理といっても幅が広いので今回は色を検証するということに重点を置き開発を行いました。
[開発環境]
Eclipse


[ソースコード]
package img;
import static img.Colors.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Processing {
    public static void main(String[] args) throws IOException {
        File f = new File("Apple.jpg"); //実行したい画像 
        BufferedImage read=ImageIO.read(f);
        int w = read.getWidth(),h=read.getHeight();
        BufferedImage write =
                new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        for(int y=0;y<h;y++){
            for(int x=0;x<w;x++){
                int c = read.getRGB(x, y); 
                int r = r(c);
                int g = g(c);
                int b = b(c);
                if((r > 200) && (g > 200) && (b > 200)){
                    r = 0;}
                if( r < 90){
                r = 0;
                }
                if( g > 110){
                r = 0;
                }
                if( b > 110){
                r =0;
                }
                g = 0;
                b = 0;   //チャンネルの値を検出したいものに変更
                int rgb = rgb(r,g,b);
                write.setRGB(x,y,rgb);
            }
        }
        File f2 = new File("red.jpg");
        ImageIO.write(write, "jpg", f2);
    }
}


package img;
public class Colors{
    public static int a(int c){
        return c>>>24; //Alpha
    }
    public static int r(int c){
        return c>>16&0xff; //Red
    }
    public static int g(int c){
        return c>>8&0xff;   //Green
    }
    public static int b(int c){
        return c&0xff;     //Blue
    }
    public static int rgb
    (int r,int g,int b){
        return 0xff000000 | r <<16 | g <<8 | b;
    }
    public static int argb
    (int a,int r,int g,int b){
        return a<<24 | r <<16 | g <<8 | b;
    }
}



[実行結果]
比較画像





このように赤い色素の部分だけをうまく検出することができました。
活用事例+可能性としては、画像処理の際にその画像にはどのような色素の割合が多く含まれているかなどがわかり、色弱などの目に障害を持つ人も助けになると考える。

[総括]
画像処理について研究を進める上で、色の重要さに気づくことができました。
今回は一つの検出方法しか試すことができなかったので、また機会があれば、他の方法で検証してみたいと思います。

参考URL
http://d.hatena.ne.jp/nodamushi/20111012/1318436587

Android Location APIを使ってみよう!

※この記事はイトナブ開発合宿in滝沢のイベントによって書かれました。
イトナブ開発合宿in滝沢を知らない方はまずこちらをご覧ください。
また他の参加者の方が書いた記事も掲載させて頂いております。

こんにちは、イトナブ石巻のフィッシュこと津田恭平です。
今回はAndroidのLocationAPIについて触れていきたいと思います。

Locations API
GooglePlayサービスが提供するLocation APIを使用すれば、簡単に位置情報の機能をアプリケーションに組み込むことができます。そしてGooglePlayサービスが提供するLocationAPIはなるべく端末に負担をかけずに省電力な実現を目指しています。


1.GooglePlayServiceのセットアップ
※Location APIの機能を使用するには、GooglePlayServiceのライブラリが必要となります。まずは下記のリンクを参考にインストールしてください。

2.チュートリアルをやってみる。

3.慣れてきたら詳細リファレンスを参照しカスタマイズを行う


GooglePlayサービスが提供する3つのLocationAPI
LocationAPIは大きく分けて3つのAPIを提供しています。

Fused location provider
緯度経度を取得することができます。また従来の緯度経度取得に比べ省電力になる他、端末は携帯基地局から情報をすればいいのか、Wi-fiなのか、GPSなのか、うまく切り替えをしてくれます。

使用例:
緯度経度と時間を常に取得すれば、(時間・距離・速さ)を取得出来るのでランニングアプリなどを作ることができます。

Geofencing APIs
あらかじめ任意の緯度経度と半径を指定し、目には見えませんが地図にサークル(円)を作成します。そして端末がその円を出たり入ったりするとGeofencing APIが起動しIntentを発行することができます。

使用例:
指定した場所(会社、自宅、お店)などに行くとそれに関連したアプリケーションを起動させることができます。

Activity recognition
ユーザーの行動(歩き、自転車、車など)を検知することができます。

使用例:さきほど紹介したFusedLocationAPIと組み合わせ、ランニングアプリを作成したとします。しかしユーザーが急に自転車などに乗った場合、ランニングなのか自転車なのか分からなくなります。Activity recognitionを使用すればユーザーの動きを検知できるので、A地点→B地点までランニング、B地点→C地点は自転車というような備忘録を作成することができます。

今回はFused location providerとActivity recognitionを実装してみる。

3つLocationAPIを紹介させて頂きました。
今回はこのAPIの3つうち、Fused location providerとActivity recognitionの2つを実装していきます。Geofencing APIsはイトナブ合宿の発表では不向きなので、今回は割愛させていただきます。
イトナブ開発合宿in滝沢を知らない方はまずこちらをご覧ください。

今回はこちらのソースコードを参考に作成していきます。

Fused location providerで使用し緯度経度を取得するサンプル

Manifestに下記の許可をいれてください。

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

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




MainActivity.java

import android.graphics.Typeface;
import android.location.Location;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import java.text.DateFormat;
import java.util.Date;

public class MainActivity extends ActionBarActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

    protected static final String TAG = "location-updates-sample";
    public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
    public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = UPDATE_INTERVAL_IN_MILLISECONDS / 2;
    protected final static String REQUESTING_LOCATION_UPDATES_KEY = "requesting-location-updates-key";
    protected final static String LOCATION_KEY = "location-key";
    protected final static String LAST_UPDATED_TIME_STRING_KEY = "last-updated-time-string-key";
    protected GoogleApiClient mGoogleApiClient;
    protected LocationRequest mLocationRequest;
    protected Location mCurrentLocation;
    protected Button mStartUpdatesButton;
    protected Button mStopUpdatesButton;
    protected TextView mLastUpdateTimeTextView;
    protected TextView mLatitudeTextView;
    protected TextView mLongitudeTextView;
    protected String mLastUpdateTime;
    protected Boolean mRequestingLocationUpdates;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mStartUpdatesButton = (Button)findViewById(R.id.start_button);
        mStartUpdatesButton.setTypeface(Typeface.DEFAULT_BOLD);
        mStopUpdatesButton = (Button)findViewById(R.id.stop_button);
        mStopUpdatesButton.setTypeface(Typeface.DEFAULT_BOLD);
        mLastUpdateTimeTextView = (TextView) findViewById(R.id.latitude);
        mLatitudeTextView = (TextView) findViewById(R.id.longitude);
        mLongitudeTextView = (TextView) findViewById(R.id.time);

        mRequestingLocationUpdates = false;
        mLastUpdateTime = "";

        updateValuesFromBundle(savedInstanceState);

        buildGoogleApiClient();
    }


    protected synchronized void buildGoogleApiClient() {
        Log.i(TAG, "Building GoogleApiClient");
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();

        createLocationRequest();
    }

    protected void createLocationRequest() {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    public void startUpdatesButtonHandler(View view) {
        if (!mRequestingLocationUpdates) {
            mRequestingLocationUpdates = true;
            setButtonsEnabledState();
            startLocationUpdates();
        }
    }

    public void stopUpdatesButtonHandler(View view) {
        if (mRequestingLocationUpdates) {
            mRequestingLocationUpdates = false;
            setButtonsEnabledState();
            stopLocationUpdates();
        }
    }

    private void setButtonsEnabledState() {
        if (mRequestingLocationUpdates) {
            mStartUpdatesButton.setEnabled(false);
            mStopUpdatesButton.setEnabled(true);
        } else {
            mStartUpdatesButton.setEnabled(true);
            mStopUpdatesButton.setEnabled(false);
        }
    }

    protected void startLocationUpdates() {
        LocationServices.FusedLocationApi.requestLocationUpdates(
                mGoogleApiClient, mLocationRequest, this);
    }

    @Override
    public void onConnected(Bundle bundle) {
        Log.i(TAG, "Connected to GoogleApiClient");


        if (mCurrentLocation == null) {
            mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
            mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
            updateUI();
        }


        if (mRequestingLocationUpdates) {
            startLocationUpdates();
        }

    }

    @Override
    public void onConnectionSuspended(int i) {
        Log.i(TAG, "Connection suspended");
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + connectionResult.getErrorCode());
    }

    @Override
    public void onLocationChanged(Location location) {
        mCurrentLocation = location;
        mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
        updateUI();
        Toast.makeText(this, "update",
                Toast.LENGTH_SHORT).show();
    }




    private void updateUI() {
        mLatitudeTextView.setText(String.valueOf(mCurrentLocation.getLatitude()));
        mLongitudeTextView.setText(String.valueOf(mCurrentLocation.getLongitude()));
        mLastUpdateTimeTextView.setText(mLastUpdateTime);
    }

    @Override
    protected void onPause() {
        super.onPause();
        stopLocationUpdates();
    }

    protected void stopLocationUpdates() {
        LocationServices.FusedLocationApi.removeLocationUpdates(
                mGoogleApiClient, this);
    }

    @Override
    public void onResume() {
        super.onResume();
        if (mGoogleApiClient.isConnected() && !mRequestingLocationUpdates) {
            startLocationUpdates();
        }
    }

    public void onSaveInstanceState(Bundle savedInstanceState) {
        savedInstanceState.putBoolean(REQUESTING_LOCATION_UPDATES_KEY,
                mRequestingLocationUpdates);
        savedInstanceState.putParcelable(LOCATION_KEY, mCurrentLocation);
        savedInstanceState.putString(LAST_UPDATED_TIME_STRING_KEY, mLastUpdateTime);
        super.onSaveInstanceState(savedInstanceState);
    }

    private void updateValuesFromBundle(Bundle savedInstanceState) {
        Log.i(TAG, "Updating values from bundle");
        if (savedInstanceState != null) {

            if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) {
                mRequestingLocationUpdates = savedInstanceState.getBoolean(
                        REQUESTING_LOCATION_UPDATES_KEY);
                setButtonsEnabledState();
            }


            if (savedInstanceState.keySet().contains(LOCATION_KEY)) {

                mCurrentLocation = savedInstanceState.getParcelable(LOCATION_KEY);
            }


            if (savedInstanceState.keySet().contains(LAST_UPDATED_TIME_STRING_KEY)) {
                mLastUpdateTime = savedInstanceState.getString(
                        LAST_UPDATED_TIME_STRING_KEY);
            }
            updateUI();
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        mGoogleApiClient.connect();
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }


}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:orientation="vertical"

    android:background="#FFC107"

    tools:context=".MainActivity">



    <TextView

        android:textSize="30dp"

        android:textColor="#ffffff"

        android:gravity="center"

        android:text="FusedLocationProvider"

        android:layout_weight="1"

        android:layout_width="match_parent"

        android:layout_height="0dp" />



    <TextView

        android:textSize="30dp"

        android:textColor="#ffffff"

        android:id="@+id/latitude"

        android:gravity="center"

        android:text="latitude"

        android:layout_weight="1"

        android:layout_width="match_parent"

        android:layout_height="0dp" />



    <TextView

        android:textSize="30dp"

        android:textColor="#ffffff"

        android:id="@+id/longitude"

        android:gravity="center"

        android:text="longitude"

        android:layout_weight="1"

        android:layout_width="match_parent"

        android:layout_height="0dp" />



    <TextView

        android:textSize="30dp"

        android:textColor="#ffffff"

        android:id="@+id/time"

        android:gravity="center"

        android:text="longitude"

        android:layout_weight="1"

        android:layout_width="match_parent"

        android:layout_height="0dp" />



    <LinearLayout

        android:orientation="horizontal"

        android:layout_weight="1"

        android:layout_width="match_parent"

        android:layout_height="0dp">



        <Button

            android:text="更新"

            android:onClick="startUpdatesButtonHandler"

            android:id="@+id/start_button"

            android:layout_marginTop="40dp"

            android:layout_marginRight="8dp"

            android:layout_marginLeft="8dp"

            android:gravity="center"

            android:layout_weight="1"

            android:layout_width="0dp"

            android:layout_height="match_parent" />



        <Button

            android:text="停止"

            android:onClick="stopUpdatesButtonHandler"

            android:id="@+id/stop_button"

            android:layout_marginTop="40dp"

            android:layout_marginRight="8dp"

            android:layout_marginLeft="8dp"

            android:gravity="center"

            android:layout_weight="1"

            android:layout_width="0dp"

            android:layout_height="match_parent" />



    </LinearLayout>





</LinearLayout>


Activity recognitionでユーザーの行動を取得
Activity recognitionのサンプルでは、ユーザーがどのような行動をしているのかを検知しパーセントを出します。そして最も近いものは、その画像を出すようにしています。Activity recognitionのサンプルはソースコードが少々多いので、src以下をgithubの方へコミットさせて頂きました。https://github.com/Kyoheitnav/ActivityRecognition
コミットさせて頂きました。

画像を切替えているソースのみこちらへ掲載させて頂きます。
Constants.java


 public static Bitmap getActivityImage(Context context, int detectedActivityType) {
        Resources r = context.getResources();

        switch (detectedActivityType) {
            case DetectedActivity.IN_VEHICLE:
                return BitmapFactory.decodeResource(r, R.drawable.car);
            case DetectedActivity.ON_BICYCLE:
                return BitmapFactory.decodeResource(r, R.drawable.bike);
            case DetectedActivity.ON_FOOT:
                return BitmapFactory.decodeResource(r, R.drawable.foot);
            case DetectedActivity.RUNNING:
                return BitmapFactory.decodeResource(r, R.drawable.running);
            case DetectedActivity.STILL:
                return BitmapFactory.decodeResource(r, R.drawable.still);
            case DetectedActivity.TILTING:
                return BitmapFactory.decodeResource(r, R.drawable.balance);
            case DetectedActivity.UNKNOWN:
                return BitmapFactory.decodeResource(r, R.drawable.question);
            case DetectedActivity.WALKING:
                return BitmapFactory.decodeResource(r, R.drawable.walk);
            default:
                return BitmapFactory.decodeResource(r, R.drawable.ic_launcher);
        }
    }





画像の方は任意の画像をWebなどへ検索し、drawable(mipmap)へ入れて下さい。 画像を検索する場合はライセンスなどに注意してください。


まとめ・イトナブ開発合宿の感想

今回2つのLocationAPIに触れました。GPSが多くのアプリケーションで採用されていることから、モバイルアプリでは、Androidに限らずGPSの機能が必要不可欠です。またスマートフォンのセンサーやGPSは日々進化しています。それに合わせLocationAPIなどの便利なAPIが、どんどん出てくると予想しています。それに合わせ毎回リファレンスを読むことになりますが、じっくり読まないで次へ次へと進んでしまいがちです。今回のイトナブ開発合宿ではゆっくりと腰を据えて、じっくりリファレンスを読みながら開発することが出来たのでとても満足しています。
イトナブ開発合宿が終わっても、リファレンスをはじめ新しい情報をキャッチした時に備え、日々リファレンスを読む練習を続けていきたいと感じました。

イトナブ開発合宿in滝沢を知らない方はまずこちらをご覧ください。


イトナブ開発合宿in滝沢市 全体のまとめ




イトナブの高菜こと遠藤匠です。

イトナブイトナブ開発合宿in滝沢市のまとめを紹介させていただきます。

イトナブ開発合宿のことを知らない方はまずこちらをご覧になってください。
イトナブ石巻開発合宿in滝沢市を開催しました。



2月19日と20日の2日間に渡り、岩手県滝沢市にてイトナブメンバーと地元の大
学、高校生と共に開発合宿を行いました。

開会式ではフィッシュが合宿の目的や日程等を発表。


 「1人1人がテーマを決め開発」「広く浅くではなく狭く深く」「ハッカソンではな
い」の3点のテーマが発表されました。





また、挨拶代わりに参加者の自己紹介と目標発表も行いました。






その後、デザイナー、ナナお手製のあみだくじで席を決定し、各々2日間、開発に取り組
みます。




2日目の午後は成果発表を行いました。
イトナブ
フィッシュ:Android , Location Fused location provider , Activity recognition
とみぎ:Android , マテリアルデザイン リップルエフェクト、フローティングアクション
日の出:アニメーション 、view を用いたアニメーション
紅白:Android , Camvas
ポーカー:Unityでのゲーム制作
学生参加者
ぶち:Android , 色を解析、検証する
じゃむ:Android , カメラアプリ
えんた:Android , ツールバー
じゃが:Android , センサーで値を取得し表示






発表後は今回身に付いた技術を基にどの様なアプリを作ることが出来るかをディスカッ
ションしました。



その後、イトナブ代表の古山さんから講評がありました。



参加者全員が開発の知識を深めることが出来た有意義な合宿でした!

イトナブ開発合宿in滝沢市を開催しました。


こんにちは、イトナブ石巻のフィッシュ(津田恭平)です。

岩手県の滝沢市で開発合宿、その名もイトナブ開発合宿を開催しました。

今回はイトナブ開発合宿のレポートを紹介していきます。

イトナブ開発合宿とは?
我々イトナブ石巻が主催する開発合宿です。イトナブ開発合宿は普段の活動拠点である石巻以外で開催し、プログラミングをする人であれば何方でも参加可能となっております。

具体的にどんな事をするの?
1人1つテーマを決めプログラミングを行います。開発のプラットフォームやプログラミング言語は自由です。普段触れているプログラミング言語でも、いつかやってみたいと思っていたプログラミングでも、選択は自由です。但し選択するテーマは他人と違ったものを選んでもらいます。そしてなるべく深く掘り下げて開発を行っていきます。
例:
A君「マテリアルデザインを使ったAndroidアプリを開発したい。」
B君「Unityをやってみたかったので、Unityのチュートリアルをしてみたい。」

ハッカソンとの違いは?
1つのテーマに対して深い知識を得ることを目的としています。ハッカソンなどのイベントだと、プロトタイプまでの完成を求められるので、どうしても浅く広い開発になってしまいます。また非エンジニアにも分かりやすく説明しないといけないため、どうしてもデザイン面(アプリの見た目)やプレゼンに力を入れるので、なかなか腰を据えて開発をすることができません。イトナブ開発合宿では気になる部分をとことん開発していきます。
※ここで定義する浅く広くは、例えばエラー処理のコードをきちんと書かなかったり、リファレンスをきちんと読まずに違う分野の開発にどんどん進んで開発をすることです。

目的は?
新しい技術やリファンレスの奥底に眠る便利な機能を発見を目的としています。そして合宿の最終日に技術を共有することで、個々人がどのような開発が出来るようになったのかを知ることができます。そして非エンジニアが新しい技術を見た時、新たなアイディアが出るきっかけとなったり、個々人の能力を知ることで仕事を依頼する可能性もでてきます。

イトナブ開発合宿in滝沢市では以下9名で開発を行いました。

一般社団法人イトナブ石巻のメンバー
津田 恭平(フィッシュ) 
谷口 泰大(とみぎ)   
佐々木 唯(ポーカー) 
白出 達彦(紅白)    
武山 将己(日出)  

ゲストのみなさん
遠藤 拓也        岩手県立大学
関村 涼         岩手県立大学
溝渕   俊樹        岩手県立大学
佐々木 知洋       盛岡工業高校

開発記録は膨大なため、個別投稿とさせていただきました。
予めご了承ください。

イトナブ開発合宿in滝沢市の全体まとめ
遠藤 匠(高菜)

Android Location APIを使ってみよう!
津田 恭平(フィッシュ)

Unityに触れてみよう。
佐々木 唯(ポーカー)

Canvasとmatrixの組み合わせの応用
白出 達彦(紅白)

AndroidのAnimationクラスを使ってみる
武山 将己(日出) (準備中です。)

AndroidのToolbarに触れてみよう。
遠藤 拓也 岩手県立大学

Javaによる画像処理
溝渕 俊樹 岩手県立大学












2015年3月19日木曜日

AndroidのToolbarに触れてみよう。

この記事はイトナブ開発合宿にて作成されました。
イトナブ開発合宿を知らない方はまずこちらを御覧ください。
イトナブ開発合宿in滝沢市を開催しました。


【自己紹介】
はじめまして。「えんた」こと、遠藤拓也といいます。
岩手県立大学の2年で、普段は小・中・高生にプログラミングを教えたり、
AndroidアプリやWebサービスの開発を勉強していたりします。

イトナブ開発合宿はとても和やかな感じで開発ができて、分からない所や参考になりそうな
ところを教えてくださったり、ハッカソンとは違い徹夜して切羽詰まり…ということなく、
自分のペースで開発を進めていくことが出来ました。


【開発テーマ】
Android 5.0から使えるようになった、Toolbarとそのまわりをいじってみようと思います!


【Toolbarとは?】
Android 5.0 (Lollipop)と同時に追加されたActionBarの代替となるものです。
ビュー階層内に記述するため、他のビューとの共存や、アニメーション、スクロールイベントへの反応などが簡単にでき、
Activity の Action Bar として設定することもできるので、通常のオプション メニューのアクションをその内部に表示させることも可能です。


【開発環境
・Mac OS X (10.10.2)
・Android Studio(1.0.1)
・Android(5.0.1)


【試してみたこと】

・Toolbar
・Tab(Actionbar.Tabが非推奨になったため)
Navigation Drawer


 


【準備】
build.gradle(Module: app)
忘れないように、最初に追加しておきましょう。

dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
     // Toolbarを使うために必要
     compile 'com.android.support:appcompat-v7:21.0.0'
     // PagerSlidingTabStripを使うのに必要
     compile 'com.jpardogo.materialtabstrip:library:1.0.6'
}


================================================================================
styles.xml(v21)
Toolbarの色やステータスバーの色などはここで指定します。
parentはTheme.AppCompat…を使う必要があるみたいです。

<? xml version = "1.0" encoding = "utf-8" ?>
< resources >
     < style name = "AppTheme" parent ="Theme.AppCompat.Light.NoActionBar" >
         <!-- Customize your theme here. -->
         <!-- Toolbarの背景色 -->
         < item name = "colorPrimary" >#FFBB33</ item >
         <!-- ステータスバーの背景色 -->
         < item name = "colorPrimaryDark" >#FFFF8800</ item >
         <!-- ActionModeを使うのに必要 -->
         < item name = "windowActionModeOverlay" >true</ item >
     </ style >
</ resources >



================================================================================
activity_main.xml
メインのレイアウト部分です。

<!-- Drawer start-->
< android.support.v4.widget.DrawerLayout xmlns:android = "http://schemas.android.com/apk/res/android"
     android:id = "@+id/drawer_layout"
     android:layout_width = "match_parent"
     android:layout_height = "match_parent" >
     < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
         xmlns:app = "http://schemas.android.com/apk/res-auto"
         xmlns:tools = "http://schemas.android.com/tools"
         android:layout_width = "match_parent"
         android:layout_height = "match_parent"
         android:padding = "0dp"
         android:paddingBottom = "@dimen/activity_vertical_margin"
         android:paddingLeft = "@dimen/activity_horizontal_margin"
         android:paddingRight = "@dimen/activity_horizontal_margin"
         android:paddingTop = "@dimen/activity_vertical_margin"
         tools:context = ".MainActivity" >
         <!-- Tab -->
         < com.astuetz.PagerSlidingTabStrip
             android:id = "@+id/tabs"
             android:layout_width = "match_parent"
             android:layout_height = "48dp"
             android:layout_alignParentStart = "true"
             android:layout_below = "@+id/tool_bar"
             android:background = "?attr/colorPrimary" />
         < android.support.v4.view.ViewPager xmlns:android = "http://schemas.android.com/apk/res/android"
             android:id = "@+id/pager"
             android:layout_width = "match_parent"
             android:layout_height = "match_parent"
             android:layout_alignParentEnd = "false"
             android:layout_alignParentStart = "false"
             android:layout_below = "@+id/tool_bar"
             android:focusable = "false"
             android:nestedScrollingEnabled = "false"
             android:outlineProvider = "paddedBounds" >
             < android.support.v4.view.PagerTabStrip
                 android:id = "@+id/strip"
                 android:layout_width = "match_parent"
                 android:layout_height = "wrap_content"
                 android:background = "#FFBB33"
                 android:focusable = "false"
                 android:paddingBottom = "12dp"
                 android:paddingEnd = "15dp"
                 android:paddingStart = "15dp"
                 android:paddingTop = "12dp"
                 android:textColor = "#000"
                 android:touchscreenBlocksFocus = "true" />
         </ android.support.v4.view.ViewPager >
         <!-- ToolBar start -->
         < android.support.v7.widget.Toolbar
             android:id = "@+id/tool_bar"
             android:layout_width = "match_parent"
             android:layout_height = "wrap_content"
             android:layout_alignParentLeft = "true"
             android:layout_alignParentStart = "true"
             android:layout_alignParentTop = "true"
             android:background = "?attr/colorPrimary"
             android:minHeight = "?attr/actionBarSize"
             app:theme = "@style/ThemeOverlay.AppCompat.ActionBar" >
         </ android.support.v7.widget.Toolbar >
         <!-- ToolBar end -->
     </ RelativeLayout >
     <!-- Drawer 表示部分 -->
     < LinearLayout
         android:layout_width = "280dp"
         android:layout_height = "match_parent"
         android:layout_gravity = "start"
         android:background = "#fff" >
         < ListView
             android:id = "@+id/listview"
             android:layout_width = "fill_parent"
             android:layout_height = "match_parent" />
     </ LinearLayout >
     <!-- Drawer 表示部分 end -->
</ android.support.v4.widget.DrawerLayout >
<!-- Drawer end-->


?attrはstyles.xmlで付けた名前を使用しますよって意味


================================================================================
menu_main.xml
Toolbar右に表示されているメニューボタンです

< menu xmlns:android = "http://schemas.android.com/apk/res/android"
     xmlns:tools = "http://schemas.android.com/tools"
     xmlns:app = "http://schemas.android.com/apk/res-auto"
     tools:context = ".MainActivity" >
     < item android:id = "@+id/mbtn"
         android:title = "menu-button"
         android:icon = "@drawable/ic_launcher2” //顔マーク
         android:orderInCategory = "100"
         app:showAsAction = "ifRoom" />
</ menu >


================================================================================
activity_fragment(0〜2).xml
Tabで管理される画面です。今回は同様の画面を3つ用意しましした。

< LinearLayout
     xmlns:android = "http://schemas.android.com/apk/res/android"
     xmlns:tools = "http://schemas.android.com/tools"
     android:layout_width = "match_parent"
     android:layout_height = "match_parent" >
     < TextView
         android:layout_width = "match_parent"
         android:layout_height = "wrap_content"
         android:text = "グー"
         android:textSize = "30sp"
         android:padding = "50dp"
         android:layout_gravity = "center_vertical"
         android:gravity = "center" />
</ LinearLayout >


================================================================================
MainActivity.java
メイン部分です。

public class MainActivity extends ActionBarActivity {
     private DrawerLayout vDrawerLayout;
     private ActionBarDrawerToggle vDrawerToggle;
     private ListView vListView;
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         settoolbar();
         PagerSlidingTabStrip tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
         ViewPager mViewPager = (ViewPager) findViewById(R.id.pager);
         mViewPager.setAdapter( newMyFragmentStatePagerAdapter(getSupportFragmentManager()));
         tabs.setViewPager(mViewPager);
         PagerTabStrip strip = (PagerTabStrip)findViewById(R.id.strip);
         strip.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20 );
         vDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
         initDrawer();
         vListView = (ListView) findViewById(R.id.listview);
         vListView.setAdapter( new ArrayAdapter<String>(
                 this , android.R.layout.simple_list_item_1, new String[]{ "menu 1" ,"menu 2" , "menu 3" , "menu 4" , "menu 5" }));
     }
     protected void settoolbar(){
         //  toolbar定義
         Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar);
         //  タイトル
         toolbar.setTitle( "ActionBar Title" );
         //  タイトルの色
         toolbar.setTitleTextColor(Color.parseColor( "#000000" ));
        //  ActionBarと同等にToolbarを使う  
      setSupportActionBar(toolbar);
     }
     private void initDrawer() {
         vDrawerToggle = new ActionBarDrawerToggle( this , vDrawerLayout, R.string.app_name, R.string.app_name);
         vDrawerToggle.setDrawerIndicatorEnabled( true );
         vDrawerLayout.setDrawerListener(vDrawerToggle);
         getSupportActionBar().setDisplayHomeAsUpEnabled( true );
         getSupportActionBar().setDisplayShowHomeEnabled( true );
     }
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         getMenuInflater().inflate(R.menu.menu_main, menu);
         return true ;
     }
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         int id = item.getItemId();
         if (id == R.id.mbtn) {
             Toast.makeText(MainActivity. this , "Menu click" , Toast.LENGTH_SHORT).show();
             return true ;
         }
         return vDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
     }
     @Override
     protected void onPostCreate(Bundle savedInstanceState) {
         super .onPostCreate(savedInstanceState);
         vDrawerToggle.syncState();
     }
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super .onConfigurationChanged(newConfig);
         vDrawerToggle.onConfigurationChanged(newConfig);
     }
}


================================================================================
MyFragmentStatePagerAdapter.java
FragmentStatePagerAdapterでFragmentを管理します。

public class MyFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
     public MyFragmentStatePagerAdapter(FragmentManager fm) {
         super (fm);
     }
     @Override
     public Fragment getItem( int i) {
         switch (i){
             case 0 :
                 return new Fragment0();
             case 1 :
                 return new Fragment1();
             default :
                 return new Fragment2();
         }
     }
     @Override
     public int getCount() {
         return 3 ;
     }
     @Override
     public CharSequence getPageTitle( int position) {
         switch (position){
             case 0 :
                 return "First" ;
             case 1 :
                 return "Second" ;
             default :
                 return "Third" ;
         }
     }
}


================================================================================
Fragment(0〜2).java
Tabでスワイプして表示させる画面です。

public class Fragment0 extends Fragment {
     @Override
     public View onCreateView(LayoutInflater inflater,
                              ViewGroup container,
                              Bundle savedInstanceState) {
         return inflater.inflate(R.layout.activity_fragment0, null );
     }

    @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         // Handle action bar item clicks here. The action bar will
         // automatically handle clicks on the Home/Up button, so long
         // as you specify a parent activity in AndroidManifest.xml.
         int id = item.getItemId();
         //noinspection SimplifiableIfStatement
         if (id == R.id.action_settings) {
             return true ;
         }
         return super .onOptionsItemSelected(item);
     }
}

長くなりましたが、これで
Toolbar、Tab、Navigation Drawerが実装出来たと思います。

【可能性】
ちょっと調べただけでもたくさんのサンプルが出てきました。
xmlでレイアウトを組むことが好きな私にとってはとても
使いやすいという印象がありました。
Actionbarの代わりとして扱え、さらにサイズや位置も自由に変えられるため、
Actionbar以外の使い道もアイディア次第になりそうです。
ただ、setSupportActionBarするとsetNavigationOnClickListenerやsetOnMenuItemClickListenerは
使えなくなるので注意。


【最後に】
普段は調べる→コピペ→動けばOK、という感じでしたが、
今回は『狭く、深く』ということだったので公式リファレンスを見たり、
実際にいろいろ試してみたり、英語とも格闘しました。すると、
点と点が繋がったようなことがあったり、今後の開発に活かせそうなことも
発見出来たりと、とても充実した時間を過ごすことができました。
今後も新しいことにどんどんチャレンジしていきたいと思います。


【Github】
コピペの量が多すぎ!って人はこちらをどうぞ


【参考】
Google Japan Developer Relations Blog

AndroidのToolBar(新しいActionBar)メモ