Tag Archives: Android

FabProgress(AndroidのカスタムView)をGithubで公開しました

GIF_20141228_204743

こういうやつです。

 

FloatingActionButtonの周りにRing表示をしてProgress状態を表示してくれます。

https://github.com/katsuki-nakatani/FabProgress

 

Quick電話帳がAndroidWearAppとして承認されました

朝起きたらメール来てました。

 

This is a notification that your app, Quick Contact, with package name com.miruker.qcontact, is accepted as an Android Wear app on Google Play.

Your application complies with our guidelines for creating apps for Android Wear. Please refer to the following developer page for details: Wear App Quality – Developer Guidelines

The Google Play Team

 

アプリケーションがガイドラインに準拠しているので承認したよ!ってことだそうです。

DeveloperConsole側でも更新されていました。

スクリーンショット 2014-12-18 7.34.55

さてこれどうなるんでしょうか。

 

GDG京都さんでLTしてきました

AndroidでGeoFencingを利用するときの話です。

 

AndroidでGeoFenceを使って見る from 克紀 中谷

AndroidWearで円形デバイスだけViewが表示されない

Android Wear Appを作る際にレイアウトを円形と四角でレイアウトを変えたい場合があります。
Android Studioでウィザード形式でActivityを作成すると、下記のファイルができるはずです。

今回はactivity_actionという名前で作成しました

すると3ファイル作成されますよね。

activity_action.xml

 <?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.WatchViewStub
 xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/watch_view_stub"
android:layout_width="match_parent" android:layout_height="match_parent"
app:rectLayout="@layout/rect_activity_action" app:roundLayout="@layout/round_activity_action"
tools:context="com.miruker.qcontact.ActionActivity"
tools:deviceIds="wear"></android.support.wearable.view.WatchViewStub>
rect_activity_action.xml
<?xml version="1.0" encoding="utf-8"?>
<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" android:orientation="vertical"
 tools:context="com.miruker.qcontact.ActionActivity" tools:deviceIds="wear_square">

 <android.support.wearable.view.GridViewPager
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:paddingBottom="@dimen/indicatorSize"
 android:visibility="visible"
 android:id="@+id/gridViewPager" />

 <LinearLayout
 android:id="@+id/page_dots_container"
 android:orientation="horizontal"
 android:layout_gravity="bottom|center_horizontal"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"/>

</FrameLayout>
 

round_activity_action.xml
<?xml version="1.0" encoding="utf-8"?>
<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" tools:context="com.miruker.qcontact.ActionActivity"
 tools:deviceIds="wear_round">

 <android.support.wearable.view.GridViewPager
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:visibility="visible"
 android:paddingBottom="40dp"
 android:id="@+id/gridViewPager" />

 <LinearLayout
 android:id="@+id/page_dots_container"
 android:orientation="horizontal"
 android:layout_gravity="bottom|center_horizontal"
 android:layout_marginBottom="28dp"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"/>
</FrameLayout>
でJavaファイルがこちら (一部割愛)

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

 //前の画面からデータをもらっています
 mActionType = getIntent().getIntExtra(Global.PARAM.ACTION_TYPE, -1);
 List<String> values = getIntent().getStringArrayListExtra(Global.PARAM.VALUE_LIST);
 List<String> labels = getIntent().getStringArrayListExtra(Global.PARAM.LABEL_LIST);

//リストデータの構築
 for (int i = 0; i < values.size(); i++) {
 mItems.add(new WearContactItem(0, 0, mActionType, labels.get(i), values.get(i), ""));
 }
 mListView = ButterKnife.findById(this,R.id.gridViewPager);
 mIndicator = ButterKnife.findById(this,R.id.page_dots_container);
 mListView.setOnPageChangeListener(new GridViewPager.OnPageChangeListener() {
 @Override
 public void onPageScrolled(int i, int i2, float v, float v2, int i3, int i4) {

 }

 @Override
 public void onPageSelected(int row, int column) {
 setIndicator(column);
 }

 @Override
 public void onPageScrollStateChanged(int i) {

 }
 });

 //GridViewPagerにアダプターをセット
 ActionListAdapter adapter = new ActionListAdapter(getFragmentManager(), mItems, ActionActivity.this, mActionType);
 mListView.setAdapter(adapter);

 //インジケータViewを作成
 mIndicator.removeAllViews();
 for (int i = 0; i < adapter.getColumnCount(0); i++) {
 ImageView v = new ImageView(getApplicationContext());
 v.setImageResource(R.drawable.black_circle);
 mIndicatorList.add(v);
 mIndicator.addView(v);
 }
 インジケータの初期値
 setIndicator(0);

 mGoogleApiClient = new GoogleApiClient
 .Builder(this)
 .addConnectionCallbacks(this)
 .addApi(Wearable.API)
 .build();

 }
まぁちょっと色々端折っているのですが、単純に画面中央にはGridViewPagerがあります。

その下にはインジケータ用にLinearLayout(Horizontal)のViewがあるような感じですね。

GridViewPagerはFragmntを返すようになっています。

期待する動作としてはそのようなもんで、私の所持している G watchでも正常に表示されていましたがどうやら円形デバイスで表示されないという不具合報告を受けました。

その際に表示されている画面はインジケータは表示されているがそれ以外は何も表示されていない。

要はGridViewPagerのFragmentが表示されていないということなんですね。

StackOverFlowにありました。

 

http://stackoverflow.com/questions/25808229/fragment-in-round-wear-watch-turning-black-in-emulator-watch

英語があんまり読めないので大体の解釈なのですが、RoundDeviceだとInflateする際の動きがSquareと少し違うようで、GridViewPagerで内部で管理しているタグが整合性が合わなくなるみたいなのです。

結局シンプルな解決策として、Stubを使わないようにしました。

VolleyのJsonRequestで411エラーが返却される

VolleyのJsonRequestでAndroid 2.3.xとAndroid 4.x系で動きが違ったのでメモ

Listener<JSONObject> listener , Response.ErrorListener errListener){
		String url = Common.BASE_URL + SEARCH_URL;    	
		CustomJsonRequest request = new CustomJsonRequest(Method.POST ,url,new JSONObject(), listener,errListener);        	
    	request.setShouldCache(false);
    	request.setSequence(0);
    	return request;



CustomJsonRequestは単純にJsonRequestをextendしたクラスです。

JsonRequestを利用する際にJSONObjectをコンストラクタのパラメータで渡すのですが、パラメータなしで実行したい場合、に new JSONObject()の該当箇所をnullで渡すと2系では411のエラーコードが帰ってきます

下記とか調べてContent-Lengthの値が2系では違うのかなぁと勝手に推測して、空のJSONObjectを渡すとオッケーでした。

http://stackoverflow.com/questions/3208861/http-response-411-length-required-http-client-4-0-1-android

Android SupportLibraryのActionBarを利用する

AndroidのサポートライブラリのRev18が公開されました。
それに伴いサポートライブラリでActionBarが使えるようになりました!!!
今まではActionBarをGBやFroyoでサポートするためにはActionBarSherlockなどにお世話になっていましたが
組み込まれましたので早速使い方を書いてみます。

まずActionBarはライブラリのv4ではなくv7に組み込まれています。
(APIレベルv7(2.1)以上で利用可能です)

とりあえずワークスペース内に、v7のappcompatのプロジェクトをインポートします

importimport1

 

ちなみにこの画面ではすでに追加しているためにエラーになっていますが、通常であれば問題なくインポートできるはずです

次に、自分のアプリのプロジェクトにライブラリプロジェクトのリンクとjarのインポートを行います。

import2import2

 

(jerichoは無視してくださいね)

android-suport-v4とv7のappcompatのjarをlibs配下に配置してビルドパスに追加しておきます。

これで準備は完了です。

下記の順番にコードを追加しましょう。

 

AndroidManifest.xml

テーマをActionbarCompatのものを指定します。

    <application
        android:allowBackup="true"
        android:icon="@drawable/icon"
        android:label="@string/applicationname"
        android:theme="@style/Theme.AppCompat.Light" >
/>
(Theme.AppCompat.Light.DarkActionBarなんてのもあるよ)

次にActivityの継承元を変更します。
import android.support.v7.app.ActionBarActivity;

public class BaseActivity extends ActionBarActivity {

}
これでソースもほとんど終わりです。

それではActionBarを呼び出してみましょう
//ActionBar
getSupportActionBar(); //ActionBarのインスタンス取得

//MenuItemなどの追加時の注意
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
//ではなく下記のようにしてください
 MenuItemCompat.setShowAsAction(menuItemInstance, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);
なおActionbarActivityでは、onMenuItemSelectedがOverrideできなくなっています。

NavigationDrawerで利用している場合などはOnMenuIemSelectedを利用していると思うのですが、

onOptionsItemSelectedでイベントを取得できますので処理をこちらに移動してあげてください。

これを実行すると下記のようになります

device-2013-07-25-032407device-2013-07-25-032509

 

ちなみに英語のわかる方ならたぶんこの動画を見れば一発かと。。。
Google+
追記。NavigationDrawerでドロワーをOpenCloseしている際にinvalidateOptionsMenuを読んでいると落ちるので注意(APILevel11からのメソッドなので。。)

Proguardをかけた後の例外をトレースする方法

Proguardをかけた後、例外が上がってきました。
さてどうしましょう。中身はこんな感じになっています

java.lang.IndexOutOfBoundsException: Invalid index 1, size is 0
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
at java.util.ArrayList.get(ArrayList.java:304)
at com.miruker.qcontact.c.ah.a(ProGuard:96)
at com.miruker.qcontact.Widgets.AppWidgetService.b(ProGuard:143)
at com.miruker.qcontact.Widgets.AppWidgetService.a(ProGuard:86)
at com.miruker.qcontact.Activity.WidgetSettingsListActivity.setAppWidget(ProGuard:274)
at com.miruker.qcontact.Components.ag.onItemClick(ProGuard:122)
at android.widget.AdapterView.performItemClick(AdapterView.java:292)
at android.widget.AbsListView.performItemClick(AbsListView.java:1068)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:2525)
at android.widget.AbsListView$1.run(AbsListView.java:3186)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4441)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:823)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:590)
at dalvik.system.NativeStart.main(Native Method)

うーん。さてということでproguardではリトレースするためのツールが用意されています。

SDKやOSの環境によるのですが、とりあえずWindows(64bit)の方法です。

作業フォルダDドライブ

まず2つのファイルを用意します。
1.Eclipseプロジェクトの中に、proguardのフォルダがあるはずです。
  この中のmapping.txtをDドライブにコピーします。

2.次に先ほどのスタックトレースをDドライブにtra.txtとして保存します。

あとはコマンド
retrace.bat -verbose d:\mapping.txt d:\tra.txt
と実行
パスが通っていなければ、\tools\proguard\bin\
この中のretrace.batです。

これで結果が出力されます

java.lang.IndexOutOfBoundsException: Invalid index 1, size is 0
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
at java.util.ArrayList.get(ArrayList.java:304)
at com.miruker.qcontact.Data.WidgetGroupCashData.boolean deleteInsert(android.content.Context,int,java.util.List)(ProGuard:96)
at com.miruker.qcontact.Widgets.AppWidgetService.android.widget.RemoteViews loadWidget(android.content.Context,android.widget.RemoteViews,int)(ProGuard:143)
at com.miruker.qcontact.Widgets.AppWidgetService.android.widget.RemoteViews initialWidget(android.content.Context,android.widget.RemoteViews,int)(ProGuard:86)
at com.miruker.qcontact.Activity.WidgetSettingsListActivity.void setAppWidget(android.view.View)(ProGuard:274)
at com.miruker.qcontact.Components.WidgetSettingsListView$onItemClickListener.void onItemClick(android.widget.AdapterView,android.view.View,int,long)(ProGuard:122)
at android.widget.AdapterView.performItemClick(AdapterView.java:292)
at android.widget.AbsListView.performItemClick(AbsListView.java:1068)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:2525)
at android.widget.AbsListView$1.run(AbsListView.java:3186)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4441)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:823)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:590)
at dalvik.system.NativeStart.main(Native Method)


これでどこがエラーかわかるはずです。

開発メモ/Proguardのすすめ

Proguardって、ソースを暗号化するためということで、無料アプリなら別にソース見られたっていいじゃん!とか考えていませんか?

Proguardってそれだけじゃなくてソースコードの圧縮→apkの容量削減にも役立ちます。
でも一つ間違えたらExceptionとか見えなくなるので設定には注意が必要です。

さて、最新のADTでプロジェクトを作るとproject.propertiesに下記の項目が追加されています

proguard.config=${sdk.dir}\tools\proguard\proguard-android.txt:proguard-project.txt

ここの内容はproguardの設定を読み込むライフを指定しているのですが、まずSDK内のtools\proguard\proguard-android.txtを読みます。
そして追加でプロジェクト内のproguard-projectを読みます。
この中で前者ではActivityの名前を難読化から外したりしてくれていますので細かい設定は不要です。(Activity名変えちゃうとマニフェストと整合性が取れなく落ちてしまいます)

で、そのままでも使えるのですが(外部ライブラリがない場合)、そのままだとExceptionの発生した行数などがすべてUnknown Sourceとなってしまいます。
なので、
プロジェクト内のproguard-project.txtに下記を追加してあげます
-renamesourcefileattribute ProGuard <-ソースをProguard文字列に置換します
-keepattributes SourceFile,LineNumberTable <-ソースファイル、行数属性を変換せず維持します

基本的にはこれだけでOKです。

ただ、外部jarなどのライブラリをロードしている場合、これらが難読化されてしまうと思うように動きません。(※下記はAdmobを例にします)
同様にproguard-project.txtに下記を追加します
-libraryjars \libs\GoogleAdMobAdsSdk-6.1.0.jar <-ライブラリをつかうよー
-keep class com.google.ads.** {*;} <-この名前空間は変換しないでね
-dontwarn com.google.ads.** <-この名前空間は警告を出さないでね

これでOKです。dontwarnはいるのかわからないですが、警告がでてリリースビルドができなかったのでぐぐって追加しました。

あとは、エクスポートしたパッケージでテストをしてみるといいです。

SwitchPreferenceがおかしな件

Android4.0から採用されましたSwitch

スイッチとはこういうのですね



これのPreference版です。

さてこのSwitchを下記のように指定しました


もちろんちゃんと表示されます。

で全く気付かなかったんですが、このSwitchPreferenceを複数設置しているPreferenceActivityにて
画面をスクロールすると。。。。

なぜか、ONにした後、スクロールして戻ってくるとOFFになってたりしました。
(ListViewのgetView()処理でうまくかけていない状態みたいな感じでした)

でも別のアプリではうまく動いていたので差分を見てみるとうまく動いていたアプリはSwitchPreferenceを継承したクラスを使用していたのです。
でも書いているソースはこんな感じ

public class SwitchPreference extends android.preference.SwitchPreference{



	/**
	 *
	 * 設定画面呼び出し時に呼ばれる
	 * @param context
	 * @param attrs
	 */
    public SwitchPreference(Context context, AttributeSet attrs) {
        super(context, attrs);

    }



    /**
     * データバインド時に呼び出される
     */
    @Override
    protected void onBindView(View view) {
       super.onBindView(view);
    }

}


いやなんもしてないですやん・・・

でもこれだと上の事象が解消されるのです。
実際ほかのアプリに対して適用したら解消されてしまいました。

よくわかりません。バグでしょうか?それとも私の指定がおかしいのでしょうか。

ご存知の方教えてください

Switchを利用するときの注意

現在Android 4.0以降はswitchで、それ未満はCheckBoxをと
include等を使用してXMLレイアウトを作成していて実行してみました。

Aアプリ NG
Bアプリ NG
Cアプリ OK

switch関連(switchPreference)も使うと
落ちるんです。

android.widget.Switchの651行目でぬるぽします。

半日ほどハマリました

08-24 16:58:41.155: E/AndroidRuntime(13624): FATAL EXCEPTION: main
08-24 16:58:41.155: E/AndroidRuntime(13624): java.lang.NullPointerException
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.Switch.jumpDrawablesToCurrentState(Switch.java:651)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.ViewGroup.jumpDrawablesToCurrentState(ViewGroup.java:5137)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.View.onAttachedToWindow(View.java:9602)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.View.dispatchAttachedToWindow(View.java:9904)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2301)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.ViewGroup.addViewInner(ViewGroup.java:3483)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.ViewGroup.addViewInLayout(ViewGroup.java:3420)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.ListView.setupChild(ListView.java:1864)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.ListView.makeAndAddView(ListView.java:1818)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.ListView.fillDown(ListView.java:687)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.ListView.fillDown(ListView.java:666)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.ListView.fillFromTop(ListView.java:757)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.ListView.layoutChildren(ListView.java:1668)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.AbsListView.onLayout(AbsListView.java:1873)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.View.layout(View.java:11434)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.ViewGroup.layout(ViewGroup.java:4331)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1666)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1524)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.LinearLayout.onLayout(LinearLayout.java:1429)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.View.layout(View.java:11434)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.ViewGroup.layout(ViewGroup.java:4331)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.FrameLayout.onLayout(FrameLayout.java:443)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.View.layout(View.java:11434)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.ViewGroup.layout(ViewGroup.java:4331)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1666)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1524)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.LinearLayout.onLayout(LinearLayout.java:1429)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.View.layout(View.java:11434)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.ViewGroup.layout(ViewGroup.java:4331)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.widget.FrameLayout.onLayout(FrameLayout.java:443)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.View.layout(View.java:11434)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.ViewGroup.layout(ViewGroup.java:4331)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1489)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2442)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.os.Handler.dispatchMessage(Handler.java:99)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.os.Looper.loop(Looper.java:137)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at android.app.ActivityThread.main(ActivityThread.java:4441)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at java.lang.reflect.Method.invokeNative(Native Method)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at java.lang.reflect.Method.invoke(Method.java:511)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:823)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:590)
08-24 16:58:41.155: E/AndroidRuntime(13624): 	at dalvik.system.NativeStart.main(Native Method)

違いを見つけてみると単純なこと
CアプリだけtargetSDKを指定していました。
A,BはminSDKだけでした。
みなさんきちんとtargetSDKは指定しましょう。

ってかこれってもしかしてAndroid側の不具合なのかな?
もし不具合ならIssueしたいけどやり方がわかりません。。。

1 / 712345...最後 »