2015年8月31日月曜日

3DS版ドラクエ8を購入しました。 - プレイ日記 理不尽だけど楽しいファンタジーの世界 - その1

  • 公開日:2015年08月30日

プレイ時期

  • 開始日: 2015年08月29日
  • 終了日: 不明

記事内容


3DS版のドラクエ8のプレイ日記です。
一年半ぶりにゲームをしたので、せっかくなので記事を書きました。

ドラゴンクエスト8とは


国民的人気のRPGドラゴンクエストシリーズの8作目です。
オリジナルはPlayStation 2です。
3DS版ではキャラクターにボイスが追加されたり、シナリオにもエピソードが加わっているようです。

オープニング - ポルトリンク


キャラクターが喋るので、これまでのドラクエと随分と異なる印象を受けました。
個人的には感情移入がしやすくなって良いと思いました。
ちょっとアニメっぽいとも思いました。ドラゴンボールみたいです。

さて、物語は酒場で会った酔っぱらいのオッサンの娘から、滝の洞窟に水晶球を取ってくるように頼まれます。

見ず知らずの旅人に頼むとかありえない

と思ってしまうのは、僕が社会人として世の中の仕組みをしってしまったからなのでしょう。
だいたい、酒場でからんでくる酔っぱらいのオッサンとか、僕苦手なんですよねー。たまーに仲良くなっちゃうけど。
まあ、そんなこといっていたらゲームが進まないので、滝の洞窟へ向かいます。

滝の洞窟


うわ、モンスターがいっぱいいる。

依頼料よこせや

と思ってしまうのは、僕がビジネスマンだからでしょうか…。
しかも、中のモンスターが結構強い。いくら若くて可愛い女の子の頼みでも、タダ働きとかありえないんですけど。

奥に進むといかにも「ボス」です、みたいなモンスターが待ち構えています。
でも、ここでは戦闘は発生しないで先に進むことができます。

最奥に着くと本当にボスバトルが発生。

速攻全滅しました

なんか敵強いんですけど。せっかくたまったゴールドが半分に。保険とかないの?

2回目は戦闘方法を主人公の【ためる】攻撃中心にして撃破。

水晶球を奪い返し、町に戻って酔っぱらいのオッサンに話しかけると

わざと捨てたんだ。余計なことしやがって。

と、まさかの逆ギレ。

無職で酔っぱらいの元占い師

で、人の親切まで踏みにじるとか、すごいキャラクターや。

この後、イベントが発生し、物語が進行します。
久しぶりにRPGの世界を体験しているのに、

心が歪んでる

と実感した数時間でした。

リーザス村


街にはいる早々、ポルクという子供が戦闘をしかけてきます。

遠慮なく「ためる」コマンドを選択

しかし、戦闘は強制中断。まあ、そりゃそうだな。
そして、なんだかんだとリーザス像の塔へ向かった少女ゼシカを探しにいくことへ。
だからなぜ見知らぬ旅人に頼むのだ?

リーザス像の塔


リーザス像の塔まではポルク少年が一緒に来ます。
そのポルク少年ですが、塔の入口を開けると、まかせたと言って街へ帰っていきます。

どう考えても一人で帰るとかやばいだろ

突っ込んだら負けとはわかっているんですけどね。

しかし、この塔。まじで敵が強い。
逃げまくりながら先へ進むと、女神像を発見。

そして、後ろからゼシカさん登場。めっちゃかわいい女の子。すると、

この兄さんのかたきめ!

は?なにこの糞女。前言撤回。全然かわいくねえ。
炎で攻撃してくるのを交わしていると、女神像から死んだお兄さんの声が。

回想が始まり、ゼシカちゃん号泣。

それにしても、回想のドルマゲスさん

すごく変態っぽい

人でした。嫌いじゃない。こういう人。

さて、街に戻ると、ゼシカさんは親と喧嘩して家出。
しかも、盗賊呼ばわりした我々に詫びの言葉もなく。

この後、ポルトリンクでのボス戦を通じてゼシカちゃんが仲間に。でも、

メラしか使えない

なんなのさ、このねーちゃんは…。

次回へ続きます。

3DS版ドラゴンクエスト


攻略本


この記事がお役にたちましたらシェアをお願いします

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

2015年8月30日日曜日

Android Volleyで画像URLから画像を表示する

  • 公開日:2015年08月30日

記事概要


AndroidでVolleyを使用して画像URLから画像を表示する方法の記事です

環境


  • Android Studio 1.3.0
  • OS X Yosemite
  • android sdk 23(Android 6.0 Marshmallow(マシュマロ))
  • com.android.support:recyclerview-v7:23.0.0
  • compile 'com.mcxiaoke.volley:library:1.0.18'

Volley


Androidでネットワーク接続の処理をおこなうときに便利なフレームワークです。
データの習得は得意ですが、画像のアップロード、更新、登録処理には向きません。
データ習得以外には利用しないほうがよいと思います。

gradleの設定


volleyをアプリで利用できるよう、以下のように設定します。


dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:support-v4:23.0.0'
    compile 'com.mcxiaoke.volley:library:1.0.18'
    compile 'com.google.code.gson:gson:2.3.1'
}

volleyを使う場合、gsonを利用可能にしておくと便利です。

今回利用する画面


上記は、openWeatherMapの一覧リストの画面です。この画面の天気アイコン画像をvolleyで非同期表示実装をおこないます。
また、このリスト表示はRecyclerViewで作成しています。詳しい実装方法が知りたい場合はこの記事を参考にしてください。

画像Viewの設定


まずは、com.android.volley.toolbox.NetworkImageViewをレイアウトxmlに設定します。


    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/weather_image"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_toRightOf="@id/time_value"
        android:layout_marginLeft="5dp"
        android:scaleType="centerCrop"/>

layout_widthとlayout_heightはサイズを指定しておきましょう。 そうしないと表示が小さくなりすぎてしまいます。

BitmapCacheの実装


画像をキャッシュするBitmapCacheを実装します。この実装はなくても特に問題ないのですが、メモリを効率的に扱うために実装します。
サイズの大きい画像を扱うアプリのときには、必ず実装しましょう。


public class BitmapCache implements ImageLoader.ImageCache{

    private LruCache<String, Bitmap> mMemoryCache;

    /**
     * create BitmapCache instance
     * @return
     */
    public static BitmapCache getInstance() {
        BitmapCache bitmapCache = null;

        if (bitmapCache == null) {
            bitmapCache = new BitmapCache(getDefaultLruCacheSize());
        }
        return bitmapCache;
    }

    /**
     * Don't instantiate this class directly, use
     * @param memCacheSize Memory cache size in KB.
     */
    private BitmapCache(int memCacheSize) {
        init(memCacheSize);
    }

    /**
     * Initialize the cache.
     */
    private void init(int memCacheSize) {
        // Set up memory cache
        mMemoryCache = new LruCache<String, Bitmap>(memCacheSize) {
            /**
             * Measure item size in kilobytes rather than units which is more practical
             * for a bitmap cache
             */
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                final int bitmapSize = getBitmapSize(bitmap) / 1024;
                return bitmapSize == 0 ? 1 : bitmapSize;
            }
        };
    }

    /**
     * Get the size in bytes of a bitmap.
     */
    public static int getBitmapSize(Bitmap bitmap) {
        return bitmap.getByteCount();
    }

    @Override
    public Bitmap getBitmap(String key) {
        return getBitmapFromMemCache(key);
    }

    @Override
    public void putBitmap(String key, Bitmap bitmap) {
        addBitmapToCache(key, bitmap);
    }

    /**
     * Adds a bitmap to both memory and disk cache.
     * @param data Unique identifier for the bitmap to store
     * @param bitmap The bitmap to store
     */
    public void addBitmapToCache(String data, Bitmap bitmap) {
        if (data == null || bitmap == null) {
            return;
        }

        synchronized (mMemoryCache) {
            // Add to memory cache
            if (mMemoryCache.get(data) == null) {
                mMemoryCache.put(data, bitmap);
            }
        }
    }

    /**
     * Get from memory cache.
     *
     * @param data Unique identifier for which item to get
     * @return The bitmap if found in cache, null otherwise
     */
    public Bitmap getBitmapFromMemCache(String data) {
        if (data != null) {
            synchronized (mMemoryCache) {
                final Bitmap memBitmap = mMemoryCache.get(data);
                if (memBitmap != null) {
                    return memBitmap;
                }
            }
        }
        return null;
    }

    /**
     * Clears the memory cache.
     */
    public void clearCache() {
        if (mMemoryCache != null) {
            mMemoryCache.evictAll();
        }
    }

    public static int getDefaultLruCacheSize() {
        final int maxMemory =
                (int) (Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 8;

        return cacheSize;
    }
}

BitmapCacheのインスタンス作成をsingletonで行っていますが、newでオブジェクト生成しても同じです

Adapterの実装


上記で作成したBitmapCacheクラスとVolleyのImageLoaderクラスを使ってAdapterを実装します。


    private ImageLoader mImageLoader;

    /**
     * Initialize the dataset of the Adapter.
     *
     * @param dataSet String[] containing the data to populate views to be used by RecyclerView.
     */
    public WeatherAdapter(Context context, List<WeatherItem> dataSet) {
        mDataSet = dataSet;
        ImageLoader.ImageCache imageCache = BitmapCache.getInstance();
        mImageLoader = new ImageLoader(Volley.newRequestQueue(context), imageCache);
    }


    viewHolder.getWeatherView().setImageUrl (weatherItem.getDisplayImageURL(), mImageLoader);

Adapterを作成するときに、ImageLoaderを生成して変数として保持してしまいます。
あとは、データ生成をしているviewHolderのsetImageUrlに画像urlとImageLoaderオブジェクトを引数に設定すれば終了です。

さて、ビルドして表示を確認してみましょう。

綺麗に表示されています。

まとめ


volleyは優秀なネットワークフレームワークなのですが、学習コストが少し高いです。
私も初めてプロジェクトで利用した時は、結構痛い目に会いました。
フレームワークは問題を解決するための手段に過ぎません。
プロジェクトの状況(メンバーの力量やアプリの仕様)に合わせて適切なフレームワークを使ってください。
また、時には仕様をフレームワークに合わせてみることもお薦めします。アプリとして無駄なことをやっていることが見えてくるかもしれません。

以上

Androidアプリ開発にオススメの本


開発にあると便利なオススメ製品


関連記事 参考サイト

この記事がお役にたちましたらシェアをお願いします

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

Android RecyclerViewの実装 viewTypeでViewを切り替える

  • 公開日:2015年08月30日

記事概要


AndroidのRecyclerViewでViewを切り替える実装方法を記載した記事です

環境


  • Android Studio 1.3.0
  • OS X Yosemite
  • android sdk 23(Android 6.0 Marshmallow(マシュマロ))
  • com.android.support:recyclerview-v7:23.0.0

はじめに


RecyclerViewを使ったリスト表示の仕方はこの記事を参考にしてください。
また、RecyclerViewを使った下線表示の仕方はこの記事を参考にしてください。
今回は、RecyclerViewでViewを切り替える実装方法を説明します。

RecyclerViewでViewを切り替える


RecyclerViewでリストを表示する場合に、ある一定条件で表示するviewを変更したくなる場合があると思います。

例を挙げると、以下のような画面です。

上記はOpenWeatherMapで取得した予報データをリスト表示しています。
5日分のデータを表示しているのですが、上記の表示方法では日付の区切りがわかりません。
なので、日付が変更になるごとにTextViewで日付を表示するようなラベルのViewを挟む実装をしたいと思います。

Adapterクラスの実装


RecyclerView.AdapterにはviewTypeを習得するメソッドgetItemViewTypeメソッドが用意されています。
このメソッドをオーバーライドします。


    @Override
    public int getItemViewType(int position) {
        WeatherItem weatherItem = mDataSet.get(position);
        if (weatherItem.isLabel) {
            return -1;
        }
        return position;
    }

上記の内容はサンプルです。やっていることは、Adapterで扱うリストの中で、異なるviewを利用するデータのときに、任意のViewTypeの値を返すようにしています。

この戻り値はonCreateViewHolderメソッドで習得することができます。
以下のように実装します。


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        // Create a new view.
        View v = null;

        if (viewType == -1) {
            v = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.weather_row_label, viewGroup, false);

        } else {
            v = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.weather_row_item, viewGroup, false);

        }

        return new ViewHolder(v, viewType);

    }

上記ならviewTypeが-1とその他の値の場合で異なるlayoutのxmlを生成しています。
ViewHolderはRecyclerView.ViewHolderの継承クラスです。ここでは以下のように実装します。


    public static class ViewHolder extends RecyclerView.ViewHolder {
        private TextView timeView;
        private ImageView weatherView;
        private TextView temperatureView;
        private TextView windView;
        private TextView pressureView;

        private TextView timeLabel;

        public ViewHolder(View v, int viewType) {
            super(v);
            // Define click listener for the ViewHolder's View.
            v.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.d(TAG, "Element " + getPosition() + " clicked.");
                }
            });

            if (viewType != -1) {
                timeView = (TextView) v.findViewById(R.id.time_value);
                weatherView = (ImageView) v.findViewById(R.id.weather_image);
                temperatureView = (TextView) v.findViewById(R.id.temperature_value);
                windView = (TextView) v.findViewById(R.id.wind_value);
                pressureView = (TextView) v.findViewById(R.id.pressure_value);
            } else {
                timeLabel = (TextView) v.findViewById(R.id.time_label);
            }

        }

        public TextView getTimeView() {
            return timeView;
        }

        public ImageView getWeatherView() {
            return weatherView;
        }

        public TextView getTemperatureView() {
            return temperatureView;
        }

        public TextView getWindView() {
            return windView;
        }

        public TextView getPressureView() {
            return pressureView;
        }

        public TextView getTimeLabel() {
            return timeLabel;
        }


    }

引数で受け取るviewTypeごとに異なるviewのオブジェクトからfieldのオブジェクトを生成しています。
最後にonBindViewHolderメソッドでデータを設定します。


    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
        Log.d(TAG, "Element " + position + " set.");

        // Get element from your dataset at this position and replace the contents of the view
        // with that element
        WeatherItem weatherItem = mDataSet.get(position);
        if (viewHolder.getItemViewType() == -1) {
            viewHolder.getTimeLabel().setText(weatherItem.getDisplayDateLabel());
        } else {
            viewHolder.getTimeView().setText(weatherItem.getDisplayTime());
            viewHolder.getWeatherView().setImageResource(R.mipmap.clouds_n);
            viewHolder.getTemperatureView().setText(weatherItem.getDisplayMaxTemperature());
            viewHolder.getWindView().setText(weatherItem.getDisplayWind());
            viewHolder.getPressureView().setText(weatherItem.getDisplayPressure());
        }

    }

viewHolder.getItemViewType()で場合分けしている箇所がポイントです。
では、上記の変更をビルドして画面を立ち上げてみましょう。

日付ごとにラベルが表示されています。

まとめ


RecyclerViewではviewTypeを使うことで自由にViewを切り替えることができます。
RecyclerView.ViewHolderを継承したクラスを複数作ることで、もっと可読性の高いコードにすることも可能だと思います。
viewを4つ5つと多く切り替える必要がある場合は、RecyclerView.ViewHolderを継承したクラスを複数作るほうが後々リファクタリングも楽でしょう。

以上

Androidアプリ開発にオススメの本


開発にあると便利なオススメ製品


関連記事 参考サイト

この記事がお役にたちましたらシェアをお願いします

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

2015年8月29日土曜日

Androidアプリ開発の準備をテンプレート化した - その1 Android Studioを最新にする -

  • 公開日:2015年08月29日

記事概要


androidのアプリ開発をする前の準備の手順をまとめました。

環境


  • Android Studio 1.0.1 → 1.3.0
  • OS X Yosemite

はじめに


androidアプリ開発をする時、後々のリリースやアップデートのため、最初にやっておくと便利な作業がいくつかあります。
そのノウハウをまとめました。
また、この記事で記載した準備と手順は、2015年08月現在のものです。androidの開発環境は日進月歩の世界です。常に色々と試してみることが重要です。

Android Studioを最新にする


まずはAndroid Studioを最新にします。この記事を書く時点で、私は4,5ヶ月ぶりのandroidアプリの作成作業なのですが、調査するとAndroid Studioが 1.0.1 → 1.3.0にアップしていました。相変わらず更新速度が早いです。
では、さっそくAndroid Studioをandroid developerから取得しましょう。

Android Studioには更新機能があります。しかし、今回のように長期間更新をしなかった場合や、Android Studioに大きな変更が入った場合は、新しいAndroid Studioをダウンロードして、手動で更新作業を行うことになります。

ダウンロードしたdmgファイルを解凍して、アプリケーションフォルダに移動させます。

ポップアップで警告がでますが「置き換える」を選択します。
置き換えたら、Android Studioを立ち上げます。

前のAndroid Studioの設定を引き継ぎたいので、一番上のラジオボタンを選択します。

これまで通りプロジェクト選択画面が表示されるので、任意のプロジェクトを選択します。

プロジェクトを選択すると、アプリケーションのビルドが開始されます。indexを更新するので結構時間がかかります。ビルドが無事に終了したら、Android Studioの設定からバージョンを確認してみましょう。

無事に1.3.0に変更されています。
これでAndroid Studioは最新バージョンになりました。次はプロジェクトを作成していきます。

Androidアプリ開発にオススメの本

開発にあると便利なオススメ製品


関連記事 参考サイト

この記事がお役にたちましたらシェアをお願いします

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

2015年8月28日金曜日

Android OrmLiteの導入

  • 公開日:2015年08月28日

記事概要


AndroidでOrmLiteを利用した実装方法を記載した記事です。(検証した結果OrmLiteの導入は見送りました)

環境


  • Android Studio 1.3.0
  • OS X Yosemite
  • android sdk 23(Android 6.0 Marshmallow(マシュマロ))
  • OrmLite

はじめに


最近ではAndroidでも色々なORM(データベースとオブジェクト指向プログラミング言語の間の非互換なデータを変換するプログラミング技法)が用意されています。
私はこれまで自作のDB操作クラスを利用してたのですが、今回はOrmLiteを調査したので、その方法をまとめました。

gradleの設定


まずはOrmLiteを利用できるようにgradleの設定を行ないます。
android sdkは23.0(Android 6.0 Marshmallow(マシュマロ))を利用します。


apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.0"

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 23
    }

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.j256.ormlite:ormlite-android:4.48'
}

compile 'com.j256.ormlite:ormlite-android:4.48'でOrmLiteを利用できるように設定しています。

DatabaseHelperの実装


続いてDatabaseHelperを実装します。DatabaseHelperはOrmLiteSqliteOpenHelperを継承したクラスにします。
Databaseの作成、更新、処理時のクローズ処理等の共通処理の役割を担うようにします。
パッケージを分けている場合は、FragmentやActivityのui系とは別のutilに置くのが一般的でしょう。


public class DatabaseHelper extends OrmLiteSqliteOpenHelper {

    // name of the database file for your application -- change to something appropriate for your app
    private static final String DATABASE_NAME = "sample.db";
    // any time you make changes to your database objects, you may have to increase the database version
    private static final int DATABASE_VERSION = 1;

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION, R.raw.ormlite_config);
    }

    /**
     * This is called when the database is first created. Usually you should call createTable statements here to create
     * the tables that will store your data.
     */
    @Override
    public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
        try {
            Log.i(DatabaseHelper.class.getName(), "onCreate");
            TableUtils.createTable(connectionSource, LocationItem.class);
        } catch (SQLException e) {
            Log.e(DatabaseHelper.class.getName(), "Can't create database", e);
            throw new RuntimeException(e);
        }

        Log.i(DatabaseHelper.class.getName(), "created new entries in onCreate: ");
    }

    /**
     * This is called when your application is upgraded and it has a higher version number. This allows you to adjust
     * the various data to match the new version number.
     */
    @Override
    public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
        try {
            Log.i(DatabaseHelper.class.getName(), "onUpgrade");
            TableUtils.dropTable(connectionSource, LocationItem.class, true);
            // after we drop the old databases, we create the new ones
            onCreate(db, connectionSource);
        } catch (SQLException e) {
            Log.e(DatabaseHelper.class.getName(), "Can't drop databases", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * Close the database connections and clear any cached DAOs.
     */
    @Override
    public void close() {
        super.close();
    }
}

続いてormlite_config.txtを用意します。ormlite_config.txtには作成するテーブルの情報を記載します。
取得した現在情報を格納するLocationテーブルを作成します。
配置場所はres/rawの配下です。


#
# generated on 2015/08/27
#
# --table-start--
dataClass=mtchannel.com.mtscouter.model.Location
tableName=location
# --table-fields-start--
# --field-start--
fieldName=id
generatedId=true
# --field-end--
# --field-start--
fieldName=latitude
# --field-end--
# --field-start--
fieldName=longitude
# --field-end--
# --field-start--
fieldName=altitude
# --field-end--
# --field-start--
fieldName=created_at
# --field-end--
# --field-start--
fieldName=updated_at
# --field-end--
# --table-fields-end--
# --table-end--

最後にテーブルにアクセスするクラスを作成します。


@DatabaseTable(tableName = "location" )
public class Location {

    // id is generated by the database and set on the object automagically
    @DatabaseField(generatedId = true)
    int id;
    String latitude;
    @DatabaseField
    String longitude;
    @DatabaseField
    String altitude;
    @DatabaseField
    Date created_at;
    @DatabaseField
    Date updated_at;

    Location() {
        // needed by ormlite
    }

}

これで準備は完了です。あとはActivityからOpenHelperManager.getHelperメソッドを利用すればsqliteを利用できます。
しかし、さらに調査をすすめたところ、sqlの実行速度の問題やリフレクションにコストがかかる等の問題点があるのがわかりました。
それらを考慮した結果、自作のDB操作クラスをリファクタリングして使用することにしました。

まとめ


Androidの実装にORMを使うと柔軟性が著しく落ちる気がします。
ContentProviderかSQLiteOpenHelperを使って実装するのが、結局はBEST PRACTICEというのが、android1.6の時代から実装してきた結論になります。

以上

Androidアプリ開発にオススメの本


開発にあると便利なオススメ製品


参考サイト

この記事がお役にたちましたらシェアをお願いします

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

2015年8月27日木曜日

Android RecyclerViewの実装 区切り線を表示する

  • 公開日:2015年08月27日
  • 最終更新日:2015年08月30日

記事概要


AndroidのRecyclerViewで区切り線を実装する方法を記載した記事です

環境


  • Android Studio 1.3.0
  • OS X Yosemite
  • android sdk 23(Android 6.0 Marshmallow(マシュマロ))
  • com.android.support:recyclerview-v7:23.0.0

はじめに


RecyclerViewを使ったリスト表示の仕方はこの記事を参考にしてください。
RecyclerViewでViewを切り替える実装方法はこの記事を参考にしてください。
今回は、RecyclerViewの区切り線の実装方法を説明します。

ListViewでの区切り線


ListViewで区切り線を表示する場合は、レイアウトxmlに


// 色
android:divider="#808080"

// 高さ
android:dividerHeight="2sp"

上記のように記載することで区切り線を表示していました。(昔の記事)

RecyclerViewで区切り線の実装


RecyclerViewの場合は、上記のようなxmlによる属性が用意されていないので、javaで実装する必要があります。
RecyclerView.ItemDecorationというクラスが用意されているので、このクラスを継承して作成します。
このクラスを使うことで、Adapterが保持しているデータセットに対して描画やレイアウトのようなエフェクトを付加することができます。


public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };

    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;

    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;

    private Drawable mDivider;

    private int mOrientation;

    public DividerItemDecoration(Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }

    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent) {
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    public void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}

上記で記述したコードをFragmentやActivityで利用しているRecyclerViewに設定します。


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_test, container, false);
        mRecyclerView = (RecyclerView)view.findViewById(R.id.recyclerView);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(mLayoutManager);

        // add ItemDecoration
        RecyclerView.ItemDecoration itemDecoration = new
                DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL_LIST);
        mRecyclerView.addItemDecoration(itemDecoration);

        mAdapter = new TestAdapter(mDataset);
        // Set TestAdapter as the adapter for RecyclerView.
        mRecyclerView.setAdapter(mAdapter);
        // END_INCLUDE(initializeRecyclerView)

        return view;
    }

この時、addItemDecorationを呼び出す位置に注意してください。setAdapterの後にaddItemDecorationを記述すると、区切り線が表示されません。setAdapterを呼び出す前にaddItemDecorationを呼び出します。
きちんと実装できていれば以下のように表示されます。

まとめ


RecyclerViewは柔軟性が高い代わりに、必要な機能を自分で実装していく必要があります。
以前のListViewの機能のみで十分で、今後も拡張する予定がない場合はRecyclerViewは使わなくても良いでしょう。
ただ、個人的には最初からRecyclerViewで実装しておいたほうがあとあと拡張しやすく便利だとは思います。

以上

Androidアプリ開発にオススメの本


開発にあると便利なオススメ製品


関連記事 参考サイト

この記事がお役にたちましたらシェアをお願いします

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

2015年8月25日火曜日

Android RecyclerViewの実装

  • 公開日:2015年08月25日
  • 最終更新日:2016年04月05日

記事概要


AndroidでRecyclerViewの実装方法を記載した記事です
RecyclerViewでの区切り線の表示の仕方はこの記事を参考にしてください。
RecyclerViewでViewを切り替える実装方法はこの記事を参考にしてください。

gitのサンプルコード付きの、より詳細な記事を記述しました。

環境


  • Android Studio 1.3.0
  • OS X Yosemite
  • android sdk 23(Android 6.0 Marshmallow(マシュマロ))

gradleの設定


まずはRecyclerViewを利用できるようにgradleの設定を行ないます。
android sdkは23.0(Android 6.0 Marshmallow(マシュマロ))を利用します。


apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.0"

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 23
    }

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

compile 'com.android.support:recyclerview-v7:23.0.0'でRecyclerViewを利用できるように設定しています。

RecyclerViewの実装


続いてRecyclerViewを実装します。Fragmentに実装します。
まずはxmlです。


<FrameLayout 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">

    <RelativeLayout
        android:id="@+id/test_relative"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        >
        
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </RelativeLayout>

</FrameLayout>

android.support.v7.widget.RecyclerViewはRelativeLayoutの子にするのがコツです。RecyclerViewは、親で利用できないので注意してください。

続いてFragmentを実装します。



public class TestFragment extends Fragment {

    private OnFragmentInteractionListener mListener;

    protected RecyclerView mRecyclerView;
    protected RecyclerView.LayoutManager mLayoutManager;

    protected TestAdapter mAdapter;
    protected List<TestItem> mDataset;

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

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_test, container, false);
        mRecyclerView = (RecyclerView)view.findViewById(R.id.recyclerView);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(mLayoutManager);

        mAdapter = new TestAdapter(mDataset);
        // Set TestAdapter as the adapter for RecyclerView.
        mRecyclerView.setAdapter(mAdapter);
        // END_INCLUDE(initializeRecyclerView)

        return view;
    }

    private void initDataset() {
        mDataset = new ArrayList<TestItem>();
        for (int i = 0; i < 10; i++) {
            TestItem testItem = new TestItem();
            testItem.time = "" + i;
            mDataset.add(testItem);
        }
    }
}

通常のListFragmentと同じでAdapterを実装する必要があります。
続いてAdapterを実装します。


public class TestAdapter extends RecyclerView.Adapter<TestAdapter.ViewHolder> {

    private static final String TAG = "TestAdapter";

    private List<TestItem> mDataSet;

    // BEGIN_INCLUDE(recyclerViewSampleViewHolder)
    /**
     * Provide a reference to the type of views that you are using (custom ViewHolder)
     */
    public static class ViewHolder extends RecyclerView.ViewHolder {
        private final TextView textView;
        private final ImageView imageView;


        public ViewHolder(View v) {
            super(v);
            // Define click listener for the ViewHolder's View.
            v.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.d(TAG, "Element " + getPosition() + " clicked.");
                }
            });
            textView = (TextView) v.findViewById(R.id.textView);
            imageView = (ImageView) v.findViewById(R.id.imageView);
        }

        public TextView getTextView() {
            return textView;
        }

        public ImageView getImageView() {
            return imageView;
        }
    }
    // END_INCLUDE(recyclerViewSampleViewHolder)

    /**
     * Initialize the dataset of the Adapter.
     *
     * @param dataSet String[] containing the data to populate views to be used by RecyclerView.
     */
    public TestAdapter(List<TestItem> dataSet) {
        mDataSet = dataSet;
    }

    // BEGIN_INCLUDE(recyclerViewOnCreateViewHolder)
    // Create new views (invoked by the layout manager)
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        // Create a new view.
        View v = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.test_row_item, viewGroup, false);

        return new ViewHolder(v);
    }
    // END_INCLUDE(recyclerViewOnCreateViewHolder)

    // BEGIN_INCLUDE(recyclerViewOnBindViewHolder)
    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
        Log.d(TAG, "Element " + position + " set.");

        // Get element from your dataset at this position and replace the contents of the view
        // with that element
        TestItem testItem = mDataSet.get(position);
        viewHolder.getTimeView().setText(testItem.time);
        viewHolder.getWeatherView().setImageResource(R.mipmap.ic_launcher);
    }
    // END_INCLUDE(recyclerViewOnBindViewHolder)

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataSet.size();
    }
}


ViewHolderパターンを使って、viewを使いまわしています。
あとはビルドをすればRecyclerViewを使った一覧画面が表示されます。

まとめ


Android 6.0 Marshmallow(マシュマロ)のリリースでrecyclerview-v7も23.0.0まであがり、昔のFragmentListを使う理由はないように思いました。

今後アプリを作成、修正する場合はリスト画面にはRecyclerViewを積極的に利用したいですね。

以上

Androidアプリ開発にオススメの本


開発にあると便利なオススメ製品


関連記事 参考サイト

この記事がお役にたちましたらシェアをお願いします

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

2015年8月21日金曜日

Androidアプリ『マウンテンスカウター』をリリースしました

  • 公開日:2015年08月21日

お知らせ


2015年08月21日

『マウンテンスカウター』

というandroidアプリをリリースしました。

このアプリは、気圧標高方位を測定することが可能です。
作成した理由はシンプルで、私が登山時に腕時計等を身に着けるのがあまり好きでないので、欲しい機能をスマホアプリとして実装しました。

Android app on Google Play

機能


気圧と標高(補正なし)

現在位置の気圧を測定します。
単位はhPaです。

また、現在位置の標高を測定します。
単位はmです。この標高は気圧から計算されて、補正はされていません。

標高(GPS)

現在位置の標高を測定します。
単位はmです。この標高はGPSから取得されます。気圧は考慮されません。

方位磁石(コンパス)

方位を調べます。

対応言語


英語、日本語

対応端末


Android version4.0.3以上

今後の予定


バッテリーの消費電力の削減や、より精度の高いデータを取得する実装を予定しています。

まとめ


今後も登山で「あったらいいな」と思っているツールをスマホ化してリリースしていきたいと思います。
評判がよければiphone版も作成する予定です。

以上
よろしくお願いします。

Instagramに登山写真をアップしました。よかったら「いいね!」をお願いします。
Instagram
登山サイトを運営中です。こちらもよろしくお願いします。
山に関する関連記事

この記事がお役にたちましたらシェアをお願いします

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

2015年8月16日日曜日

Androidアプリ開発 位置情報取得のまとめ

  • 公開日:2015年08月16日
  • 最終更新日:2015年08月16日

記事概要


Androidのアプリ開発をする際の、位置情報取得の方法をまとめました。

環境


  • Android Studio 1.3.0
  • Android SDK 22
  • OS X Yosemite

はじめに


位置情報が必要なアプリを作る必要があったので、現段階での位置情報の取得方法を詳細にまとめました。
色々なやり方がありすぎて、みんな混乱してますよgoogleさん。

位置情報取得のpermissionを理解する

1.ACCESS_COARSE_LOCATION



電波塔やWIFIのようなットワークの場所の供給源から、適切な位置にアクセスすることを許可する

Allows an app to access approximate location derived from network location sources such as cell towers and Wi-Fi.
Constant Value: "android.permission.ACCESS_COARSE_LOCATION"

原典

ネットワークを利用するので、インターネットにアクセスできる必要があります。なので、android.permission.INTERNETのパーミッションも設定しないといけません。

2.ACCESS_FINE_LOCATION



GPS, 電波塔, WIFIのようなットワークの場所の供給源から、正確な位置にアクセスすることを許可する

Allows an app to access precise location from location sources such as GPS, cell towers, and Wi-Fi.
Constant Value: "android.permission.ACCESS_FINE_LOCATION"

原典

android.permission.ACCESS_COARSE_LOCATIONで利用できる電波塔, WIFIの他にGPSを使って正確な位置情報を取得できます。当然ネットワークも利用するので、インターネットにアクセスできる必要があります。なので、android.permission.INTERNETのパーミッションも設定しないといけません。

位置情報取得の実装


1. AndroidManifestへの設定


上記を読んでわかるように、AndroidManifest.xmlに設定するのはandroid.permission.ACCESS_COARSE_LOCATIONだけで良いことになります。


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

2. google play serviceを利用する


現在androidで位置情報を取得するには、google play serviceを利用することが推奨されています。
なので、app/build.gradleに以下の記述を記載します。


dependencies {
    compile 'com.google.android.gms:play-services:7.5.0'
}

play-servicesのバージョンは頻繁にあがります。また、バージョンによって実装方法がちょこちょこ変化します。メジャーバージョンアップ(6→7とか)以外は対応しないほうが安全です。

3. Fragmentの実装

ここではFragmentを使って実装します。クラス名PlayLocationFragmentとして実装することで、継承して色々なFragmentで利用可能にします。


public class PlayLocationFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment PlayLocationFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static PlayLocationFragment newInstance(String param1, String param2) {
        PlayLocationFragment fragment = new PlayLocationFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    public PlayLocationFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_play_location, container, false);
    }


}

4. GoogleApiClientの実装

GoogleApiClientを利用して、Googleが提供しているサービスに接続します。
位置情報の実装の場合は、LocationServices.APIを指定します。
また、GoogleApiClient.ConnectionCallbacksとGoogleApiClient.OnConnectionFailedListenerをimplementsする必要があります。接続に成功した場合に、onConnectedメソッドが呼び出され、失敗した場合には、onConnectionFailed()が呼び出されます。


public class PlayLocationFragment extends Fragment implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

    private GoogleApiClient mGoogleApiClient;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }

        buildGoogleApiClient();
    }

    /**
     * Builds a GoogleApiClient. Uses the {@code #addApi} method to request the
     * LocationServices API.
     */
    protected synchronized void buildGoogleApiClient() {
        // Log.i(TAG, "Building GoogleApiClient");
        mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @Override
    public void onConnected(Bundle bundle) {

    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        
    }

}

5. gpsの設定のチェック

端末でgps利用の設定がチェックされていなければ、LocationServices.APIに接続しても意味がありません。
なので、APIに接続にする前に、チェック処理を挟みます。


    private boolean mIsLocationEnabled;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }

        mIsLocationEnabled = isLocationEnabled(getActivity().getApplicationContext();
        if (!mIsLocationEnabled) {
            // TODO gps設定
            return;
        }

        buildGoogleApiClient();

    }

    /**
     * 端末のGPS情報が有効ならtrue, 有効でないならfalse
     * @param context
     * @return
     */
    public boolean isLocationEnabled(Context context) {

        int locationMode = 0;
        String locationProviders;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
            try {
                locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
            } catch (Settings.SettingNotFoundException e) {
                e.printStackTrace();
            }

            return locationMode != Settings.Secure.LOCATION_MODE_OFF;

        }else{
            locationProviders = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
            return !TextUtils.isEmpty(locationProviders);
        }

    }



6. gpsの設定画面

すごく長い記事になりそうなので、ちょこちょこと追記、編集していきます。

Androidアプリ開発にオススメの本

参考サイト

この記事がお役にたちましたらシェアをお願いします

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

2015年8月15日土曜日

谷川岳(たにがわだけ)に行ってきました -西黒尾根ピストン この世で一番危険な山- その3(最終回)

  • 公開日:2015年08月15日
  • 最終更新日:2019年07月15日

登山日時

  • 2015/08/10

記事内容


その2の続きです。

タイムライン(赤字が今回の記事内容)


  • 05:30 自宅を出発(交通手段:電車)
  • 06:30 待ち合わせの駅で友人と合流
  • 09:30 土合駅(どあいえき)に到着(交通手段:車)
  • 10:30 西黒尾根入口を出発
  • 13:30 谷川岳山頂到着
  • 14:00 谷川岳山頂出発
  • 16:30 西黒尾根ルート入口に下山
  • 17:30 谷川岳温泉
  • 19:00 帰宅路
  • 22:00 自宅に到着

谷川岳頂上(オキの耳) - 西黒尾根登山入口


さて、下山です。のぼりでかなりの体力を使っているので、不安を抱きながらのスタートです。
目にはいる光景が見上げる景色から見下ろす景色へと変わります。

我ながらよくのぼったものだと感心します。
いつもの下山と違い、気を使いながら進みます。でないと、

落下して死にます

上記のような景色がずっと続きます。

そして、のぼりに立ち寄らなかった山小屋へ足を運びました。

この山小屋は予約をすれば宿泊も可能です。陳列されたおみやげに目をやると「百名山谷川岳」制覇の記念グッツがずらり。

谷川岳って百名山だったんだ

また、少なくなった水分を補給しようかと思いましたが、¥400だったのでやめました。残りは300ミリリットルくらいでしたが、下りならもつだろうとの判断です。そして、山小屋を出発。

さらに、岩を超え、鎖場を超え、限界森林へ。そして私の足は

ひざかっくん状態

残り15分くらいの森林限界の下山道では、久々に足にきてるなとわかりました。
なるべくダメージを減らすように歩きながら、どうにかこうにか西黒尾根登山入口へ到着。

西黒尾根ピストン制覇

下り時間は2.5時間。休憩を含み往復6時間の道のりでした。
二週間前の日帰り富士山須走ルートは往復8時間でしたが、こちらのほうがはるかに疲労感がありました。
それだけ西黒尾根ピストンのほうがコース難易度が全然高いということでしょう。

谷川温泉


下山後は、下山届を提出し、水分をガッツリと補給してから温泉へ。

登山で滝のような汗をかいた後の温泉は癒されます。ゆっくりと汗を流し、体を休めてから帰路へ。
自宅の到着は22:00。柔軟体操をして布団に倒れこんだら、あっという間に眠りに落ちてしまいました。
当然、翌日はすぐ仕事へ。

まとめ


日本三大急登と言われている谷川岳の西黒尾根ルートは、やはり難易度が高かったです。
この日は谷川岳ロープウェイが運休ということもあり、西黒尾根ピストンになりましたが、下山時はロープウェイを利用したほうがいいかなとも思いました。

私は一般ルートといわれている天神尾根には行ったことがないので、次回来るときはロープウェイとリフトを利用して楽しみたいと思います。

「人食い山」と呼ばれる谷川岳ですが、百名山に選ばれているだけあって素晴らしい山です。
一般ルートを使えば、安全なハイキングコースで楽しめます。なので、谷川岳に行く前は、ロープウェイの運行状況を確認してから行くのが良いと思います。

もちろん根っからの登山好きなら、西黒尾根ルートで満足のいく登山ができるはずです。
都内からの日帰りも十分可能なので、天気の良い休みの選択肢に入れてみてはどうでしょうか。

以上です。

Instagramに登山写真をアップしました。よかったら「いいね!」をお願いします。
Instagram
山に関する関連記事

この記事がお役にたちましたらシェアをお願いします

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

2015年8月14日金曜日

谷川岳(たにがわだけ)に行ってきました -西黒尾根ピストン この世で一番危険な山- その2

  • 公開日:2015年08月14日
  • 最終更新日:2019年07月15日

登山日時

  • 2015/08/10

記事内容


その1の続きです。

タイムライン(赤字が今回の記事内容)


  • 05:30 自宅を出発(交通手段:電車)
  • 06:30 待ち合わせの駅で友人と合流
  • 09:30 土合駅(どあいえき)に到着(交通手段:車)
  • 10:30 西黒尾根入口を出発
  • 13:30 谷川岳山頂到着
  • 14:00 谷川岳山頂出発
  • 16:30 西黒尾根ルート入口に下山
  • 17:30 谷川岳温泉
  • 19:00 帰宅路
  • 22:00 自宅に到着

谷川岳入口(西黒尾根) - ラクダのコル


谷川岳の西黒尾根ルートは日本三大急登と言われている場所です。
それくらい急勾配で有名な場所なのですが、私は最初「にしぐろおね」を「ニシクローネ」と聞き間違えてしまいました。
そして脳内で勝手にクローネという名称をつけるんだから、

金髪のドイツ人美女

がいるような素敵な場所に違いないと想像していました。でも、実際に来てみると以下の写真でした。

スゴイ急斜面です。クローネちゃん、もとい西黒尾根の登山は、いきなり急斜面から始まり、それからもずっと急斜面が続く山道です。

しばらくは限界森林に囲まれた山道を進むので、周囲の景色は全然見えません。おまけに、標高が低くて気温は高く、あぶが飛び回りヘビが横道から出現するというなかなかのハードゲームモードです。

そして、相変わらず私とマジコ@ムンクさんは俺の嫁さんの二人で行く登山の場合は、ビックリするほど好天に恵まれます。この日は恵まれすぎて、汗が目に入るのが止まらず

ムスカ状態

でした。

そして、限界森林を抜けると有名な鎖場へ到着します。

登山じゃなくてクライミングだろ!

と、内心で突っ込みをいれながら、鎖場をのぼりました。
注意しなければならないのは、鎖場は登りと下りの登山者がすれ違うことができないことです。
なので、上と下でお互いに声をかけないと危険です。

また、鎖場はここから4連続で現れます。なので、軍手を持参している場合はここで使用したほうがよいと思います。ただし、滑り止め付きの軍手でないなら、素手のほうが滑らないと思います。
あと、膝を岩場にぶつけやすいので手と足元以外も注意を払ってください。急がず、ゆっくりと進みましょう。
そして、鎖場を抜けたあたりから、絶景が広がってきます。

西欧のアルプスのような美しい景色です。この感動を味わうためにここまで来たといっても過言じゃありません。

とはいえ、ここからがクローネちゃん、もとい西黒尾根ルートの本番です。
また、この時点で当初の予想以上に持参した水分を消費していたので、意識して水分を取るようにしました。前回の富士登山は往復で1.5リッターでしたが、今回はこの時点ですでに1リッターの消費。山頂で食べるためのカップヌードルも持ってきてたので、それを考慮すると残りは800ミリリットルくらいなので若干不足気味です。飲料は重いので、どれくらいの量を用意するかは難しい判断です。

さらに二人で足を進めます。谷川岳は景色がいいので、先が良く見通せるのも特徴です。

セーブポイントが見えないよ!

きっとボス戦の前にセーブポイントが用意してあるに違いない。そうだ、そうに決まっている。……これはマジで

難易度高え!

とはいえ、この辺りからは標高も高くなって、体感温度もちょうどよくなってきたので進行速度はアップ。
さらに進むと有名な『ラクダのコル』を発見しました。

地味です。ただの目印ですね。先を見ると、頂上まではまだまだ続く模様です。

ラクダのコル - 谷川岳頂上(オキの耳)


山道はますます険しくなっていきます。

しばし進むと他ルートとの合流地点へ。

もうこのあたりになると、絶景ばかりで感覚がマヒしてきます。

落ちて死んでもしょうがない

こんな感じで悟りを開きながら進んでいると、山頂が近くに見えてくるようになり、山小屋を発見。

お腹空いてるし華麗にスル—

帰りに立ち寄ればいいんじゃにないかと、山頂を目指して進みました。
そして、とうとう

トマの耳に到着

標高は1,963m。ここは先客がいて場所も狭かったので、一気に最高峰まで進むことにしました。

そして15分後

オキの耳に到着

標高は1,977m。谷川岳の最高峰です。上り時間は3時間でした。でも、普段の登山の3時間の疲れよりも、数倍の疲労感がありました。
到着後は、お昼時だったので食事の用意をしました。マジコ@ムンクさんは俺の嫁さんが持参したジェットボイルを使って湯をわかします。

山で食べるカップ麺ってどうしてこんなに美味しいのでしょうか。空腹と疲労にかなうスパイスはないですね。
腹ごなしを終え、色々と記念撮影をして、下山へ。

次回に続きます。

Instagramに登山写真をアップしました。よかったら「いいね!」をお願いします。
Instagram
山に関する関連記事

この記事がお役にたちましたらシェアをお願いします

このエントリーをはてなブックマークに追加
Related Posts Plugin for WordPress, Blogger...