2013年12月7日土曜日

Android開発環境のインストール(adt-bundle-windows, a java runtime environment (jre) or Java Development kit (JDK), fail to load the jni shared library)

PCを乗り換えたので、再度Android開発環境を構築してみたので、インストール方法等を紹介します。

今まで、プログラミング経験がない人のためにも、あんまり専門用語は使わずに書いていきます。

とりあえず


https://developer.android.com/sdk/index.html

画面に飛びます。

download sdk クリック


ギガ超えしているので、落とすのに結構時間がかかるかもしれません。。
落とし終えたら、解凍してくだいさい。
zipを右クリック展開でOKです。

そして、解凍したフォルダに行き、『eclipse』→『eclipse.exe』を叩いてください。

Android Developer Toolという画面が立ち上がったら、成功です。

これでひとまず終了



こんな感じのダイアログがでた人は次の作業

JREとJDKを落とします。

JRE
http://java.com/ja/download/

ダウンロード
インストール

JDK
http://www.oracle.com/jp/downloads/index.html
ダウンロード
インストール

そして、環境pathの設定

コントロール パネル\システムとセキュリティ\システム→システムの詳細設定→詳細設定→環境変数→システム環境変数のPathを選択編集,現在書かれていpathの最後に
;を付け加えてjdkのbinまでを記述
C:\Program Files\Java\jdk1.6.0_29\bin
こんな感じ

でもう一回
『eclipse』→『eclipse.exe』を叩いてください。

上手く言った人は終了!!


こんなのが出現した人は失敗
JDKの×64とか×32のインストールを間違えているので、正しい数字でインストールしてください。

以上です。





2013年12月2日月曜日

タブの表示(ActionBar.TabListener, FragmentActivity, TabActivity)

TabActivityが非推奨になったことを最近しりました。。。

結構便利で使っていたのですが。。。

しかし、もっと便利でかっこいいタブを見つけたので紹介します。

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

アクションバーのNAVIGATION_MODE_TABSです。
iPhoneアプリで言うところのUINavigationControllerみたいなものです。

PagerAdapterを使うことによって、スワイプ操作で画面の切り替えができるので
スマホっぽさを演出できます。

使い方は簡単

Androidアプリの新規プロジェクトを作成して、
最低APIのバージョンを11以上にしてください。



この画面でナビゲーションタイプを 『Tabs + Swipe』にすれば完了!!

てか、今まで自分こんな便利なものがあると知らなかった!!
他のもいろいろ試したくなります。

で、作成されたソースを実行すると、
それっぽい感じの画面ができてます。

ということで、

ActionBar.TabListener, TabActivityの使い方は多分だいたいOKだと思いますが、
一応このブログではできるだけ、xmlファイルを使用しないでソースで作成するということなので、
少しだけ、手を加えたサンプルソースを載せます。
まぁ、ほとんどサンプルどおりですが。。。

■サンプルソース

package com.example.tabtest;

import android.app.ActionBar;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class MainActivity extends FragmentActivity implements ActionBar.TabListener {

    /**
    * The {@link android.support.v4.view.PagerAdapter} that will provide
    * fragments for each of the sections. We use a
    * {@link android.support.v4.app.FragmentPagerAdapter} derivative, which
    * will keep every loaded fragment in memory. If this becomes too memory
    * intensive, it may be best to switch to a
    * {@link android.support.v4.app.FragmentStatePagerAdapter}.
    */
    SectionsPagerAdapter mSectionsPagerAdapter;

    /**
    * The {@link ViewPager} that will host the section contents.
    */
    ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        // Set up the action bar.
        final ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        // Create the adapter that will return a fragment for each of the three
        // primary sections of the app.
        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
 
        // Set up the ViewPager with the sections adapter.
        mViewPager = new ViewPager(getApplicationContext());
        mViewPager.setId(2);
        mViewPager.setAdapter(mSectionsPagerAdapter);

        setContentView(mViewPager);
 
        // When swiping between different sections, select the corresponding
        // tab. We can also use ActionBar.Tab#select() to do this if we have
        // a reference to the Tab.
        mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                actionBar.setSelectedNavigationItem(position);
            }
        });
 
        // For each of the sections in the app, add a tab to the action bar.
        for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
            // Create a tab with text corresponding to the page title defined by
            // the adapter. Also specify this Activity object, which implements
            // the TabListener interface, as the callback (listener) for when
            // this tab is selected.
            //アクションバーにタブを設定する
            actionBar.addTab(actionBar.newTab()
            //タブタイトルの設定
            .setText(mSectionsPagerAdapter.getPageTitle(i))
            //リスナーの設定(スワイプ操作を受け付ける)
            .setTabListener(this)
            //アイコンの設定
            setIcon(R.drawable.ic_launcher));
        }
    }


    @Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
        // When the given tab is selected, switch to the corresponding page in
        // the ViewPager.
        mViewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab,
        FragmentTransaction fragmentTransaction) {
    }

    @Override
    public void onTabReselected(ActionBar.Tab tab,
        FragmentTransaction fragmentTransaction) {
    }

    /**
    * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
    * one of the sections/tabs/pages.
    */
    public class SectionsPagerAdapter extends FragmentPagerAdapter {
        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            Fragment fragment = new DummySectionFragment();
            Bundle args = new Bundle();
            args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
            fragment.setArguments(args);
            return fragment;
        }

        @Override
        public int getCount() {
            // Show 3 total pages.
            return 3;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            switch (position) {
            case 0:
                return "1";
            case 1:
                return "2";
            case 2:
                return "3";
            }
            return null;
         }
    }

    /**
    * A dummy fragment representing a section of the app, but that simply
    * displays dummy text.
    */
    public static class DummySectionFragment extends Fragment {

        /**
         * The fragment argument representing the section number for this
         * fragment.
         */
        public static final String ARG_SECTION_NUMBER = "section_number";

        public DummySectionFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
             // Create a new TextView and set its text to the fragment's section
             // number argument value.
            TextView textView = new TextView(getActivity());
            textView.setGravity(Gravity.CENTER);
            textView.setText(Integer.toString( getArguments().getInt(ARG_SECTION_NUMBER)));
            return textView;
        }
    }
}

■実行結果


こんな感じになります。

mViewPager = new ViewPager(getApplicationContext());
mViewPager.setId(2);

このViewPagerを作成するところが肝でした。
なぜかって??
setIdをしないと実行エラーになるからです。

あまり普段、id指定をしていなかったので、
結構ハマりました。。。。。。

2013年11月28日木曜日

アプリ内課金実装(IInAppBillingService.aidl, IabException)

IInAppBillingServiceのライブラリを実装しました。

前回までのIMarketBillingService.aidlを使っていたのですが、
version3にアップデートしろと言われ仕方なくアップデート。。


アップデートした感想を先にいうと、
全体的に便利になっています。

前回は自分で準備した処理も
既に入っていたりと。。

IInAppBillingServiceを使っている人は今すぐにでも
アップデートしていいレベルだど思います。

また、新機能として、consumeという機能が付きました。
購入した商品を消費するという処理です。
この消費が正常に行われないと、ユーザーは商品を購入できません。
購入した商品はちゃんと、consumeの処理を走らせて、商品を消費させてください。

ではでは、課金機能を新規追加の人のためにも実装方法を説明します。
他のサイトでも書かれていると思いますが、サンプルソースを見ながら、自分のアプリに必要な
ソースをコピって、貼り付けてね。
とそんな感じになるんですが。。。。

■サンプルの取得
Android SDKマネージャーを立ち上げ。
play_billingをインストール

eclipseでplay_billingをインポート

■サンプルソースを見る前に覚えておくといい事
◇商品タイプ
Googleのアプリ内課金で購入できるアイテムには3つのタイプがあります。
(アイテム登録時に分ける必要あり)

・管理対象の商品
・管理対象外の商品
・定期購入

定期購入は何となくわかると思いますが、管理対象の商品と管理対象外の商品違いとは何か??
管理対象のアイテムは1度しか購入できない商品で、
管理対象外のアイテムは何度も購入できる商品の事です。

RPGで言うと、回復アイテムは管理対象外のアイテムで
鍵は管理対象のアイテムです。

何となくわかりますかね??

今回のgoogleのサンプルソースで言うと、
gasが管理対象外の商品
premiumが管理対象の商品
infinite_gasが定期購読
になります。

◇商品購入テストの仕方
パッケージ名がcom.exampleから、始まると購入テストはできません。
googleplayに実際にアプリを上げないとテストはできません。(アプリは非公開で)
また、googleplayにあげるので、本番で使う証明書でBuildするのがお勧めです。
developer consoleのアカウントの詳細でテスト用のアクセス権を設定すれば、テスト購入ができます。

ただ、そのテスト用のアクセス権を設定する際にテスト用に使いたい端末の初期設定時の
Gmailアドレスを設定しないといけないので注意して下さい。下手すると、端末を初期化しないといけなくなります。


■サンプルソースをよく読む
MainActivityがメイン処理です。
課金機能の流れとしては、

消費していないアイテムがあるかの確認

アイテムの購入

アイテムの消費

という風になっております。


■サンプルを見ても自分のアプリに必要なソースがわからない人のために
ちょっと、コメントアウトを噛みくだいて、MainActivitywを記述して見ました。

◇サンプルのサンプル
public class MainActivity extends Activity {
    //デバック用のログタイトル
    static final String TAG = "TrivialDrive";

    //管理されているアイテムを購入しているかのフラグ
    boolean mIsPremium = false;

    //定期購読アイテムを購入しているかのフラグ
    boolean mSubscribedToInfiniteGas = false;

    //developer consoleページ内のアプリ内アイテムで登録されているアイテムID
    //premiumは管理対象のアイテム
    //gasは管理対象外のアイテム
    //infinite_gasは定期購読商品のアイテム
    static final String SKU_PREMIUM      = "premium";
    static final String SKU_GAS          = "gas";
    static final String SKU_INFINITE_GAS = "infinite_gas";

    //onActivityResultで返ってくるときのrequest_code
    static final int RC_REQUEST = 10001;

    //画像配列
    static int[] TANK_RES_IDS = { R.drawable.gas0, R.drawable.gas1, R.drawable.gas2,
                                   R.drawable.gas3, R.drawable.gas4 };

    //管理対象外アイテムの購入可能回数
    static final int TANK_MAX = 4;

    //管理対象外アイテムの購入回数
    int mTank;

    //IabHelperオブジェクト
    IabHelper mHelper;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //画面を設定
        setContentView(R.layout.activity_main);

        //ゲーム内容のロード
        loadData();

        //このアプリのライセンス キー 。バイナリに追加する Base64 エンコードの RSA 公開鍵
        String base64EncodedPublicKey = "CONSTRUCT_YOUR_KEY_AND_PLACE_IT_HERE";

        //base64EncodedPublicKeyの変数をアプリライセンスキーに変更したかの判定
        if (base64EncodedPublicKey.contains("CONSTRUCT_YOUR")) {
            throw new RuntimeException("Please put your app's public key in MainActivity.java. See README.");
        }
     
        //パッケージ名がcom.exampleから、始まっていないかの判定(com.exampleのままだと購入テストはできません)
        if (getPackageName().startsWith("com.example")) {
            throw new RuntimeException("Please change the sample's package name! See README.");
        }

        //IabHelperオブジェクトの生成
        Log.d(TAG, "Creating IAB helper.");
        mHelper = new IabHelper(this, base64EncodedPublicKey);

        //ログ出力フラグをONにする
        mHelper.enableDebugLogging(true);

        //IabHelperのインスタンスの呼び出し
        //IabHelperが使用可能か非同期で通信して、通信完了後に呼ばれる
        Log.d(TAG, "Starting setup.");
        mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
            public void onIabSetupFinished(IabResult result) {
                Log.d(TAG, "Setup finished.");

                //IabHelperが使用不能なときに通る
                if (!result.isSuccess()) {
                    complain("Problem setting up in-app billing: " + result);
                    return;
                }

                // Have we been disposed of in the meantime? If so, quit.
                if (mHelper == null) return;

                //アイテム購入復元処理を実行(消費していないアイテムがあるかの判定処理が走る)
                Log.d(TAG, "Setup successful. Querying inventory.");
                mHelper.queryInventoryAsync(mGotInventoryListener);
            }
        });
    }

    //消費してしないアイテムを判定完了時に呼ばれる
    IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
        public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
            Log.d(TAG, "Query inventory finished.");

            // Have we been disposed of in the meantime? If so, quit.
            if (mHelper == null) return;

            //購入が成功しているかの判定。購入画面から、購入しないで戻ってもここを通る
            if (result.isFailure()) {
                complain("Failed to query inventory: " + result);
                return;
            }

            Log.d(TAG, "Query inventory was successful.");

            /*
             * Check for items we own. Notice that for each purchase, we check
             * the developer payload to see if it's correct! See
             * verifyDeveloperPayload().
             */

            //管理対象のアイテム購入履歴があるかの判定
            Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);
            mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
            Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM"));

            //定期購読アイテム購入履歴があるかの判定
            Purchase infiniteGasPurchase = inventory.getPurchase(SKU_INFINITE_GAS);
            mSubscribedToInfiniteGas = (infiniteGasPurchase != null &&
                    verifyDeveloperPayload(infiniteGasPurchase));
            Log.d(TAG, "User " + (mSubscribedToInfiniteGas ? "HAS" : "DOES NOT HAVE")
                        + " infinite gas subscription.");
            if (mSubscribedToInfiniteGas) mTank = TANK_MAX;

            //消費されていない管理対象外のアイテムがあるかの判定
            Purchase gasPurchase = inventory.getPurchase(SKU_GAS);
            if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {
                Log.d(TAG, "We have gas. Consuming it.");
                mHelper.consumeAsync(inventory.getPurchase(SKU_GAS), mConsumeFinishedListener);
                return;
            }

            updateUi();
            setWaitScreen(false);
            Log.d(TAG, "Initial inventory query finished; enabling main UI.");
        }
    };

    //管理対象外アイテム購入ボタン押下時
    public void onBuyGasButtonClicked(View arg0) {
        Log.d(TAG, "Buy gas button clicked.");

        //定期購読アイテム購入していたら、処理を抜ける
        if (mSubscribedToInfiniteGas) {
            complain("No need! You're subscribed to infinite gas. Isn't that awesome?");
            return;
        }
     
        //管理対象外アイテムの購入限度を超えていたら、処理を抜ける
        if (mTank >= TANK_MAX) {
            complain("Your tank is full. Drive around a bit!");
            return;
        }

        //Viewを購入画面に変更(これは地味に重要!!仕込むアプリにも是非導入しよう)
        setWaitScreen(true);
        Log.d(TAG, "Launching purchase flow for gas.");

        /* TODO: for security, generate your payload here for verification. See the comments on
         *        verifyDeveloperPayload() for more info. Since this is a SAMPLE, we just use
         *        an empty string, but on a production app you should carefully generate this. */
        String payload = "";

        //アイテム購入フローが走る
        mHelper.launchPurchaseFlow(this, SKU_GAS, RC_REQUEST,
                mPurchaseFinishedListener, payload);
    }

    //管理対象アイテム購入ボタン押下時
    public void onUpgradeAppButtonClicked(View arg0) {
        Log.d(TAG, "Upgrade button clicked; launching purchase flow for upgrade.");
        setWaitScreen(true);

        /* TODO: for security, generate your payload here for verification. See the comments on
         *        verifyDeveloperPayload() for more info. Since this is a SAMPLE, we just use
         *        an empty string, but on a production app you should carefully generate this. */
        String payload = "";

        mHelper.launchPurchaseFlow(this, SKU_PREMIUM, RC_REQUEST,
                mPurchaseFinishedListener, payload);
    }

    //定期購読商品を購入する時に流れる
    public void onInfiniteGasButtonClicked(View arg0) {
        if (!mHelper.subscriptionsSupported()) {
            complain("Subscriptions not supported on your device yet. Sorry!");
            return;
        }

        String payload = "";

        setWaitScreen(true);
        Log.d(TAG, "Launching purchase flow for infinite gas subscription.");
        //定期購読用の引数を渡すので注意
        mHelper.launchPurchaseFlow(this, SKU_INFINITE_GAS, IabHelper.ITEM_TYPE_SUBS,
                RC_REQUEST, mPurchaseFinishedListener, payload);
    }


    boolean verifyDeveloperPayload(Purchase p) {
        String payload = p.getDeveloperPayload();


        return true;
    }

    //購入フロー完了時に呼ばれる
    IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
        public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
            Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);

            // if we were disposed of in the meantime, quit.
            if (mHelper == null) return;

            if (result.isFailure()) {
                complain("Error purchasing: " + result);
                setWaitScreen(false);
                return;
            }
            if (!verifyDeveloperPayload(purchase)) {
                complain("Error purchasing. Authenticity verification failed.");
                setWaitScreen(false);
                return;
            }

            Log.d(TAG, "Purchase successful.");

            if (purchase.getSku().equals(SKU_GAS)) {
                // bought 1/4 tank of gas. So consume it.
                Log.d(TAG, "Purchase is gas. Starting gas consumption.");
                //購入アイテムの消費フローが走る
                mHelper.consumeAsync(purchase, mConsumeFinishedListener);
            }
            else if (purchase.getSku().equals(SKU_PREMIUM)) {
                // bought the premium upgrade!
                Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
                alert("Thank you for upgrading to premium!");
                mIsPremium = true;
                updateUi();
                setWaitScreen(false);
            }
            else if (purchase.getSku().equals(SKU_INFINITE_GAS)) {
                // bought the infinite gas subscription
                Log.d(TAG, "Infinite gas subscription purchased.");
                alert("Thank you for subscribing to infinite gas!");
                mSubscribedToInfiniteGas = true;
                mTank = TANK_MAX;
                updateUi();
                setWaitScreen(false);
            }
        }
    };

    //購入したアイテムの消費に完了した時に呼ばれる
    IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
        public void onConsumeFinished(Purchase purchase, IabResult result) {
            Log.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result);

            // if we were disposed of in the meantime, quit.
            if (mHelper == null) return;

            // We know this is the "gas" sku because it's the only one we consume,
            // so we don't check which sku was consumed. If you have more than one
            // sku, you probably should check...
            if (result.isSuccess()) {
                // successfully consumed, so we apply the effects of the item in our
                // game world's logic, which in our case means filling the gas tank a bit
                Log.d(TAG, "Consumption successful. Provisioning.");
                mTank = mTank == TANK_MAX ? TANK_MAX : mTank + 1;
                saveData();
                alert("You filled 1/4 tank. Your tank is now " + String.valueOf(mTank) + "/4 full!");
            }
            else {
                complain("Error while consuming: " + result);
            }
            updateUi();
            setWaitScreen(false);
            Log.d(TAG, "End consumption flow.");
        }
    };

    // Drive button clicked. Burn gas!
    public void onDriveButtonClicked(View arg0) {
        Log.d(TAG, "Drive button clicked.");
        if (!mSubscribedToInfiniteGas && mTank <= 0) alert("Oh, no! You are out of gas! Try buying some!");
        else {
            if (!mSubscribedToInfiniteGas) --mTank;
            saveData();
            alert("Vroooom, you drove a few miles.");
            updateUi();
            Log.d(TAG, "Vrooom. Tank is now " + mTank);
        }
    }

    // We're being destroyed. It's important to dispose of the helper here!
    @Override
    public void onDestroy() {
        super.onDestroy();

        // very important:
        Log.d(TAG, "Destroying helper.");
        if (mHelper != null) {
            mHelper.dispose();
            mHelper = null;
        }
    }

    // updates UI to reflect model
    public void updateUi() {
        // update the car color to reflect premium status or lack thereof
        ((ImageView)findViewById(R.id.free_or_premium)).setImageResource(mIsPremium ? R.drawable.premium : R.drawable.free);

        // "Upgrade" button is only visible if the user is not premium
        findViewById(R.id.upgrade_button).setVisibility(mIsPremium ? View.GONE : View.VISIBLE);

        // "Get infinite gas" button is only visible if the user is not subscribed yet
        findViewById(R.id.infinite_gas_button).setVisibility(mSubscribedToInfiniteGas ?
                View.GONE : View.VISIBLE);

        // update gas gauge to reflect tank status
        if (mSubscribedToInfiniteGas) {
            ((ImageView)findViewById(R.id.gas_gauge)).setImageResource(R.drawable.gas_inf);
        }
        else {
            int index = mTank >= TANK_RES_IDS.length ? TANK_RES_IDS.length - 1 : mTank;
            ((ImageView)findViewById(R.id.gas_gauge)).setImageResource(TANK_RES_IDS[index]);
        }
    }

    // Enables or disables the "please wait" screen.
    void setWaitScreen(boolean set) {
        findViewById(R.id.screen_main).setVisibility(set ? View.GONE : View.VISIBLE);
        findViewById(R.id.screen_wait).setVisibility(set ? View.VISIBLE : View.GONE);
    }

    void complain(String message) {
        Log.e(TAG, "**** TrivialDrive Error: " + message);
        alert("Error: " + message);
    }

    void alert(String message) {
        AlertDialog.Builder bld = new AlertDialog.Builder(this);
        bld.setMessage(message);
        bld.setNeutralButton("OK", null);
        Log.d(TAG, "Showing alert dialog: " + message);
        bld.create().show();
    }

    void saveData() {

        /*
         * WARNING: on a real application, we recommend you save data in a secure way to
         * prevent tampering. For simplicity in this sample, we simply store the data using a
         * SharedPreferences.
         */

        SharedPreferences.Editor spe = getPreferences(MODE_PRIVATE).edit();
        spe.putInt("tank", mTank);
        spe.commit();
        Log.d(TAG, "Saved data: tank = " + String.valueOf(mTank));
    }

    void loadData() {
        SharedPreferences sp = getPreferences(MODE_PRIVATE);
        mTank = sp.getInt("tank", 2);
        Log.d(TAG, "Loaded data: tank = " + String.valueOf(mTank));
    }
}

2013年11月12日火曜日

メニューの表示(onCreateOptionsMenu())

Androidの特徴の一つであるメニューボタン。
iPhoneにはないAndroid特有のボタンです。
iPhoneだと、画面のどこかに設定ボタンとか作らないとダメですが、
Androidだと、このメニューボタンを実装すればいいので、わりと楽です。

メニューボタンの生成

@Override
public boolean onCreateOptionsMenu(Menu menu) {
}

Acticvityを継承していれば使えます。
このメソッドの中でメニューを作っていきます。

Menu.add(int, int, int, String);
          groupid, 識別ID, ボタン配置優先順位, ボタン名

addした数だけ、メニューにボタンが配置されます。
レイアウトは勝手に作ってくれるので、わりと簡単で便利です。

■サンプルソース
package com.example.manifest;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;
import android.widget.Toast;

public class MainActivity extends Activity {
    //どのボタンが押されたかの識別用ID
    private final int Menu1 = Menu.FIRST;
    private final int Menu2 = Menu.FIRST + 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //適当にレイアウトを作成
        LinearLayout oLayout = new LinearLayout(this);
        setContentView(oLayout);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // メニューアイテムを追加します
        menu.add(Menu.NONE, Menu1, Menu.NONE, "メニュー1").setIcon(R.drawable.ic_launcher);
        menu.add(Menu.NONE, Menu2, Menu.NONE, "メニュー2");
        return super.onCreateOptionsMenu(menu);
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case Menu1:
                Toast.makeText(getApplicationContext(), "Menu1", Toast.LENGTH_SHORT).show();
                return true;
            case Menu2:
                Toast.makeText(getApplicationContext(), "Menu2", Toast.LENGTH_SHORT).show();
                return true;
        }
        return false;
    }

    //メニューボタンを押される度に呼ばれる
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);
        Log.d("メニュー","押された");
        return true;
    }

    //メニューが消える度に呼ばれる  
    @Override
    public void onOptionsMenuClosed (Menu menu) {
        super.onPrepareOptionsMenu(menu);
        Log.d("メニュー","閉じられた");
    }
}

■実行結果




メニュー画面が開くタイミングや閉じるタイミングのOverrideが既にあるのが、とても便利です。
また、サンプルソースで使っている定数ですが、
Menu.NONEが0
Menu.FIRSTが1
です。



2013年11月5日火曜日

Fileオブジェクト(getName(), getParent())

ファイルを扱う場合によく使うオブジェクトです。
フォルダを作ったり、存在しているかの確認等してくれます。

Fileオブジェクトの生成  ファイルのパズを指定
File fFile = new File( "C:\\test\\test.txt");

ファイルのパスはurlでも、ローカルでもどちらでも大丈夫です。

簡単なFileオブジェクトの使い方

■ファイル名の取得
fFile.getName()
→test.txt

■親ディレクトリの取得
fFile.getParent()
→C:\test

■ファイルかの判定
fFile.isFile()
→true or false

仮にFileオブジェクトで指定したファイルの場合には、
ファイルが実在しないと、trueになりません。

■ファイルが存在するのかの判定
fFile.exists();


基本的なところはこんな感じです。

Fileオブジェクト生成時に指定するパスをディレクトリーにした場合
のサンプルです。

■生成
File fParent = new File( fFile.getParent() );

■ディレクトリを作成する
fParent.mkdir()
→true or false
1つ上の階層のディレクトリが作成されていないとfalseになります。

■ディレクトリを作成する
fParent.mkdirs()
→true or false
マジ神レベル!!
上の階層がなくても作ってくれます。
また、Fileオブジェクト作成時にファイルを指定すると、その名前の
ディレクトリが作成されます。


Fileオブジェクトは上の方で書いたようにサーバー上のファイルも使えるので、
サーバー上のファイルをローカルに保存する際に、
ファイルがあるか?ディレクトリがあるか?の判定で便利です。

■ディレクトリ内のファイルを取得
fParent.listFiles()
→File配列

ディレクトリ内のファイルが配列になって、返ってきます。

■ファイル名の変更
fParent.renameTo(new File ( 変更先のファイル名 ) )
→true or false

ファイル名の変更します。
コピーではないので、元ファイルは消えます。
Linuxで言うmvみたいなものです。move。
なので、ファイルを移動させるメソッドとして利用できます。

また、指定するファイルはフルパスで。。。


本日のまとめ
■サンプルソース
import java.io.File;


public class main{

    public static void main(String [] args) {
        //対象のファイル
        String sPath = "C:\\test\\test.txt";

        //ファイルシステムの作成
        File fFile = new File( sPath );

        //ファイル名の取得
        System.out.println( fFile.getName() );

        //親ディレクトリの取得
        System.out.println( fFile.getParent() );

        //ファイルかディレクトリかの判定
        System.out.println( fFile.isFile() );

        //親ディレクトリのファイルシステム作成
        File fParent = new File( fFile.getParent() );

        //親ディレクトリが存在していなかったら作成
        if ( !fParent.exists() )System.out.println( fParent.mkdirs() );
    }
}

2013年11月1日金曜日

AndroidManifest.xml-01(Manifest.xml書き方)

ソースに関してはある程度はググれば調べられるけど、
マニフェストファイルについては、全然調べられない!!

っていう人を対象に、(自分もそうですが、)

AndroidManifest.xmlについて激基本的なところを説明したいと思います。


■サンプル

<manifest
  android:versioncode="1"
  android:versionname="1.0"
  package="com.example.manifest"
  xmlns:android="http://schemas.android.com/apk/res/android">
  <uses-sdk android:minsdkversion="8" android:targetsdkversion="17">
  <application
    android:allowbackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">
    <activity
      android:label="@string/app_name"
      android:name="com.example.manifest.MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN">
        <category android:name="android.intent.category.LAUNCHER">
      </category></action></intent-filter>
    </activity>
  </application>
</manifest>


上記は
Android4.2のSDKで新規プロジェクトを立ち上げたときの初期のAndroidManifest.xmlです。
これについて説明していきます。


<?xml version="1.0" encoding="utf-8"?>
<manifest
 xmlns:android="http://schemas.android.com/apk/res/android"

この部分は決まった形です。
無心で毎回何も考えずに記述しましょう


  package="com.example.manifest"

ここで指定したパッケージ名がGoglePlayのURLになります。
また、ここで指定するパッケージに初期起動時のActivityクラスを配置するのが個人的には
オススメです。

  android:versionCode="1"
  android:versionName="1.0"

android:versionCodeは整数が必須!!
GooglePlayにアプリを上げる際の管理番号になります。
GooglePlayにあげる場合には必ず、最後にあげた番号よりも
多い数字にしないといけません。

android:versionNameはユーザーがGoglePlayから、みられるバージョン
1.2とか1.0.0.1とかが可能

  <uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="17" />

minSdkVersionはアプリをインストールするのに必要な
AndroidAPIレベルの最低値です。
今回は8となっているので、Android 2.2未満の端末だと、
インストールできないことを示しています。

android:targetSdkVersion="17"はそのアプリをどのAndroidに
合わせるかを示しています。Build時に指定するバージョンと違い、
ザックリ言うと見え方をどのバージョンにするかの指定です。
ついでに言うと、このSdkVersionはなくても全然ビルドできます。


  <application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >

applicationタグ内の説明になります。
この中にアクテビティの設定を記述したりします。

android:allowBackup="true"
クラウドにバックアップすることを許可します。
Googleアカウントに紐づいているアプリを管理するあの機能を使えるっていうとです。
特に設定しなくてもアプリは動きます。

android:icon="@drawable/ic_launcher"
どの画像ファイルをアイコンにするかの設定です。
ここでいうアイコンとは、端末内で確認できるアプリの
iPhoneと違い画像サイズについてはサイズの規定とかとくにありません。(推奨サイズとかはあるかもしれませんが。。。。)

android:label="@string/app_name"
タイトルラベルに記述されるアプリのタイトルです。
ホーム画面で表示されるタイトルにもなります。
また、文字列を直書きでも大丈夫です。

android:theme="@style/AppTheme"

targetSdkVersionが14以上から、使えます。
簡単に言うとWebのCSS見たいなもので、
styleに文字色とか背景とかを設定できます。

android:allowBackup、icon、label、theme
の4つも特に記述しなくても、動くっちゃ動きます。
ただ、アイコンやタイトルぐらいは設定しておいた方がいいとは自分は思います。


    <activity
      android:name="com.example.manifest.MainActivity"
      android:label="@string/app_name" >
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>

android:name="com.example.manifest.MainActivity"
でActivityファイルのパスを指定します。
上で指定したパッケージ内なら".MainActivity"こんな書き方でも大丈夫です。
android:label="@string/app_name"アクティビティ内でのタイトルに表示されます。

      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>

<intent-filter>のこの部分は結構大事で、これがわかると本当にいろいろなことができるようになります。
これがiPhoneアプリとは違うAndroidの醍醐味的なところです。
だがしかし、結構難しいところでもあるので、さっくりと説明しますと、
他のアプリとの連携や他のアプリからの起動などを記述します。
例えば、カメラアプリから写真加工アプリを呼び出すみたいな。。。

で、今回書かれている内容をみると、
<action android:name="android.intent.action.MAIN" />
"android.intent.action.MAIN"で
データの受け渡しなしの初期起動アクティビティになります。
<category android:name="android.intent.category.LAUNCHER" />
こちらを記述することで、初期起動が可能なアクティビティとカテゴライズされます。



すんごい基本的なAndroidManifest.xmlについての説明は
以上になります。

AndroidManifest.xmlについてよくわからずに
いろいろな機能や広告SDKを組み込むとmanifestファイルが混沌して、
わけわかめーなファイルになってしまうので気を付けてください。

2013年10月21日月曜日

Webの表示-02(WebView,requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);, )

WeViewをもう少し使いやすくしたソースを紹介します。

サンプルソースで紹介しています。

タイトルバーにページを読み込む時のクルクル回る”あれ”を表示させる方法は

requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

ページの読み込みの進捗を表すプログレスバーの表示は
requestWindowFeature(Window.FEATURE_PROGRESS);

いづれも、タイトルバーを非表示にすると、
表示されないので、気を付けてください。



サンプルソースでは、urlの読み込み時に
urlを分析して、GooglePlayの画面に遷移させたり、
メーラーのスキームを検知した時の処理を記述しています。
また、WebViewのプラグインも何個か入れているので、
よかったら、サンプルソースのコメントアウトを確認してください。



■サンプルソース
package com.example.webview;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.view.Window;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings.PluginState;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.LinearLayout;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //レイアウトの作成
        LinearLayout oLayout = new LinearLayout(getApplicationContext());

        //ページ読み込み中にぐるぐる回す
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        //ページ読み込みの進捗を棒で表示
        requestWindowFeature(Window.FEATURE_PROGRESS);
        //WebVIewの生成・設定
        WebView oWebview = Create_WebView();
        //WebViewをレイアウトに追加
        oLayout.addView(oWebview);
        //レイアウトの設定
        setContentView(oLayout);
        //最初に読み込むページの設定
        oWebview.loadUrl("http://google.co.jp");
    }

    private WebView Create_WebView(){
        WebView view = new WebView(this);
        //JSに対応
        view.getSettings().setJavaScriptEnabled(true);
        //拡大できるようにする
        view.getSettings().setBuiltInZoomControls(true);
        //Web Storageを使う。Fcebookで使われている
        view.getSettings().setDomStorageEnabled(true);
        //FLASH対応
        view.getSettings().setPluginsEnabled(true);
        view.getSettings().setPluginState(PluginState.ON);
  
        setProgressBarIndeterminateVisibility(true);
        setProgressBarVisibility(true);
        try {
            view.setWebViewClient(new WebViewClient() {
                //ページの読み込み完了時に呼ばれる
                @Override
                public void onPageFinished(WebView view, String url) {
                    super.onPageFinished(view, url);
                    //プロセスバーの表示終了
                    setProgressBarIndeterminateVisibility(false);
                }
            
                //ページの読み込み時に呼ばれる
                @Override
                public void onPageStarted(WebView view, String url, Bitmap favicon) {
                    super.onPageStarted(view, url, favicon);
                    //プロセスバーの表示開始
                    setProgressBarIndeterminateVisibility(true);
                }

                //ページの読み込み前に呼ばれる。urlごとの処理はここで記述
                @Override
                public boolean shouldOverrideUrlLoading(WebView view,String url){
                    Toast.makeText(getApplicationContext(), url, Toast.LENGTH_SHORT).show();
                    //メーラーの立ち上げスキームを検知
                    if(url.substring(0, 7).equals("mailto:")) {
                        view.stopLoading();
                        Uri uri = Uri.parse(url);
                        //メーラーを立ち上げる
                        Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
                        startActivity(intent);
                        finish();

                    //Google Playのスキームを検知
                    }else if (url.startsWith("market://")){
                        //Google Playに飛ばす
                        Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                        startActivity(i);
                        return true;
                    //他は全部読み込む
                    }else{
                        view.loadUrl(url);
                    }
                    return true;
                }
            });
      
            // インジケータの表示
            view.setWebChromeClient(new WebChromeClient() {
                @Override
                public void onProgressChanged(WebView view, int progress) {
                    setProgress(progress * 100);
                    if(progress == 100) {
                        setProgressBarIndeterminateVisibility(false);
                        setProgressBarVisibility(false);
                    }
                }
            });
        } catch (Exception e) {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
        }
        return view;
    }
}



■実行結果

2013年10月17日木曜日

Webの表示-01(WebView )

アプリ内でWebページを開くすごい簡単な方法を説明します。

アプリでブラウザーを開くのではなく、
AndroidのWebViewを使う方法を説明します。

生成

WebView oWebview = new WebView(this);

注意点は引数のthisです。
Context型を渡せばいいかと思いきや、アクティビティを
渡さないと怒られます。

URLの読み込み
WebView型.loadUrl(開くURL);

こんな感じで、urlをロードします。

他にも
たくさんのメソッドが用意されています。

WebView型..goBack();              ページを1つ戻る
WebView型..goForward();        ページを1つ進める
WebView型..canGoBack();        ページを1つ戻れるかの判定
WebView型..canGoForward();   ページを1つ進められるかの判定
WebView型..reload();              ページを再読み込みする

あと、ネットワークに接続するので、
Manifest.xmlの許可の追加

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

が必要です。


■サンプルソース
package com.example.webview;

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

import android.webkit.WebView;
import android.widget.LinearLayout;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //レイアウトの作成
        LinearLayout oLayout = new LinearLayout(getApplicationContext());

        //WebVIewの生成
        WebView oWebview     = new WebView(this);
        //WebViewをレイアウトに追加
        oLayout.addView(oWebview);
        //レイアウトの設定
        setContentView(oLayout);
        //最初に読み込むページの設定
        oWebview.loadUrl("http://google.co.jp");

    }
}


■実行結果




とりあえず、WebViewを使う方法を紹介しましたが、
このままだとFlashページが表示できなかったり、スキーマの検知が
できなかったりと、あんまり便利でないです。
次にもう少し使い勝手のいいWebViewを紹介します。

2013年10月15日火曜日

タッチを検出(onTouchEvent, MotionEvent)

画面をお触りされたかの検出方法について、

Activityを継承 (extends)していれば、簡単に使えます。

方法

@Override
public boolean onTouchEvent(MotionEvent event) {
}

を作成すれば、
画面をタッチされる度に作成したメソッド(onTouchEvent)が呼ばれます。


■サンプルソース

package com.example.touch;

import android.os.Bundle;
import android.app.Activity;
import android.view.MotionEvent;
import android.widget.LinearLayout;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        LinearLayout oLayout = new LinearLayout(getApplicationContext());
        setContentView(oLayout);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        String sEvent = "";
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            sEvent = "タッチ";
            break;
        case MotionEvent.ACTION_UP:
            sEvent = "離した";
            break;
        case MotionEvent.ACTION_MOVE:
            sEvent = "動かしている";
            break;
        }
        String x = String.valueOf(event.getX());
        String y = String.valueOf(event.getY());

        String sTime =String.valueOf( event.getDownTime() );

        Toast.makeText(getApplicationContext(), sEvent + " 時間:" + sTime + "横:" + x + "縦:" + y, Toast.LENGTH_SHORT).show();

        return super.onTouchEvent(event);
    }
}

■実行結果


座標の数字の単位はpxです。何px目を押されたかを返されます。
画面の幅もpxで取得できるので、相対的にどこを押されたかの計算も簡単にできます。


MotionEvent.getActionの
リアクション判定は他にもたくさんあるのですが、
よく利用するのが上の3つです。

今回は
MotionEvent.getX()
                 .getY()
                 .getAction()
                 .getDownTime()

を使いましたが
                 getPressure()
をつかうと、どれくらいの強さでタッチされたかが、
返ってくるらしいっすよ!!

詳しくはこちらで

http://developer.android.com/reference/android/view/MotionEvent.html

2013年10月7日月曜日

サーバーに格納している画像を表示させる(ImageView, URL)

URLから、画像を表示する方法です。

作り方はbitmapファイルをURLから、作成して、
BitmapをImageViewで表示しています。

Http通信して、画像を持ってくる方法もありますが、
個人的には画像を表示するだけなら、こちらの方法がお勧めです。

■サンプルソース
package com.example.imageview;

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

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
   
        //レイアウトパラム定数(縦横の長さの定数)の格納
        final int WC = ViewGroup.LayoutParams.WRAP_CONTENT;
        final int MP = ViewGroup.LayoutParams.MATCH_PARENT;
     
        super.onCreate(savedInstanceState);
        //基礎画面の作成
        LinearLayout oLayout = new LinearLayout(getApplicationContext());
        oLayout.setOrientation(LinearLayout.VERTICAL);
        setContentView(oLayout);
       
        //普通のviewの生成
        ImageView oImg = new ImageView(getApplicationContext());
        //横MAXの縦幅は画像と同じ高さ
        oImg.setLayoutParams(new LinearLayout.LayoutParams(MP, WC));
       
        URL url;
        InputStream istream;
        try {
            //画像のURLを直うち
            url = new URL("https://www.gstatic.com/android/market_images/web/play_logo_x2.png");
            //インプットストリームで画像を読み込む
            istream = url.openStream();
            //読み込んだファイルをビットマップに変換
            Bitmap oBmp = BitmapFactory.decodeStream(istream);
            //ビットマップをImageViewに設定
            oImg.setImageBitmap(oBmp);
            //インプットストリームを閉じる
            istream.close();
        } catch (IOException e) {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
        oLayout.addView(oImg);
    }
}


■実行結果





こんな感じになります。

サーバーの画像を読みこむので、
通信が遮断されてる時や、サーバ自体の故障時には
エラーになります。
try catcchの中をちゃん考慮して、
記述してください。

どんな、エラー処理をすればいいかというと、
画像を読み込む前に電波があるのかの確認とか、
エラー時の画像を用意するとか必要になります。

2013年10月2日水曜日

エラー (you must specify a way to create the tab)

you must specify a way to create the tab

TabHost
TabSpec

でViewを作る際に出現したエラー


。。。。。


いろいろと調べたら、
TabSpecにタブ名とタブの画像の指定がなかったために発生したようでした。


2013年9月30日月曜日

リストビューの基礎(ListView, BaseAdapter)


これもまた、よくあるUIの
リストビューの作り方を説明します。

基本的な作り方はGridViewと同じで、

ListViewオブジェクトの生成
adapterの生成
adapterにアイテムを入れる
ListViewにadapterをsetな流れです。


サンプルソースでは
アイコン(画像)と共に文字をクラスに格納させ、画面に表示させています。

行の中にレイアウトを作成しているので、
固定的なリストビューでなく、カスタマイズ可能なリストビューになっています。

package com.example.pururun;

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

import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements OnItemClickListener{

    private List dataList = new ArrayList();
 
    private final int MP = ViewGroup.LayoutParams.MATCH_PARENT;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        //基礎のレイアウトを作成
        LinearLayout oLayout = new LinearLayout(getApplicationContext());
        //今回の主役リストビューの生成
        ListView oListView   = new ListView(getApplicationContext());
        //リストビューに入れるアイテムのAdapterを生成
        ListAdapter     oAdp = new ListAdapter(getApplicationContext());

        //リストビューのレイアウトを指定
        oListView.setLayoutParams(new LinearLayout.LayoutParams(MP, MP));
        //リストビューにアイテムadapterを設定
        oListView.setAdapter(oAdp);
        //リストビューをクリックした時の処理を設定
        oListView.setOnItemClickListener(this);

        for (int i = 0; i < 10; i ++){
            //配列にタイトルと画像を格納
            dataList.add(new ListItem(String.valueOf("番号:" + i), R.drawable.ic_launcher));
        }
        
        //Adapteの中身を更新
        oAdp.notifyDataSetChanged();

        //基礎のレイアウトにリストビューを追加
        oLayout.addView(oListView);
        //基礎のレイアウトを画面に設定
        setContentView(oLayout);
    }
 
    public class ListAdapter extends BaseAdapter{

        private Context mContext;
  
        public ListAdapter(Context context){
            //生成時のコンストラクターでコンテクストを取得
            mContext = context;
        }
  
        @Override
        public int getCount() {
            //リストの行数
            return dataList.size();
        }

        @Override
        public Object getItem(int posion) {
            //配列の中身を返す
            return dataList.get(posion);
        }

        @Override
        public long getItemId(int posion) {
            //現在の配列の場所
            return posion;
        }

        @Override
        public View getView(int position, View view, ViewGroup parent) {
            TextView title;
            ImageView imag;
            View v = view;
            if(v==null){
                //グリットビューの1マス内のレイアウトを作成
                LinearLayout oListLayout = new LinearLayout(mContext);
                //左から右にアイテムを追加
                oListLayout.setOrientation(LinearLayout.HORIZONTAL);
                //イメージビューを生成
                ImageView oImage = new ImageView(mContext);
                //テキストビューを生成
                TextView oText = new TextView(mContext);
                //判別用にタグをつける
                oImage.setTag("CellImage");
                oText.setTag("CellTitle");
                //グリットビュー用のレイアウトに追加
                oListLayout.addView(oImage);
                oListLayout.addView(oText);
                v = oListLayout;
            }
            //配列から、アイテムを取得
            ListItem oList = (ListItem)getItem(position);
            if( dataList != null){
            //タグからテキストビューを取得
            title = (TextView) v.findViewWithTag("CellTitle");
            //取得したテキストビューに文字列を設定
            title.setText(oList.Get_Text());
            //タグからイメージビューを取得
            imag = (ImageView) v.findViewWithTag("CellImage");
            //イメージビューに画像を設定
            imag.setBackgroundResource( oList.Get_Res() );
        }
        return v;
    }
}

    @Override
    public void onItemClick(AdapterView parent, View view, int position, long id) {
        //押された時のパラメーターを表示
        Toast.makeText(getApplicationContext(), "押下されたのは:" + String.valueOf(position), Toast.LENGTH_SHORT).show();
        Toast.makeText(getApplicationContext(), "IDは:" + String.valueOf(id), Toast.LENGTH_SHORT).show();
    }
}

■ListItemクラス

package com.example.pururun;

public class ListItem {

    private String sText;
    private int    iRes;

    public ListItem(String text, int res){
        //文字列を取得
        sText = text;
        //画像のResIDを取得
        iRes  = res;
    }

    public String Get_Text(){
        //文字列を返す
        return sText;
    }

    public int Get_Res(){
        //画像のResIDを返す
        return iRes;
    }
}


■実行結果


2013年9月24日火曜日

ページめくり(ViewPager, PagerAdapter )

書籍アプリのような、スワイプ操作に対応した画面を作りたい。
Playストアのような画面を作りたい。

そんな時に用いるのがPagerAdapt。。

使い方は簡単で、GridViewやListViewで用いたAdapterを使用します。

昔は外部ライブリを参照しないとViewPagerを使えませんでしたが、
Android3.0以降は外部ライブラリを参照しなくても利用できます。

※android-support-v4.jarで今回のサンプルソースを作成しました。




■サンプルソース
・メインアクティビティ

package com.example.pageadapter;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Color;
import android.support.v4.view.ViewPager;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
      
        // カスタム PagerAdapter を生成
        PagerAdapt oAdp = new PagerAdapt(getApplicationContext());
        oAdp.add(Color.RED);
        oAdp.add(Color.GREEN);
        oAdp.add(Color.WHITE);

        // ViewPager を生成
        ViewPager oViewPager = new ViewPager(getApplicationContext());
        oViewPager.setAdapter(oAdp);
        setContentView(oViewPager);
    }
}

・Adapter用ファイル

package com.example.pageadapter;

import java.util.ArrayList;

import com.example.pageadapter.R;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.support.v4.view.PagerAdapter;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class PagerAdapt extends PagerAdapter {

    private Context mContext;
    private ArrayList mList;

    //コンストラクター(クラスの生成時に呼ばれる)
    public PagerAdapt(Context context) {
        mContext = context;
        mList = new ArrayList();
    }

    //リストにアイテムを追加する
    public void add(Integer item) {
        mList.add(item);
    }

    //アイテム追加後に呼ばれる
    @Override
    public Object instantiateItem(ViewGroup oVg, int position) {
        // リストから取得
        Integer iColor = mList.get(position);
        //レイアウトを作成
        LinearLayout oLayout = Make_Linear();
        //背景にお色を付ける
        oLayout.setBackgroundColor(iColor);
        oVg.addView(oLayout);
        return oLayout;
    }

    //View を削除
    @Override
    public void destroyItem(ViewGroup oVg, int position, Object object) {
        oVg.removeView((View) object);
    }

    //アイテム数を返す
    @Override
    public int getCount() {
        return mList.size();
    }

    //View が存在するかの判定
    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == (LinearLayout) object;
    }
  
    private LinearLayout Make_Linear(){
        //LinearLayoutを生成
        LinearLayout oLayout = new LinearLayout(mContext);
        //縦に格納
        oLayout.setOrientation(LinearLayout.VERTICAL);
        for (int i = 0; i < 4; i++){
            //さらにレイアウトを作成
            LinearLayout oL = new LinearLayout(mContext);
            //横に格納
            oL.setOrientation(LinearLayout.HORIZONTAL);
            oLayout.addView(oL);
            for (int ii = 0; ii < 3; ii++ ){
                //これが最後
                LinearLayout oll = new LinearLayout(mContext);
                //縦に格納
                oll.setOrientation(LinearLayout.VERTICAL);
                //画像とテキストを格納
                oll.addView(Make_ImageView());
                oll.addView(Make_TextView());
                oL.addView(oll);
            }
        }
      
        return oLayout;
    }
  
    private TextView Make_TextView(){
        // TextView を生成
        TextView textView = new TextView(mContext);
        textView.setText("Hello World");
        textView.setTextColor(Color.BLACK);
        textView.setGravity(Gravity.CENTER);
        return textView;
    }

    private ImageView Make_ImageView(){
        // ImageViewを生成
        ImageView oIv = new ImageView(mContext);
        //Bitmapを作成
        Bitmap oBmp = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher);
        //Bitmapをimagviewに貼り付ける
        oIv.setImageBitmap(oBmp);
        return oIv;
    }
}


■実行結果








こんな感じで仕上がります。
何となくViewFlipperみたいな感じがしますが、
ViewFlipperとの違いは階層の深いLayoutを1画面に複数表示できることらしいです。
(本当かどうかはわからない)

2013年9月20日金曜日

layout_weightの設定( setLayoutParams )

weightを設定するとどのように表示されるのかを検証してみました。
基本的にはcssとかで使うときと同じ感覚で使えます。

まずはweightを指定しないとどうなるのか


ソース

package com.jp.layout;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Menu;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class MainActivity extends Activity {

    private final int WC = ViewGroup.LayoutParams.WRAP_CONTENT;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout oLayout = new LinearLayout(getApplicationContext());
     
        oLayout.addView( make_Image(R.drawable.ic_launcher));
        oLayout.addView( make_Image(R.drawable.ic_launcher));
        oLayout.addView( make_Image(R.drawable.ic_launcher));
        oLayout.setOrientation(LinearLayout.HORIZONTAL);
     
        setContentView(oLayout);
    }

    private ImageView make_Image(int ResID){
        ImageView img = new ImageView(getApplicationContext());
    Bitmap bmp = BitmapFactory.decodeResource(getResources(), ResID);
    img.setImageDrawable(getResources().getDrawable(ResID));
    img.setLayoutParams(new LinearLayout.LayoutParams(WC,WC));
    return img;
    }
}



このソースを

package com.jp.layout;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Menu;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class MainActivity extends Activity {

private final int WC = ViewGroup.LayoutParams.WRAP_CONTENT;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout oLayout = new LinearLayout(getApplicationContext());
     
        oLayout.addView( make_Image(R.drawable.ic_launcher, 1));
        oLayout.addView( make_Image(R.drawable.ic_launcher, 1));
        oLayout.addView( make_Image(R.drawable.ic_launcher, 1));
        oLayout.setOrientation(LinearLayout.HORIZONTAL);
     
        setContentView(oLayout);
    }

    private ImageView make_Image(int ResID, int weight){
        ImageView img = new ImageView(getApplicationContext());
    Bitmap bmp = BitmapFactory.decodeResource(getResources(), ResID);
    img.setImageDrawable(getResources().getDrawable(ResID));
    img.setLayoutParams(new LinearLayout.LayoutParams(WC, WC, weight));
    return img;
    }
 
    private ImageView make_Image(int ResID){
    ImageView img = new ImageView(getApplicationContext());
    Bitmap bmp = BitmapFactory.decodeResource(getResources(), ResID);
    img.setImageDrawable(getResources().getDrawable(ResID));
    img.setLayoutParams(new LinearLayout.LayoutParams(WC, WC));
    return img;
    }  
}

こんな感じにすると


ちゃんと均等に画像が割り振られていますね。

        oLayout.addView( make_Image(R.drawable.ic_launcher, 1));
        oLayout.addView( make_Image(R.drawable.ic_launcher, 1));
        oLayout.addView( make_Image(R.drawable.ic_launcher, 1));

この処理で、すべての画像を1:1:1で表示しています。


weightをいじって

        oLayout.addView( make_Image(R.drawable.ic_launcher, 1));
        oLayout.addView( make_Image(R.drawable.ic_launcher, 1));
        oLayout.addView( make_Image(R.drawable.ic_launcher, 2));

とすると




こんな感じになります。

わかるかな??

ちょっと比率がかわります。

2013年9月5日木曜日

layout_weightをjavaソースで書く(android:layout_weight=)

レイアウトファイルでよく指定する

android:layout_weight="1"

をjavaソースで書く時にどんなメソッドを使うのか、わからなくてなって
ググってもなかなか見つからないことがあるので、メモって見ます。

ちゃんと、覚えないといけないのですが、すぐ忘れてしまって。。。


オブジェクト.setLayoutParams(new LinearLayout.LayoutParams(
  ViewGroup.LayoutParams.MATCH_PARENT,
 ViewGroup.LayoutParams.MATCH_PARENT,
  weight));

setLayoutParamsのnew LinearLayout.LayoutParams()の中の第3引数にweightの
値を入れる

と覚えておこう

つまり

xml でこう書かれているobjectは
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"

javaソースで

private final int WC = ViewGroup.LayoutParams.WRAP_CONTENT;

オブジェクト.setLayoutParams(new LinearLayout.LayoutParams(WC,WC, weight));

と記述します。


2013年8月22日木曜日

?でsplit Exception in thread "main" java.util.regex.PatternSyntaxException: Dangling meta character '?' near index 0 ?

Exception in thread "main" java.util.regex.PatternSyntaxException: Dangling meta character '?' near index 0
?

"?" (はてな、クエッション)
でsplitをしようとしたら、このようなエラーがでました。

"http://XXXXXX.com/AAAAAAA/CCCCC/?page=2&mode=max&rtn_urk=http://XX&version=1.0"

こんなURLのパラメータだけを取得しようとして、失敗!!

とりあえず、splitから方向転換して、

String sUrl   = "http://XXXXXX.com/AAAAAAA/CCCCC/?page=2&mode=max&rtn_urk=http://XX&version=1.0";
String sParam = sUrl.substring( sUrl.indexOf("?") + 1);
System.out.println("パラメーター:" + sParam);
こんな感じにすると成功!!

出力結果
パラメーター:page=2&mode=max&rtn_urk=http://XX&version=1.0

splitはだめでしたが、indexOfは?を認識したようです。
なぜ??

このままでもいいんですが、何となくsplitできる方法をさがしたところ。。。


String[] sList = sUrl.split("\\?");

このようにエスケープ文字を2つ入れるとOKでした。

?がエスケープ文字とは知らなかったです。。。

2013年8月20日火曜日

エラー(java.lang.SecurityException: Requires READ_PHONE_STATE: Neither user 10030 nor current process has android.permission.READ_PHONE_STATE)

java.lang.RuntimeException: Unable to start activity ComponentInfo{パッケージ名/パッケージ名.起動クラス名}: java.lang.SecurityException: Requires READ_PHONE_STATE: Neither user 10030 nor current process has android.permission.READ_PHONE_STATE.

こんなエラーが出てしまった。。。

何だと、思いながらも、何となくわかってしまった!!

原因は

manifest.xmlのパーミッションの付け忘れだ!!

パーミッションがないのに、端末情報を参照しようとしたら、怒らたンゴ

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

↑これを追加したら、ちゃんと動きました。。


文字列(String型)

AndroidというよりJAVAよりな事ですが、
文字列を扱う各関数について、よく使うのを
紹介します。

■生成
String str = "Hello, World";

new はなくても生成できます

■文字長さ取得
int型 str.length();
かっこの中に何を入れるかはわかりませんが、
こんな使い方。
注意点は全角でも1文字と判定されることです。

■文字列が同じかの判定
boolean型  str.equals("判定文字");

=とか==とか===では同じ文字列かの判定はできません

ex)
if ( !str.eqquals("ok") ){
}
//文字列がokではないことを判定しています。


■指定した文字列から始まっているかの判定
boolean型 str.startsWith("判定文字列")


■指定した文字列の現れる位置を取得
int型 str.indexOf("判定文字列");

存在しない場合には,-1を返します。。


■型を変更させる
●文字列型から、数値型
Integer.valueOf("1245");

●数値型から、文字列型
String.valueOf(123556);

2013年8月10日土曜日

JSON( JSONObject, rootObject)

Androidアプリで、WEB通信の返し値をさばきたいときありますよね??

WEBAPIでよく使われる返し値はたいていJSON型なので、
JSONをパースしてみようと思います。

◆JSONオブジェクトの生成

JSONObject 変数名 = new JSONObject(文字列);

このように渡せば文字列がJSON型で返されます。

◆サンプルソース

{"result":"true","item_list":{"item_1":"apple","item_2":"banana","item_3":"melon"}}
こんなJSONがWEBAPIで返ってくるというていでお願いいたします。

package com.page.sss;

import org.json.JSONException;
import org.json.JSONObject;

import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //基礎画面の作成
        LinearLayout oLayout = new LinearLayout(getApplicationContext());
        oLayout.setOrientation(LinearLayout.VERTICAL);
        setContentView(oLayout); 
        
                
        //postデータを作成

            ~~~~~~
     ~~~~~~

        //WEBAPIにPOST時の返し値を取得
        String sRet = post(sUrl);
        //テキストビューに出力
        oLayout.addView(Make_TextView("返し値:" + sRet));
        
        try {
            //文字列をJSONオブジェクトに変換
            JSONObject rootObject = new JSONObject(sRet);

            //キーから、アイテムを取得する
            JSONObject itemobject = (JSONObject)rootObject.get("item_list");
            //JSONオブジェクト型から、文字列に変換
            String sKekka = itemobject.toString();
            //テキストビューに出力
            oLayout.addView(Make_TextView("1階層掘り下げ:" + sKekka) );
            //テキストビューに出力
            oLayout.addView(Make_TextView("一番奥の情報:" + itemobject.get("item_2").toString()) );
        } catch (JSONException e) {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
    }
    
    private TextView Make_TextView(String sText){
        TextView oTv = new TextView(getApplicationContext());
        oTv.setText(sText);
        return oTv;
    }
    
}
◆実行結果
こんな、感じで無事に文字列を取得できました!! JSONObject itemobject = (JSONObject)rootObject.get("item_list"); この部分はもっと上手く作る方法もあるかと思うんですが、どうなんだろう。。。。 個人的にはPHPのように簡単に連想配列にできるわけでもなく、JSONって結構面倒 とか思っているのですが、とりあえずできてよかったです。