2015年4月28日火曜日

ViewPagerの中のEditTextでカーソルを移動したときにページが変わってしまうことの対策方法

はじめまして、イトナブ石巻に今月入社しましたデルシオです。

今日、ViewPagerを使っていて、ページの中にEditTextを入れたのですが、困ったことにEditTextのカーソルが端にあるときに、カーソルを動かそうとするとページが変わってしまうという現象が起こりました。調べても出てこなかったので、ここにまとめたいと思います。

まず、EditTextのOnKeyListenerを下のように定義します。
public class OnKeyDownDisablePageChange implements EditText.OnKeyListener {
 @Override
 public boolean onKey(View v, int keyCode, KeyEvent event) {
  switch (keyCode) {
   case (KeyEvent.KEYCODE_DPAD_LEFT):
    // ←ボタンが押されたら
    if (mEditMemo.getSelectionEnd() == 0) {
     // 選択範囲の終わり(カーソルも含む)が左端だったら
     return true;
     // その他のonKeyの動作を無効にする(ページが変わらない)
    }
    break;
   case (KeyEvent.KEYCODE_DPAD_RIGHT):
    // →ボタンが押されたら
    int length = ((EditText) v).getText().length();
    if (mEditMemo.getSelectionStart() == length) {
     // 選択範囲の始まり(カーソルも含む)が右端だったら
     return true;
     // その他のonKeyの動作を無効にする(ページが変わらない)
    }
    break;
  }
  return false;
  // なにもない場合はカーソルを移動する
 }
}
こうすることで、カーソルが端から出ようとするとき(ViewPagerにページを変える通知を送るとき)の動作を無効にしています。
そしてViewPagerのページのFragmentの中で
OnKeyDownDisablePageChange mOnKeyDownDisablePageChange = new OnKeyDownDisablePageChange();
のように定義して
mEditText.setOnKeyListener(mOnKeyDownDisablePageChange);
などとEditTextのsetOnKeyListenerを使って適用させれば良いと思います。

2015年4月21日火曜日

RecyclerViewを使ってみる

こんにちは、イトナブ石巻のとみぎです。

今回は、MaterialDesignで追加されたウィジェットの1つ、RecyclerViewを使ったサンプルを作成してみました。

RecyclerViewは、従来のListViewやGridViewのような表示を、コードで自在に変更させる事が可能なようです。
今回はボタンでそれぞれのレイアウトを切り替えるようなサンプルを作ってみました。

まずはサンプルアプリのイメージから。


ListViewのような表示


GridViewのような表示


このように、同じRecyclerViewなのに、ボタン(メソッド)1つで表示を切り替える事ができます。
では、コードを紹介します。


【build.gradle】
apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "jp.itnav.tomigie.recyclerviewsample"
        minSdkVersion 14
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.0.0'
    compile 'com.android.support:recyclerview-v7:22.0.0'
    compile 'com.android.support:cardview-v7:22.0.0'
}


【BaseActivity】
package jp.itnav.tomigie.recyclerviewsample;

import android.app.Activity;
import android.content.Intent;
import android.support.v7.app.ActionBarActivity;
import android.view.View;

import java.util.ArrayList;

public abstract class BaseActivity extends ActionBarActivity implements View.OnClickListener {

    protected ArrayList<String> getDataList(int dataSize) {
        ArrayList<String> dataList = new ArrayList<>();
        for (int i = 1; i <= dataSize; i++) {
            dataList.add("card" + i);
        }
        return dataList;
    }

    protected void startNewActivity(Class<Activity> activity) {
        Intent intent = new Intent(this, activity);
        startActivity(intent);
    }
}


【RecyclerViewAdapter】
package jp.itnav.tomigie.recyclerviewsample;

import android.content.Context;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
    private LayoutInflater      mLayoutInflater;
    private ArrayList<String>   mDataList;
    private Context             mContext;


    public RecyclerViewAdapter(Context context, ArrayList<String> dataList) {
        super();
        mContext = context;
        mLayoutInflater = LayoutInflater.from(context);
        mDataList = dataList;
    }

    @Override
    public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = mLayoutInflater.inflate(R.layout.card, parent, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(final RecyclerViewAdapter.ViewHolder holder, int position) {
        final String data = mDataList.get(position);
        holder.text.setText(data);
        holder.cardView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // カードをクリックした時の処理
                Toast.makeText(mContext, data + " clicked", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return mDataList.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView        text;
        CardView        cardView;
        RecyclerView    recyclerView;

        public ViewHolder(View v) {
            super(v);
            text = (TextView) v.findViewById(R.id.cardArticleTitle);
            cardView = (CardView) v.findViewById(R.id.cardView);
            recyclerView = (RecyclerView) v.findViewById(R.id.recyclerView);
        }
    }
}


【MainActivity】
package jp.itnav.tomigie.recyclerviewsample;

import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;
import android.widget.Button;


public class MainActivity extends BaseActivity {
    RecyclerView mRecyclerView;

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

    @Override
    public void onClick(View v) {
        int id = v.getId();
        if (id == R.id.buttonLinear) {
            setLinearRecyclerView();
        }
        else if (id == R.id.buttonGrid) {
            setGridRecyclerView();
        }
        else if (id == R.id.buttonStaggered) {
            setStaggeredGridRecyclerView();
        }
    }

    public void setLayouts() {
        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        setLinearRecyclerView();
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setAdapter(new RecyclerViewAdapter(this, getDataList(20)));

        Button linear = (Button) findViewById(R.id.buttonLinear);
        Button grid = (Button) findViewById(R.id.buttonGrid);
        Button staggered = (Button) findViewById(R.id.buttonStaggered);
        linear.setOnClickListener(this);
        grid.setOnClickListener(this);
        staggered.setOnClickListener(this);
    }

    public void setLinearRecyclerView() {
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
    }

    public void setGridRecyclerView() {
        mRecyclerView.setLayoutManager(new GridLayoutManager(this, 2));
    }

    public void setStaggeredGridRecyclerView() {
        mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL));
    }
}


【activity_main.xml】
<RelativeLayout 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"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical"
        android:layout_above="@+id/linearLayout" />

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:id="@+id/linearLayout">

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="Linear"
            android:id="@+id/buttonLinear"
            android:layout_weight="1" />

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="Grid"
            android:id="@+id/buttonGrid"
            android:layout_weight="1" />

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="Staggered"
            android:id="@+id/buttonStaggered"
            android:layout_weight="1" />
    </LinearLayout>

</RelativeLayout>


【card.xml】
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:weightSum="1"
    android:id="@+id/cardLayout"
    android:elevation="10dp">

    <android.support.v7.widget.CardView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardCornerRadius="4dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp"
        android:foreground="?android:attr/selectableItemBackground"
        android:id="@+id/cardView"
        android:layout_marginBottom="10dp">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center_horizontal"
            android:id="@+id/cardRelative"
            android:orientation="vertical">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/cardArticleImage"
                android:src="@mipmap/ic_launcher"
                android:adjustViewBounds="true"
                android:scaleType="center" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:text="CardView"
                android:id="@+id/cardArticleTitle"
                android:textSize="13sp"
                android:gravity="center_vertical"
                android:textColor="#ffffffff"
                android:background="#96000000"
                android:padding="10dp"
                android:layout_gravity="bottom" />

        </FrameLayout>
    </android.support.v7.widget.CardView>
</RelativeLayout>


以上です。

レイアウトの変更はRecyclerViewのsetLayoutManageで、変更したい引数を渡すだけなので、思ったよりも簡単に変更ができました。
この他にも、雑誌のようなちぐはぐしたGrid表示(StaggeredGridっていうらしい)など、まだまだカスタムができるようなので、またカスタムしたサンプルができたら紹介しようかなと思います。

今回のサンプルはGitHubにもアップロードしましたので以下にリンクを添付しておきます。
GitHub - RecyclerViewSample


ではでは。
posted by とみぎ

2015年4月12日日曜日

CardViewを使用した簡単なサンプル

こんにちは!
イトナブ石巻のとみぎです。

今回は、CardViewを使った簡単なサンプルを作ってみました。

サンプルはこんな感じです↓




こちらのサンプルコードの詳細は以下をご覧ください。

【MainActivity.java】
public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LinearLayout cardLinear = (LinearLayout)this.findViewById(R.id.cardLinear);
        cardLinear.removeAllViews();

        int cardSize = 15; // カードの枚数

        for(int i = 0; i < cardSize; i++) {
            LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
            LinearLayout linearLayout = (LinearLayout) inflater.inflate(R.layout.card, null);
            CardView cardView = (CardView) linearLayout.findViewById(R.id.cardView);
            TextView textBox = (TextView) linearLayout.findViewById(R.id.textBox);
            textBox.setText("CardView " + i);
            cardView.setTag(i);
            cardView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this, String.valueOf(v.getTag()) + "番目のCardViewがクリックされました", Toast.LENGTH_SHORT).show();
                }
            });
            cardLinear.addView(linearLayout,i);
        }
    }
}

【Activity_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="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/scrollView"
        android:fillViewport="false">
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/cardLinear">
        </LinearLayout>
    </ScrollView>

</LinearLayout>

【card.xml】
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:weightSum="1"
    android:id="@+id/cardLayout">

    <android.support.v7.widget.CardView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardCornerRadius="4dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp"
        android:foreground="?android:attr/selectableItemBackground"
        android:id="@+id/cardView"
        android:layout_marginBottom="10dp">

        <!-- カードに載せる情報 -->

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center_horizontal"
            android:id="@+id/cardRelative"
            >

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="170dp"
                android:id="@+id/imageView"
                android:src="@mipmap/ic_launcher"
                android:adjustViewBounds="true"
                android:scaleType="fitCenter" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:text="CardView"
                android:id="@+id/textBox"
                android:textSize="30sp"
                android:layout_marginLeft="17dp"
                android:layout_marginStart="17dp"
                android:gravity="center_vertical" />

        </LinearLayout>
    </android.support.v7.widget.CardView>
</LinearLayout>


今回のサンプルは1つのクラスと2つのxmlファイルのシンプルな構成となっています。
アプリの動作は、カードがタップされたらそのカードに対応したToast文が表示されるのみです。

ここからOnClickの動作を次のActivityに遷移させたり、cardView.xmlのレイアウトをよりカスタムして見た目を変えていく等々、アレンジができるのではと思います。


プロジェクトはGitHub(以下リンク)で公開してますので、こちらもご参考頂ければ幸いです。
GitHub - CardViewSample
posted by とみぎ

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 , センサーで値を取得し表示






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



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



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