2013年10月2日水曜日

support-v7-appcompatを使ってAndroid2系でActionBarを動かす

API level 18 から support-v7-appcompat を使って
古いOSでも ActionBar が使えるようになったと聞いたので、
さっそくやってみたら、エラー、エラー、エラー。
かなり苦労したので、記録に残します。

jar ファイルだけ libs に入れて関連付けてやればいいのかと思ったら、そうでもなかった。
jar ファイルだけだとリソースがないので、プロジェクトで追加しないとダメらしい。


※SDKはアップデートしておきます
※SDKマネージャーもアップデートが必要な場合、
 ライブラリの紐付け方とかいろいろ変わるので、
 既存のプロジェクトがある場合はいろいろ時間と覚悟が必要です


手順

http://developer.android.com/tools/support-library/setup.html#libs-with-res
↑のサイトを参考に support-v7-appcompat プロジェクトを作る。
Eclipseの場合を要約すると、

  1. SDK Manager を使って Android Support library をダウンロードする
  2. 新しいプロジェクトとして support-v7-appcompa をインポートする
    1. File -> Import を選択
    2. Existing Android Code Into Workspace を選択
    3. <sdk>/extras/android/support/v7/appcompat/ を選択
    4. Finish を押してインポートする ←わたしは念のためワークスペースにコピーしました。
    5. 作成した support-v7-appcompa プロジェクトの libs 内の android-support-v4.jar と android-support-v7-appcompat.jar を右クリックし、Build Path -> Add to Build Path を選択 ←このメニューが出てこない場合は追加済みなのでやらなくてよさそう
    6. プロジェクトを右クリックし、Build Path -> Configure Build Path を選択
    7. Order and Export タブで、今追加した v4 と v7 にチェックを入れる
    8. Android Dependencies のチェックは外す
    9. OKを押して、完了!
次に、自分で作りたいアプリのプロジェクトに v7 を設定する。

  1. 自分のアプリのプロジェクトを右クリックし、Properties を選択
  2. Android -> Library の Add をクリック
  3. さっき作った support-v7-appcompat を選択し、OKを押す
  4. 設定画面をOKで閉じる
と、ドキュメントに書いてある手順はここまで。
このあとビルドしたら、エラーになりました。

Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.

どうやら、AndroidManifest に書いてあるテーマの変更が必要なようです。

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat" >
        <activity
             ・・・>
        </activity>
</application>

もう1つ、v4 がかぶってるよ的なエラーがでたので、自分のアプリのプロジェクトから
android-support-v4.jar を削除した。

これでビルドしてみたら、うまくいった!!!

2013年8月26日月曜日

Android 超高解像度端末一覧。(2013.8.26現在)

2013年8月26日現在の、解像度1080x1920のいわゆる「超高解像度端末」の一覧。

xxhdpiに分類されるこのとてもキレイな画面でアプリを開くと、
「画像が小さくなりすぎて見えない!!」「なんか表示がおかしい!ボタンがない!!」
みたいな現象がよく起こります。

大きめの画像を用意してやれば解決する話なんだとは思うけど、
まだ試せてないので解決策がまとまったらまた追加で記事に残します。


これからどんどん増えてくると思うので、無視してはいられない。


■docomo
ELUGA X P-02E Android4.1
ELUGA P P-03E Android4.2
ARROWS X F-02E Android4.1
ARROWS NX F-06E Android4.2
AQUOS PHONE ZETA SH-06E Android4.2
GALAXY S4 SC-04E Android4.2
Optimus G pro L-04E Android4.1

Xperia Z SO-02E Android4.1

AQUOS PAD SH-08E  (1200×1920 タブレット)

■au
HTC J butterfly HTL21 Android4.1

HTC J One HTL22 Android4.1
Xperia UL SOL22 Android4.1


■SoftBank
AQUOS PHONE Xx 206SH Android4.2
ARROWS A 202F Android4.2


■その他

新型Nexus 7  (1200×1920 タブレット)

2013年6月26日水曜日

Android アプリのハッシュキーの確認方法

■Windowsの場合
コマンドプロンプトで

keytool -exportcert -alias xxx -keystore /keystoreまでのパス/xxx.keystore | openssl sha1 -binary | openssl base64

を実行。

キーストアのパスワードを聞かれるので、用意しておくこと。

2013年5月24日金曜日

Google Maps V2 API でNoClassDefFoundErrorが出た。

java.lang.NoClassDefFoundError: com.google.android.gms.R$string
at com.google.android.gms.common.GooglePlayServicesUtil.c(Unknown Source)
at com.google.android.gms.internal.e.a(Unknown Source)
at com.google.android.gms.internal.e.onCreateView(Unknown Source)
...


Androidでこのエラーが出ていた場合、「Google Play開発者サービス」アプリがアンインストールされている可能性が考えられます。

このアプリはgoogleが勝手にインストールするアプリで、アンインストールしてもしてもしても一定時間で生き返ります。
現在発売されている端末には最初からインストールされているのですが、2012年10月頃以前の端末には説明が不十分なままアップデートで追加され、しかも容量が大きかったためにユーザーから大批判を買い、アンインストールするユーザーが続出しているようです。

もちろん、一定時間が経てばまたインストールされるのですが、Androidユーザーは「とにかく不必要なアプリは消したい」という思いが強いので、こまめにチェックしてはアンインストールしているようです。
今まで目立った不具合がなかったようなのですが、今回Google Map V2が使えないことが分かりました。

さらにこのアプリ、Google Play Storeから検索してもヒットしないんですね。
Google検索にはヒットするのでそこから入れなおすか、自動更新されるのを待つしかないので、ダウンロードの導線を付けてあげたほうが良いです。
(ちなみにfoursquareは、このアプリが入っていないとアラートを表示してダウンロードを促しています。)

今回はGoogle Maps V2 APIを利用しているところでこのエラーが発生しましたが、
今後GooglePlayServicesUtil.cを参照するプログラムで問題になってくると思います。

2013年5月22日水曜日

phpと外部js間で値を渡そうとして詰まったお話・・

phpでhtmlサイトを作っているとき、ふとphpの値をjsでも使いたいな・・・と思った。
jsは外部ファイルから読み込んでいる。

ぐぐってみた結果、php→javascriptの値渡しは簡単にできそうなことが分かった。

$a = 100;

<script type="text/javascript">
  var a = <?php echo $a; ?>;
</script>

しかし、全く動かない。
ていうか、値取れてないし!!




調べた結果、これには【 同じソース内に書かれているという大前提】があった。
つまり、外部javascriptを読み込んでいる場合はダメ。


よく考えてみたら確かに、phpでhtmlを出力してる時点でhtmlなわけなんだけど、
ぐぐって出てきたら「そんなことできるの!?」って思っちゃうじゃないですか。


こんな大前提なこと、書いてるサイトもひっかからないし・・・。
ということで、メモ。

2013年5月8日水曜日

Androidの位置情報取得 - 1 (現在地取得)

■端末の設定から現在地取得が利用可能か確認する

    LocationManager mLocationManager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
    // 3Gまたはwifiから位置情報を取得する設定
    boolean networkFlg =  mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    // GPSから位置情報を取得する設定
    boolean gpsFlg = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

■現在地を取得する
  LocationListenerをimplementsする。
  LocationListenerの必須メソッドは以下の4つ。
  ・public void onLocationChanged(Location location)
  ・public void onProviderDisabled(String provider)
  ・public void onProviderEnabled(String provider)
  ・public void onStatusChanged(String provider, int status, Bundle extras)

  位置情報を取得したら onLocationChanged が呼ばれ、引数に取得した位置情報が入る。
  ※位置情報取得に失敗したことを検知するメソッドはない。

  requestLocationUpdates()で位置情報取得を開始する。
  removeUpdates()で取得処理を終了する。
  終了するまで、不定期で取得し続ける。
  
  ※removeUpdates()を忘れると、しばらくのあいだonLocationChangedが
   呼ばれなくなることがあるらしいので、onPause()などに書いて
   必ず呼ばれるようにしておくこと。
  参考:http://d.hatena.ne.jp/glass-_-onion/20101113/1289615195

  requestLocationUpdates()には引数の種類がいくつかあるけど、今回はこれを利用。
  mLocationManager.requestLocationUpdates(provider, minTime, minDistance, listener);
  provider:network または gps  ← providerの選び方は次の記事で
  minTime:位置情報を取得する最小時間
  minDistance:位置情報を取得する最小距離
  listener:リスナーを実装しているクラス

  ※minTime, minDistanceはそれぞれ取得する最小時間、最小距離なので、必ずこの間隔で取得されるとは限らない

  位置情報の取得を1回しか行わない場合は、onLocationChanged()でremoveUpdates()を呼べばいい。


  AsyncTaskのdoInBackgroundではrequestUpdatesが書けない。


以下、サンプルコード。
ネットワークとGPSで位置情報を取りに行って、両方取得したら処理を終了する。
onLocationChangedはnetworkとgps、それぞれ取得出来たら1回ずつ呼ばれる。
(重要なとこだけ。これだけでは動かない。)

public class LocationBaseFragment implements LocationListener {

public LocationManager mLocationManager;
/** 利用できる現在地プロバイダの数 */
private int providerNum = 0;


@Override
public void onResume() {
super.onResume();
// 利用できるプロバイダをカンマ区切りで取得
String gpsStatus = android.provider.Settings.Secure.getString(getActivity().getContentResolver(), Secure.LOCATION_PROVIDERS_ALLOWED);
if (gpsStatus.matches(".*" + LocationManager.NETWORK_PROVIDER + ".*")) {
providerNum++;
// ネットワークから取得を開始する
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000, 10, this);
}
if (gpsStatus.matches(".*" + LocationManager.GPS_PROVIDER + ".*")) {
providerNum++;
// GPSから取得を開始する
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 10, this);
}
}


@Override
public void onPause() {
super.onPause();
if (mLocationManager != null) {
mLocationManager.removeUpdates(this);
}
}

@Override
public void onLocationChanged(Location location) {

// 全てのプロバイダからの現在地を取得したら取得処理をやめる
providerNum--;
if (providerNum == 0) {
mLocationManager.removeUpdates(this);
}
/* 取得したlocationを好きに使う */
}

@Override
public void onProviderDisabled(String provider) {
}

@Override
public void onProviderEnabled(String provider) {
}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
}

2013年3月14日木曜日

TextViewに表示させようとして落ちる

すごく単純なところなのに詰まったのでメモ。
TextViewに表示させようとして、以下のエラーで落ちた。

android.content.res.Resources$NotFoundException: String resource ID #0x5dc

TextViewに設定しているidの名前も間違ってないし、クリーンもしたし、
eclipseの再起動もしたしなんで???と思っていたら、

原因は「int型をセットしていた」ことだった。


ちょっと疑問には思ったけど、コンパイルエラーにならないからいいやと思っていたらダメだった。

かなり残念すぎるバグ・・・。

2013年3月12日火曜日

ListViewの中にImageButtonを使うとonItemClickが呼ばれなくなる?

ListViewをカスタマイズして実装しているときに、
リスト1つ1つの中にImageButtonがあると、
リスト全体のクリックイベントも、ImageButtonのクリックイベントも拾ってくれないらしい。

解決策としては、
1. xmlのImageButtonに
android:focusableInTouchMode="false"
android:focusable="false"
を追加する

または

javaで以下を設定する

((ImageButton)v.findViewById(R.id.recommend_checkin)).setFocusableInTouchMode(false);
((ImageButton)v.findViewById(R.id.recommend_checkin)).setFocusable(false);

2. ***_item.xml(リストの中身のレイアウト)の一番外に
android:descendantFocusability="blocksDescendants"
を追加する

例)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:descendantFocusability="blocksDescendants"
    android:gravity="center" >

<ImageView
        android:id="@+id/recommend_product_image"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:background="#e5fff7"
        android:layout_weight="1" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_margin="3dp"
        android:gravity="left|center_vertical"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/recommend_product_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/recommend_pickup_comment"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

</LinearLayout>


これで、全体のクリックイベントを拾ってくれるようになる。


さらに、ImageButtonのクリックイベントを拾うためには、
adapterの中でonClickイベントを拾ってやればいいらしい。

参考URL
http://tomstay.hatenablog.jp/entry/20110421/1303391426
http://blog.babukuma.com/2010/01/android-listviewbutton.html

2013年3月8日金曜日

mysqlで、任意の順番で取得したい場合


ex.)
type が1, 5, 4, 2, 3の順番で取得する場合

ORDER BY FIELD(type, 1,5,4,2,3)


2013年2月27日水曜日

ScrollViewのメモ

Androidのレイアウトでスクロールビューを使うとき

上部にメニューなどを固定し、下側の要素だけスクロールさせたい場合。

ScrollView の
layout_height = "wrap_content" 
layout_weight="1" 

他の要素にはlayout_weight 入れない