‘Android’ カテゴリーのアーカイブ

Android 5.0 LollipopでImageViewがButtonの下に隠れる

4系では問題なく表示されていたToggleButtonにかぶせたImageViewが、Lollipopで表示されないという問題がありました。

image.png
4系

image.png
Lollipop

ちなみにレイアウト構成はこんな感じ。FrameLayoutで単純に2枚のViewを重ねているだけです。

image.png

ImageViewの背景色を変更して確認してみたところ、Viewの重なり順(Z-Order)が逆になってしまっていて、ToggleButtonの下にImageViewが入ってしまっているようでした。

マテリアルデザインの影響で、ボタンは影が入るようになったため、前面に移動してしまっているようです。

Android – Buttonのelevationを設定する方法 – Qiita

で、奥行きを修正するために、ImageViewの方をtranslationZで2単位ほど手前に出せば良いらしい。

Android 5.0 – ProgressBar cannot be displayed over a Button – Stack Overflow

<ImageView
android:id=”@+id/image”
android:layout_width=”match_parent”
android:layout_height=”match_parent”
android:contentDescription=”@null”
android:scaleType=”fitCenter”
android:translationZ=”2dp”
tools:ignore=”UnusedAttribute” />

で、解決しました。

あ、translationZはAPI Level 21からのアトリビュートなので、プロジェクトの方も設定が必要です。

image.png

試してませんが、stateListAnimatorでやる方が正しいかもしれません。

あと、ToggleButtonに限らず、Button系はこの現象が起きるようです。ボタンの上に何かを重ねている人は注意。


スマートにThis tag and its children can be replaced by one <TextView/> and a compound drawableを消す方法

Androidのレイアウトを編集していると、

This tag and its children can be replaced by one <TextView/> and a compound drawable

という警告が出ることがあります。

これはImageViewとTextViewを並べて配置したときに、それはひとまとめにできるからTextViewとCompound drawableを使いなさいよというメッセージです。

実際、ひとまとめにできるなら良いのですが、ImageViewの縮小機能を使いたいなど、別にしておく必要があることが多々あります。

というわけで、警告を無視する設定を追加します。

レイアウトXMLのルート要素に

xmlns:tools="http://schemas.android.com/tools"

があることを確認します。これはtools namespaceの定義です。古いプロジェクトにはないことが多いので、なければ追加してください。

で、ImageViewとTextViewを覆うLinearLayoutに

tools:ignore="UseCompoundDrawables"

属性を追加します。

全体はこんな感じです。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:orientation="vertical"
        tools:ignore="UseCompoundDrawables" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:contentDescription="@null"
            android:src="@drawable/ic_launcher" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="@string/app_name"
            android:textSize="18sp" />
    </LinearLayout>

</LinearLayout>

tools:ignore属性をつけると、Lintチェックをその要素だけ回避できます。ルート要素に付ければ、そのXML全体で効きます。

警告メッセージからのLint名は、以下のLint一覧から探してください。

Android Lint Checks – Android Tools Project Site


AnimationDrawableが最後のフレームしか表示されない場合

AndroidのAnimationDrawableを再生するには

        ImageView imageView = (ImageView) findViewById(R.id.imageView1);
        imageView.setBackgroundResource(R.drawable.animation1);
        AnimationDrawable animation = (AnimationDrawable) imageView.getBackground();
        animation.setOneShot(true); // 一度だけ再生
        animation.start();

こんなコードで実行しますが、もう一度再生しようとstart()を実行すると、最後の1フレームしか再生されません。

        ImageView imageView = (ImageView) findViewById(R.id.imageView1);
        imageView.setBackgroundResource(R.drawable.animation1);
        AnimationDrawable animation = (AnimationDrawable) imageView.getBackground();
        animation.stop(); // 念のため一度ストップ
        animation.selectDrawable(0); // 0フレーム目に戻す
        animation.setOneShot(true);
        animation.start();

とすれば最初から再生できます。

DrawableContainer | Android Developers

selectDrawableってドキュメントに何の説明も書いてないんですね。


Volleyが勝手にリダイレクトするのを禁止する方法

サーバから302が返ってきたときに、Volleyが自動的にリダイレクトするのですが、開発用のAPIなどでBasic認証ヘッダを追加している場合など、リダイレクト先にも認証ヘッダを付けたままリクエストしてしまいます。

これが原因でAWS S3からデータが取れないということがありました。

とりあえず勝手にリダイレクトしないようになれば良いので、HttpURLConnectionのsetInstanceFollowRedirectsをfalseにすることで対応しました。

public class MyHurlStack extends HurlStack {

    @Override
    protected HttpURLConnection createConnection(URL url) throws IOException {
        HttpURLConnection connection = super.createConnection(url);

        // 自動リダイレクトを禁止する
        connection.setInstanceFollowRedirects(false);
        connection.setRequestProperty("Accept-Encoding", "");

        return connection;
    }

    @Override
    public HttpResponse performRequest(Request<?> request,
            Map<String, String> additionalHeaders) throws IOException,
            AuthFailureError {
        URL url = new URL(request.getUrl());
        if (url.getHost().equals("開発環境のホスト名")) {
            // Basic認証ヘッダの追加
            additionalHeaders
                    .put("Authorization", "Basic ********************");
        }

        return super.performRequest(request, additionalHeaders);
    }

}

setInstanceFollowRedirectsは

HTTP リダイレクト (応答コード 3xx の要求) を、この HttpURLConnection インスタンスで自動に従うかどうかを設定します。

とのこと。

HttpURLConnection (Java Platform SE 6)


FragmentTabHost+SupportMapFragmentで画面が真っ白になる

Android Support Library v4を使っていますが、FragmentTabHostのタブのひとつにSupportMapFragmentを継承したFragmentを配置、その地図上のマーカーから同じタブ内に詳細情報表示用のFragmentを表示させるようにしていました。

詳細情報表示用のFragmentを開いたまま、他のタブに行く際に、Fragmentスタックを全部クリアしてから移動するのですが、そうするとこのタブに戻ってきたときにSupportMapFragmentがまったく反応しなくなってしまいます。

対策としては、一度detach/removeしておいて、再度SupportMapFragmentのインスタンス化をして、Transactionにaddという流れでした。

    @Override
    public void onTabChanged(String tabId) {
        FragmentManager fm = getSupportFragmentManager();
        // 移動前に既存のFragmentスタックをクリアする
        for (int i = 0; i < fm.getBackStackEntryCount(); ++i) {
            fm.popBackStack();
        }

        // 地図タブから移動する場合は、再アクティブ化のために一度Fragmentをデタッチする
        if (lastTabId != null && lastTabId.equals("map_section")) {
            MapSectionFragment fragment = (MapSectionFragment) fm
                    .findFragmentByTag(lastTabId);
            if (fragment != null) {
                FragmentTransaction ft = fm.beginTransaction();
                ft.detach(fragment);
                ft.remove(fragment);
                ft.commit();
            }
        }
        // 地図タブを開くときは、removeされていれば再インスタンス化する
        if (tabId.equals("map_section")) {
            // FragmentTabHost.onTabChangedでcommitされたスタックを先に反映させないとfindできない
            fm.executePendingTransactions();

            MapSectionFragment fragment = (MapSectionFragment) fm
                    .findFragmentByTag(tabId);
            FragmentTransaction ft = fm.beginTransaction();
            if (fragment == null) {
                // インスタンス化
                fragment = (MapSectionFragment) Fragment.instantiate(this,
                        MapSectionFragment.class.getName(), null);
                // Fragmentを追加
                ft.add(R.id.content, fragment, "map_section");
            }
            ft.commit();
        }

        // 最後に開いたタブのタグを保持する
        lastTabId = tabId;
    }

今回キモだったのはexecutePendingTransactions()です。

v4/java/android/support/v4/app/FragmentTabHost.java – platform/frameworks/support – Git at Google

FragmentTabHostのソースを見ると、doTabChangedメソッドで同様の処理を実行してFragmentのインスタンス化、追加を実行、コミットしているにも関わらず、findFragmentByTagがnullを返す現象が発生しました。

これはコミットが実際にはすぐには実行されず、メインスレッドにスケジューリングされるためだそうです。

android – Can’t find fragment by tag – Stack Overflow

ドキュメントに書いてありました。

FragmentTransaction | Android Developers

executePendingTransactionsを実行しておけば、地図が二重に表示されるなどの現象はなくなります。


ADT Translation Manager Pluginを一瞬でアンインストールした話

Eclipse Keplerで、ADT Translation Manager Pluginをインストールしたら、Eclipseの設定が消えたようになってworkspace含め初期状態で起動するようになってしまいました。

Installing the Eclipse Plugin | Android Developers

結局根本的な解決には至りませんでしたが、上記プラグインをアンインストールしたら復旧したので解決です。


ADT22でのMultiple dex files defineの解決方法

ADT22にして既存のプロジェクトを実行してみようと思ったら「Multiple dex files define」というエラーがコンソールに出て、実行できない状態になりました。

[2013-05-24 17:07:13 - Dex Loader] Unable to execute dex: Multiple dex files define Loauth/signpost/commonshttp/CommonsHttpOAuthConsumer;
[2013-05-24 17:07:13 - ***] Conversion to Dalvik format failed: Unable to execute dex: Multiple dex files define Loauth/signpost/commonshttp/CommonsHttpOAuthConsumer;

これは同じクラス名を持つ複数のjarがAPKに含まれている場合に出るエラーです。
ADT22でプロジェクトにAndroid Private Librariesというフォルダができていて、apkにexportするファイルが被るから、という理由のようです。

解決方法は、Eclipseのプロジェクトを右クリックして、「Build Path」→「Configure Build Path」→「Order and Export」タブで、「Android Private Libraries」にチェックが入っていることを確認して、またlibs以下のファイルにチェックが「入っていないこと」を確認します。
このエラーが出たときは、signpostのライブラリにチェックが入っていたので、外しました。


apple-itunes-appと同等のものをAndroidで実現する

iOS 6のSafariではapple-itunes-appというメタ要素をHTMLのヘッダに指定することで、ブラウザ上部にアプリへのリンクを表示させることができます。

これをAndroidでも簡単にGoogle Play Storeへのリンクを表示できるJSがありました。

jQuery Smart Banner – Jasny · web development

jQueryのライブラリとして公開されています。

    <meta name="apple-itunes-app" content="app-id=544007664">
    <meta name="google-play-app" content="app-id=com.google.android.youtube">

こんな形で設定できるようです。



[dx] Could not reserve enough space for object heap

PHPからAndroidのapkをantでビルドする環境を作成中、ヒープ不足でVMが起動しない問題に遭遇しました。

-dex:
      [dex] input: /path1/to/bin/classes
      [dex] input: /path2/to/bin/classes.jar
      [dex] input: /path/to/android-sdk-linux/tools/support/annotations.jar
      [dex] input: /path2/to/libs/android-support-v4.jar
      [dex] Pre-Dexing /path2/to/bin/classes.jar -> classes-df6cdfb7c6be3c83700a640e18a54033.jar
       [dx] Error occurred during initialization of VM
       [dx] Could not reserve enough space for object heap
       [dx] Error: Could not create the Java Virtual Machine.
       [dx] Error: A fatal exception has occurred. Program will exit.

解決策は android-sdk-linux/platform-tools/dx の
defaultMx=”-Xmx1024M”
を小さくすればOKです。

今回は
defaultMx=”-Xmx128M”
としました。

dxコマンドを直接実行する場合は、コマンドに
-JXmx128M
とオプションを付ければ大丈夫なようですが、antの場合はできないっぽいです。

あと、ANT_OPTSはantの中で実行されるjavaコマンドについては引き継がないようです。