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系はこの現象が起きるようです。ボタンの上に何かを重ねている人は注意。


仮想マシンのdebianをHyper-VからVirtualBoxに移行したら起動しなかった話

ゲストの環境は32ビットでjessie/sidです。
ホストはWindows 8.1 x64。

Windows 7でVirtual PCを使って動かしていたイメージですが、Windows 8.1に乗り換えるにあたって、Hyper-Vに移行していました。
こっちはそんなに問題なかったのですが、Hyper-Vのネットワーク設定がやりたかったことをすべて満たせない&なぜかゲストでhg pushするとホストのインターフェースが死ぬという現象に見舞われたので、VirtualBoxに移行することにしました。

ちなみに移行はVHDファイルを持ちまわるだけです。

色々問題噴出だったのですが、最終的にはVirtualBoxの設定でどうにかなりました。

エラー内容覚えていないので、検索履歴から箇条書きで。

・VERR_REM_VIRTUAL_CPU_ERROR
・VBOX_E_INVALID_VM_STATE
・fatal error in recompiler cpu 中略 VERR_DEADLOCKのエラーでVirtual Boxが落ちる
・VirtualBox自体は落ちなくなったものの、loading please waitで止まる
・smbus base address uninitialized upgrade bios or use

やったこと。
・設定→システム→マザーボードの「メインメモリー」を512MBから1024MBにした
・設定→システム→マザーボードの「チップセット」をICH9にした
・設定→システム→マザーボードの「I/O APICを有効化」にチェック
・設定→システム→プロセッサーの「PAE/NXを有効化」にチェック
・GRUB2起動時にeを押して編集モードに入り、linuxから始まる行の末尾にsingleを追加してシングルユーザーモードで起動できるか確認
・起動時に画面に表示されない何かの処理が裏で動いているらしいのを、10分以上待った

カーソルが点滅しているなら、気長に待ってみるのも良いかと。

squeezeのときのイメージが残っていたので、そっちも試したのですが、そっちは大丈夫でした。
Hyper-V上でkernelとディストリビューションをアップデートして、686-paeになったのが問題だったかも。

とりあえず復旧できたので良かったです。


スマートに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


PhalconのコマンドラインでError: This command should be invoked inside a Phalcon project directory

phalcon create-modelをしようとして、上記エラーが発生しました。

プロジェクトディレクトリで実行しろということなのですが、プロジェクトディレクトリには既にいるはず…

Error: This command should be invoked inside a Phalcon project directory – Discussion – Phalcon Framework

上記サイトでは.phalconファイルを作成すれば良いという話。が、正解は.phalconディレクトリを作る必要があります。

結局のところ、プロジェクトを自分で作った人ではなく、リポジトリからダウンロードしてきた場合に多く発生します。

というのも、自分のところはMercurialですが、中身が空のディレクトリがリポジトリに登録できないという仕様なので、.phalconディレクトリをリポジトリにコミットし損ねていたということですね。

$ mkdir ./.phalcon
$ touch ./.phalcon/empty

などとして、ファイルを追加してコミットしておけば解決です。


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を実行しておけば、地図が二重に表示されるなどの現象はなくなります。


他のパッケージのaptitude install後にMySQLが起動しない問題

他の環境では多分あまり発生しないかと思いますが、自分の環境では毎回のように発生する上、対処方法をいつも忘れるのでメモ。

環境はdebian squeeze。

# /etc/init.d/mysql start
[FAIL] Starting MySQL database server: mysqld . . . . . . . . . . . . . . failed!

ログも一切出ないので困っていましたが、ファイルのパーミッションの問題でした。

# cd /var/lib/mysql/
(省略)
-rw-rw---- 1 mysql mysql      126 2014-02-27 07:45 mysql-bin.000022
-rw-rw---- 1 mysql root     26620 2014-02-27 07:45 mysql-bin.000023
-rw-rw---- 1 mysql root   1061358 2014-02-27 07:45 mysql-bin.000024
-rw-rw---- 1 root  root     26620 2014-02-27 07:45 mysql-bin.000025
-rw-rw---- 1 root  root   1061358 2014-02-27 07:45 mysql-bin.000026
-rw-rw---- 1 mysql mysql      494 2014-02-27 07:45 mysql-bin.index

mysql-binファイルがroot権限で作られていました。

片っ端からchownして解決です。

# chown mysql:mysql mysql-bin.000023
# chown mysql:mysql mysql-bin.000024
# chown mysql:mysql mysql-bin.000025
# chown mysql:mysql mysql-bin.000026
# /etc/init.d/mysql start
[ ok ] Starting MySQL database server: mysqld ..

根本的な原因は何なんだろうなぁ。


redmineのメール送信エラー

CentOS 5.10にredmine 2.4.2をインストールしていて、Gmail経由でメールが送れない問題があったのでメモ。

wrong argument (NilClass)! (Expected kind of OpenSSL::SSL::SSLContext)

どうやらmailのバージョンが新しくなったところで問題が出たようです。

redmine 2.3.1 / 2.3.2でメール送信時エラーが出た: situの戯れ言

redmineをインストールしたディレクトリに作成されたGemfile.lockファイルのmailのバージョン指定を

    json (1.8.1)
    mail (2.5.4)
      mime-types (~> 1.16)
      treetop (~> 1.4.8)
    metaclass (0.0.2)

    json (1.8.1)
    mail (2.5.3)
      mime-types (~> 1.16)
      treetop (~> 1.4.8)
    metaclass (0.0.2)

と書き換えて、Apacheを再起動したら(Passengerなので)無事にメールが送れるようになりました。

2.5.3が入っていない場合は先にインストールしておく必要があります。

ruby のmail がTLSエラーを吐くので調べたらバージョンだった。 – それマグで!

Ruby 1.8系使ってるとダメみたいです。抜本的な解決のためには1.9以降が必要のようです。


GAE/PythonでWTFormsのDateTimeLocalFieldを使う

HTML5の<input type=”datetime-local”>を使うとローカル時間での日付時刻入力フィールドを作成できます。

WTFormsでこれを使用するにはDateTimeLocalFieldを使えばいいのですが、バリデーションが通らなくて困っていました。
結論としては、フォーマットを指定すれば良いようです。

from wtforms.fields.html5 import DateTimeLocalField

class SampleForm(BaseForm):
    start_at = DateTimeLocalField(u'開始日時*',
        [validators.required()], format='%Y-%m-%dT%H:%M')