FloatingActionButtonをスクロール中に消す

NestedScrollViewを下にスクロールしている間は, FloatingActionButtonを表示させないようにする. 少し変えれば, RecyclerView等の他のScrollViewにも使えそう(未確認). 下に実際の挙動を示した.



目次


方法

  1. 縦スクロールする場合に対してのみ有効にする.
  2. 現在のFloatingActionButtonのアニメーション状態を4つ, スクロールの状態を5つで管理する.
  3. スクロール時のアニメーションを設定する
  4. 停止時のアニメーションを設定する
  5. FloatingActionButtonのbehaviorを設定する

コード全文

参考にしたサイト


方法


  1. 縦スクロールする場合に対してのみ有効にする.


  2. 現在のFloatingActionButtonのアニメーション状態を4つ, スクロールの状態を5つで管理する.

  3. FloatingActionButtonが消えている間は, 押しても反応しないようにする. ここのアニメーションはListenerに, AnimatorListenerAdapterAnimator.AnimatorListenerのインスタンスを代入できればよく, もっと複雑にできるかもしれない. また, アニメーションの時間はそこまで長くしないほうが良い.

    現在のスクロール状態(scrollingState)と, (状態が変わる)1つ前のスクロール状態(preScrollingState)を保持しておく. 実際の状態管理については, 最後のコード全文を参照

  4. スクロール時のアニメーションを設定する
  5. 下にスクロールまたは, フリック(以降, スクロールに含める)したときに, FloatingActionButtonを消す. また, FloatingActionButtonが消失している場合, 上にスクロールしたとき再表示する. 4行目から7行目, 23行目から26行目では, 一番下までスクロールしたFloatingActionButtonが消えないようにしている(下のコードでは, 見やすさのためにスクロール状態の管理を省いている).

  6. 停止時のアニメーションを設定する
  7. FloatingActionButtonが消失している場合, 放置していると再表示する.
    上のコードで, 9行目から13行目までは, 以下のような挙動を抑えるために記述している.


    すなわち, スクロール(フリックは含めない)後, フリックすると, イベントとしては, onNestedPreScroll -> onStopNestedScroll -> onNestedPreFlingScroll (コードに関係しないイベントは除く)と, onStopNestedScrollが途中で呼ばれてしまい, 何もしないとここで, 一回FloatingActionButtonが消える.

    また, 15行目から22行目では, 200(=duration)ms未満の下スクロールを行うと, onStopNestedScrollが呼ばれたときに, stateSTATE.VANISHINGのままで, 該当箇所を抜くと, FloatingActionButtonが消えたままとなる(下のgif参照)ので, これを回避するために, そうなったときに, 400(=delayDuration)ms後, FloatingActionButtonを再表示させている.

    放置しているときに, FloatingActionButtonを再表示させたくない場合は, onStopNestedScrollをオーバーライドしなければよい.

  8. FloatingActionButtonのbehaviorを設定する
  9. MainActivityOnCreateで次のようにすればよい.


コード全文


コード全文を掲載する

VanishFloatingActionButtonBehavior.java

MainActivity.java


参考にしたサイト




NestedScrollViewで最下部にスクロールしたかを判定する

まず, NestedScrollViewのサイズを取得する方法 :

よって, これとNestedScrollView.getScrollY()を比較すればよい:


参考にしたサイト



Toolbarの挙動をコードで変える

例えば, 最初の画面ではToolbarを動かさないようにしたいが,次の画面ではToolbarをcollapseさせたい時の方法

方法


android.support.design.widget.CollapsingToolbarLayoutapp:layout_scrollFlags属性をJavaコード中で変更する.
実際には、以下のように行う.
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout;

...

CollapsingToolbarLayout collapsingToolbarLayout = findViewById(R.id.collapsingToolbarLayout);
AppBarLayout.LayoutParams appBarLayoutParams = (AppBarLayout.LayoutParams)collapsingToolbarLayout.getLayoutParams();
appBarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
toolbarLayout.setLayoutParams(appBarLayoutParams);

setScrollFlagsメソッドに代入する定数は以下の通りで, 複数指定したい場合は, 上のコードのようにビット演算子OR|をはさんでやる.

定数 対応するapp:layout_scrollFlagsの値
SCROLL_FLAG_ENTER_ALWAYS enterAlways
SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED enterAlwaysCollapsed
SCROLL_FLAG_SCROLL scroll
SCROLL_FLAG_EXIT_UNTIL_COLLAPSED exitUntilCollapsed
SCROLL_FLAG_SNAP snap

上の定数は, android.support.design.widget.AppBarLayout.LayoutParamsのフィールドであるが, ToolBarの挙動については, こちらが詳しい.

実装例


FloatingActionButtonを押すことで, Toolbarの挙動をenterAlways <-> scroll|exitUntilCollapsedと相互に変える. 以下のような感じ.

styles.xml

ToolBarをつかうので, アプリケーションテーマとしてNoActionBarを採用している.
また, 11行目から13行目, 15行目から18行目は, ToolBarのタイトル文字がデフォルトでは黒色なので, 見やすい白色にするために追加している(後に, activity_main.xmlのCollaspingToolbarLayoutapp:collapsedTitleTextAppearance, app:expandedTitleTextAppearanceで指定してやる).

activity_main.xml

styles.xmlで述べたが, 17行目と19行目でToolBarのタイトルのテキストスタイルを変更している
54行目に出てくる, FloatingActionButtonに使うdrawableリソースはここには載せないが, 適当な画像を使えばよい.

MainActivity.java

ハイライトしたところで, Toolbarの挙動を変更している.
基本的に, statusで現在の状態を管理している. 詳しい説明は, コード中のコメントを参照.

実装例の最初に挙げたgifを見て気づいた方もいるかもしれないが, このコードの問題として, AppBarLayouheightwrap_contentか, あまり大きくない値を指定するとタイトルが表示されないことが挙げられる(Toolbarapp:titleを設定してないことは関係ない!). この問題は, CollapsingToolbarLayoutを使わないようにすれば回避できる:

しかし, この方法では, Toolbarの挙動をJavaコード中では変えられないっぽく, 自作のカスタムViewを作る等面倒なことをしないといけないみたい(? 現在模索中)

参考にしたサイト




VisibilityAwareImageButton.setVisibility can only be called from within the same library group

sdkのバージョンを28 beta1に変えてみると、android.support.design.widget.FloatingActionButton.setVisiblityメソッドを使っていたときに次のようなエラーが出た.
VisibilityAwareImageButton.setVisibility can only be called from within the same library group (groupId=com.android.support)

build.gradle(Module: app)でこのエラーに関すると思われるものは以下:
apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        ...
        targetSdkVersion 28
        ...
    }
    ...
}

dependencies {
    implementation 'com.android.support:design:28.0.0-beta01'
    ...
}

雑に言えば、FloatingActionButtonvisibilityを直接変えてはいけないようである. setVisiblityの代わりにshowメソッドかhideメソッドを使えば、エラーを回避できる.
floatingActionButton.show();
floatingActionButton.hide();

ただし、この場合、FloationActionButtonvisibilityinvisibleにすることは工夫をしないとできないようである.

参考にしたサイト



Perl 6で現在時刻を取得する、ついでにDateTimeの使い方も


DateTimeクラスのnowメソッドを使う.
my $time = DateTime.now;


処理時間を計測する


次のようにすると、任意の処理にかかる時間を計測できる(単位は秒、結果の型はDurationになる. 詳しくは、infix:<->へ).

my $start = DateTime.now;
...何かの処理...
my $end = DateTime.now;
say $start - $end;

時間を計測するだけなら、DateTimeは書かなくてもよい(正確には、termのnowを使っている. この場合は、両方ともDateTimeを書かない.):

my $start = now;
...何かの処理...
my $end = now;
say $start - $end;


DateTimeの使い方


DateTimeのメソッドと関連する関数を可能な限りまとめてみる.

目次


now メソッド
clone メソッド
hh-mm-ss、hour、minute、second メソッド
whole-second メソッド
timezone、offset メソッド
offset-in-minutes、offset-in-hours メソッド
Instance、Date、DateTime メソッド
posix メソッド
later、earlier メソッド
truncated-to メソッド
utc メソッド
in-timezone メソッド
local メソッド
infix:<-> 関数
infix:<+> 関数
Dateish ロール


now メソッド


定義 :
method now(:$timezone = $*TZ, :&formatter --> DateTime:D)

指定したタイムゾーン、指定した形式の現在時刻を返す.
タイムゾーンは、GMT(グリニッジ標準時)からのずれを秒数により指定する.デフォルトでは、$*TZ変数の値になる.

 現在時刻を表示する :
say DateTime.now;
実行結果 :
2018-05-26T17:09:47.148866+09:00


clone メソッド


定義 :
method clone(:$year, :$month, :$day, :$hour, :$minute, :$second, :$timezone, :&formatter)

既存のDateTimeオブジェクトで所定の属性を変更した新しいオブジェクトを返す.

 年を2015年に、月を1月に変える:
say DateTime.new("2018-04-30T17:37:12+09:00").clone(year => 2015, month => 1);
実行結果:
2015-01-30T17:37:12+09:00


hh-mm-ss、hour、minute、second メソッド


定義 :
method hh-mm-ss(DateTime:D: --> Str:D)
method hour(DateTime:D: --> Int:D)
method minute(DateTime:D: --> Int:D)
method second(DateTime:D:)

それぞれ、時:分:秒、時間、分、秒を返す.

 時:分:秒だけを表示する:
say DateTime.new("2018-04-30T17:37:12+09:00").hh-mm-ss;
実行結果:
17:37:12


whole-second メソッド


定義 :
method whole-second(DateTime:D:)

秒を返すが、その結果はIntに丸め込む(切り下げ).

 秒数を表示する:
say DateTime.new("2018-04-30T17:37:12.999+09:00").whole-second;
実行結果:
12 #文字が少なすぎて表示されない


timezone、offset メソッド


定義 :
method timezone(DateTime:D: --> Int:D)
method offset(DateTime:D: --> Int:D)

UTC(協定世界時)からのずれを秒数で取得する.

 タイムゾーンを表示する:
say DateTime.new("2018-04-30T17:37:12-09:00").timezone;
実行結果:
-32400


offset-in-minutes、offset-in-hours メソッド


定義 :
method offset-in-minutes(DateTime:D: --> Real:D)
method offset-in-hours(DateTime:D: --> Real:D)

UTCからのずれをそれぞれ分、時間で表示する.


Str メソッド


定義 :
method Str(DateTime:D: --> Str:D)

formatter属性によって指定された表現で、時刻を返す.デフォルトでは、 ISO 8601による表記で返す.

 時刻を表示する:
say DateTime.new("2018-04-30T17:37:12.001+09:00").Str;
実行結果:
2018-04-30T17:37:12.001000+09:00


Instance、Date、DateTime メソッド


定義 :
method Instant(DateTime:D: --> Instant:D)
multi method Date(DateTime:U --> Date:U)
multi method Date(DateTime:D --> Date:D)
method DateTime(--> DateTime)

それぞれ、自身のInstanceオブジェクト、Dateオブジェクト、DateTimeオブジェクトを返す.


posix メソッド


POSIX/UNIXに基づいた時刻、すなわち、1970年1月1日 UTC からの経過秒数を返す.

 POSIX/UNIXによる時刻を表示する:
say DateTime.new("2018-04-30T17:37:12.001+09:00").posix;
実行結果:
1525077432


later、earlier メソッド


定義 :
method later(DateTime:D: *%unit)
method earlier(DateTime:D: *%unit)

指定した時間だけ遅らせた(later)、または、早めた(earlier)時刻を返す.
引数として有効なキーは以下の通り:
second,seconds,minute,minutes,hour,hours,day,week,weeks,month,months,year,years.
second,seconds以外を使ったとき以外の値は、Intに変換される.
負の値も指定できる.

例1 1日後の時刻を表示する:
say DateTime.new("2018-04-30T17:37:12.001+09:00").later(:1day);
次でもよい.
say DateTime.new("2018-04-30T17:37:12.001+09:00").later(:day);
実行結果:
2018-05-01T17:37:12.001000+09:00

例2 2年前の時刻を表示する:
say DateTime.new("2018-04-30T17:37:12.001+09:00").earlier(:2years);
次でもよい.
say DateTime.new("2018-04-30T17:37:12.001+09:00").earlier(:2year);
実行結果:
2016-04-30T17:37:12.001000+09:00


truncated-to メソッド


定義 :
method truncated-to(DateTime:D: Cool $unit)

指定場所未満の時刻を切り捨てた時刻を返す(例を見るとわかる).
有効な引数は以下の通り:
'second','minute','hour','day','month','year'
second,seconds

 日以降を切り捨てる:
say DateTime.new("2018-04-30T17:37:12.001+09:00").truncated-to('day');
実行結果:
2018-04-30T00:00:00+09:00


utc メソッド


定義 :
method utc(DateTime:D: --> DateTime:D)

UTCでのオブジェクトの時刻を返す.

 UTCでの時刻を表示する:
say DateTime.new("2018-04-30T17:37:12.001+09:00").utc;
実行結果:
2018-04-30T08:37:12.001000Z


in-timezone メソッド


定義 :
method in-timezone(DateTime:D: $timezone = 0 --> DateTime:D)

指定したtimezoneにおけるオブジェクトの時刻を返す.引数には、UTCからの秒数でのずれを指定する.
$timezone = 0とすれば(つまり、デフォルト)、utcメソッドと同じ値が返される.

 ロサンゼルス(太平洋夏時間:UTC-7)における時刻を表示する :
say DateTime.new("2018-04-30T17:37:12.001+09:00").in-timezone(-7 * 60 * 60);
実行結果:
2018-04-30T01:37:12.001000-07:00


local メソッド


定義 :
method local(DateTime:D: --> DateTime:D)

in-timezoneと同じだが、ローカルタイムゾーン($*TZ)で指定する.$*TZは秒数で指定する.

 ロサンゼルス(太平洋夏時間:UTC-7)における時刻を表示する :
my $*TZ = -7 * 60 * 60; #myは省略可
say DateTime.new("2018-04-30T17:37:12.001+09:00").local;
実行結果:
2018-04-30T01:37:12.001000-07:00


infix:<-> 関数


定義 :
multi sub infix:<-> (DateTime:D, Duration:D --> DateTime:D)
multi sub infix:<-> (DateTime:D, DateTime:D --> Duration:D)

DateTimeオブジェクトと、他のDateTimeオブジェクトまたは、Durationオブジェクトとの差を返す.返り値の型は右オペランドによる.すなわち、
DateTime-Duration=DateTime
DateTime-DateTime=Duration
となる.

 30日前の時刻を表示する :
say DateTime.new("2018-04-30T17:37:12.001+09:00") - Duration.new(30 * 24 * 60 * 60);
実行結果:
2018-03-31T17:37:12.001000+09:00


infix:<+> 関数


定義 :
multi sub infix:<+> (DateTime:D, Duration:D --> DateTime:D)
multi sub infix:<+> (Duration:D, DateTime:D --> DateTime:D)

DateTimeオブジェクトを、Durationオブジェクト分だけ進める(順序は問わない).

 2日後の時刻を表示する :
say DateTime.new("2018-04-30T17:37:12.001+09:00") + Duration.new(2 * 24 * 60 * 60);
実行結果:
2018-05-02T17:37:12.001000+09:00


Dateish ロール


DateTimeは、Dateishを実装(Does)しているため、Dateishのメソッドも使える.詳細は、こちらを参照.


参考にしたサイト



以下のドキュメンテーションを和(意)訳、改変した.

Parcelabeと実行時エラー

なかなか直せなかったバグが、Parcelable関連を見直すことで直ったので、その時に気づいた注意点を残しておく.

Parcelableを実装するときの注意


例えば、Stringの配列をreadする際は、readStringArrayではなく、createStringArrayを使う :
public class MyData implements Parcelable {
    
    private String mStringArray[];
    
    public String[] getStringArray() {
        return mStringArray;
    }
    
    public void setStringArray(String stringArray[]) {
        this.mStringArray = stringArray;
    }
    
    private MyData(Parcel in) {
        mStringArray = in.createStringArray();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeStringArray(mStringArray);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<MyData> CREATOR = new Creator<MyData>() {
        @Override
        public MyData createFromParcel(Parcel in) {
            return new MyData(in);
        }

        @Override
        public MyData[] newArray(int size) {
            return new MyData[size];
        }
    };
}

もし、readStringArray使った場合、画面回転程度の読み込みではエラーは出ないが、「アクティビティ破棄する(アクティビティを保持しない)」設定をオンにした状態で再起動する(ホームボタン→アプリ起動)と、私の環境では次のエラーが出た.
Caused by: java.lang.NullPointerException: Attempt to get length of null array
...
at MainActivity.onCreate(MainActivity.java:xxx)


Parcelableでデータを受渡しするときの注意


特に、onRestoreInstanceStateonSavedInstanceStateでアクティビティの復元を行う際、putParcelableArraygetParcelableArrayなどで、Parcelableの配列を使わないほうが良い.
...
private MyData myData[];
...
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    ...
    myData = (MyData[])savedInstanceState.getParcelableArray(Key1);
    ...
}

上のようにキャストしないとコンパイルエラーが発生するが、アクティビティが破棄された後(「アクティビティを破棄する」設定をオンにしてアプリケーションを終了する)では、Bundle内で型の情報が保持されないようで、次のようなエラーが出た.
Caused by: java.lang.ClassCastException: android.os.Parcelable[] cannot be cast to com.foo.example.MyData[]
        at com.foo.example.MainActivity.onViewStateRestored(MainActivity.java:xxx)

代わりに、putParcelableArrayListgetParcelableArrayListを使うことで、このエラーを回避できた.
...
private ArrayList<MyData> myData;
...
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    ...
    myData = savedInstanceState.getParcelableArrayList(Key1);
    ...
}


参考にしたサイト



RecyclerView in NestedScrollViewでスクロールが角つく

javaコード中で、次を追加すると回避できた.
recyclerView.setNestedScrollingEnabled(false);

しかし、そもそもNestedScrollViewとRecyclerViewをネストにすることが問題っぽい.
例えば、画面をスクロールしてもRecyclerView.onScrolledは呼ばれないみたいなので、ネストするというデザインを変えたほうがいいかもしれない.

参考にしたサイト



CheckBoxのチェックボックスを右側に表示させる。

題名の日本語が少し怪しい感じがするが、CheckBoxはデフォルトでは次のようになる.


これを次のようにするのが当記事の目的である.


API 17以上の場合


xmlの方で、CheckBoxlayoutDirction属性をrtlにしてやるだけ.
android:layoutDirection="rtl"


API 17未満の場合


同じくxmlの方で、CheckBoxに、次の2つを加える.
android:button="@null"
android:drawableEnd="?android:attr/listChoiceIndicatorMultiple"

ただ、この方法だとCheckBoxをクリックしたとき、チェックボックスではなくCheckBoxの中心がハイライトされるので注意(下の画像を参照).



参考にしたサイト



BloggerにSyntaxHighlighterを導入する!

SyntaxHighlighter v3は導入できなかったが、最新のSyntaxHighlighter v4を導入できたので、その方法を紹介したいと思う.
SyntaxHighlighterを導入できずに諦めている人にぜひ読んでもらい、私と同じ喜びを感じてほしいと思う.

導入する!



その0


Git Bash(Windowsの場合)Node.js(LTSのほうが良い?)をインストールする.

その1


Git Bushを起動して、syntaxhighlighterを保存するローカルディレクトリに移動し、以下のコードを続けて入力する.
$ git clone https://github.com/syntaxhighlighter/syntaxhighlighter.git
$ cd syntaxhighlighter
$ npm install

次のような感じ




その2


続いて、以下のコードを入力する.
ko-ika@PC MINGW64 ~/Blogger/syntaxhighlighter (master)
$ ./node_modules/gulp/bin/gulp.js setup-project

そのまま通れば(その3へ)問題ないが、次のエラーが出るかもしれない.(私の場合はこうなった.)
[hh:mm:ss] TypeError: loadReposFromCache(...).error is not a function

この時は、 /syntaxhighlighter/build/setup-project.js を開いて、39行の error catch に変える.
つまり、39行目を次のようにして上書き保存する.
const loadRepos = () => loadReposFromCache().catch(loadReposFromGitHub).then(R.map(R.pick(['clone_url', 'name'])));

そして、Git Bashで、その2の初めのコードを再び入力する(うまくいくはず).

無事成功すると、 /syntaxhighlighter/repos/ 下に新たなファイルフォルダーがダウンロードされる.



その3


次に、 ./node_modules/gulp/bin/gulp.js build を使う.

この時に、 --brushes --theme を指定する.


--brushes


Xという言語をハイライトさせたいのなら、 --brushes=X という風にする.

コンマで区切ることで複数指定することもできる: --brushes=X,Y

どの言語が対応しているかは、 /syntaxhighlighter/repos/ 下にある brush-X という名前のフォルダーを探せばわかる(Xには、具体的な言語が入る、javascriptとか).

また、 --brushes=all とすれば、 /syntaxhighlighter/repos/ 下にある言語すべてをハイライトできる.


--theme


Xというテーマを使いたかったら、 --theme=X という風に指定する。

使用できるテーマは、以下のサイトに載ってる(多分).


次のサイトは、画像もあって役に立つ.


/syntaxhighlighter/repos/ 下にあるフォルダー theme-X を探してもらえば、対応しているテーマが分かる.

使用例


例えば、javascriptとcssをハイライトさせたくて、テーマはdefaultにしたい場合、Git Bashに次のように入力する.
ko-ika@PC MINGW64 ~/Blogger/syntaxhighlighter (master)
$ ./node_modules/gulp/bin/gulp.js build --brushes=javascript,css --theme=default

これが通れば問題ないので、その6まで行ってほしいが、次のエラーが出るかもしれない. 多分出る.
[hh:mm:ss] TypeError: Promise.props is not a function


その4


/syntaxhighlighter/build/bundle.js を開いて、33行目の 'songbird' 'bluebird' に変えて、上書き保存する.
  const Promise = require('bluebird');

そして、再び ./node_modules/gulp/bin/gulp.js build --brushes=javascript,css --theme=defaultを走らせる.


上の画像のように、次のようなエラーが発生すると思う(もし、うまくいったらその6まで飛んでもらいたい).
ERROR in ./src/core.js
Module not found: Error: Cannot resolve module 'brush-javascript' in C:\Users\ko-ika\Blogger\syntaxhighlighter\src
 @ ./src/core.js 241:14-41


その5


上のエラー中の、Module not found: Error: Cannot resolve module 後の2つの'で囲まれたファイルフォルダーを手動でインストールする.
ko-ika@PC MINGW64 ~/Blogger/syntaxhighlighter (master)
$ npm install folder-name

上の例では、以下のようにする.
ko-ika@PC MINGW64 ~/Blogger/syntaxhighlighter (master)
$ npm install brush-javascript

ko-ika@PC MINGW64 ~/Blogger/syntaxhighlighter (master)
$ npm install brush-css

そして、 ./node_modules/gulp/bin/gulp.js buildを走らせる.

特にエラーは出ないで、終了すると思う.

もし、うまくいかなかったら、次も試してみる.
npm install opts-parser
npm install syntaxhighlighter-match
npm install syntaxhighlighter-html-renderer
npm install syntaxhighlighter-regex


その6


syntaxhighlighter/dist ディレクトリ下にいくつかのファイルが生成されているが、その中の、 syntaxhighlighter.js theme.css を各自のサーバーにアップロードする。

あとは、ブログのHTMLをいじるだけ(ここから先は、こちらの記事のその2と似ている).

Boggerのトップページのサイドメニューの「レイアウト」をクリックし、「HTML編集」ボタンを押す.


</head>タグの上に以下のコードを挿入する.

<script src='[path]/syntaxhighlighter.js' type='text/javascript'/>
<link href='[path]/theme.css' rel='stylesheet' type='text/css'/>

コード中の[path]は、syntaxhighlighter.jsと、theme.cssをアップロードしたフォルダー名を指定する. 同じフォルダーでなくてもよい. 例えば、https://foo.com/styles/theme.csshttps://foo.com/scripts/syntaxhighlighter.jsなど.


そして、「テーマを保存」ボタンを押すだけ.


使用方法・注意点


使い方は、基本的に前のバージョンのSyntaxHighlighterとかわらないっぽいので、他のサイトを見てもらいたい.

一つだけ注意することは、この方法で導入したハイライターにはデフォルトでツールバーが表示されないことだ.


参考にしたサイト






ColorFilterを元に戻す

方法は2つ見つかった.

その1
EditText editText = findViewById(R.id.editText);

editText.getBackground().clearColorFilter();


その2
EditText editText = findViewById(R.id.editText);

editText.getBackground().setColorFilter(null);


うまくいかなかったら、この後に次を入れる.
editText.refreshDrawableState();


参考にしたウェブサイト

ConstraintLayoutの制約をJavaで設定する

ConstraintLayout#LayoutParamsを使う. まず、ConstraintLayout上のViewを取得する.
Button button = findViewById(R.id.button);

もしくは、追加したい適当なオブジェクトを作成し、ConstraintLayoutにViewを追加する.
Button button = new Button(this);
constrintLayout.addView(button)

※先にaddViewしないと、次のlayoutParamsにnullが入る(エラーが発生する)ので注意.

次に、上のViewのLayoutParamsを取得し、適当に設定する. 親View(?)と連結したいときは、PARENT_IDを代入する. ほかのViewと連結したいときは、View#getIdメソッドを使う. LayoutParamsのフィールドについては、xmlのものとほとんど同じなので、そちら(例えば、Android Studioのデザインビューとか)を見るとわかりやすいと思う.
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams)button.getLayoutParams();
layoutParams.startToEnd = anotherView.getId();
layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
layoutParams.verticalBias = (float) 0.1;
layoutParams.horizontalBias = (float) 0.1;
button1.setLayoutParams(layoutParams);

最後に、上の変更を適用する.
button.setLayoutParams(layoutParams);

他にもこちらと同様に、ViewのWidthやHeight、Marginを変更できたりする(MarginLinearLayoutにキャストしなくてもよい、次の適用例を参照).

適用例


ConstraintLayout上に2つのボタンを表示させる.

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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/constraintLayoutParamsLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.blogspot.ko-ika.constraintlayoutparams.MainActivity">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button1" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button2" />
</android.support.constraint.ConstraintLayout>

activity_main.xml
package jp.blogspot.ko-ika.constraintlayoutparams;

import android.support.constraint.ConstraintLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

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

        Button button1 = findViewById(R.id.button1);
        Button button2 = findViewById(R.id.button2);

        ConstraintLayout constraintLayout = findViewById(R.id.constraintLayoutParamsLayout);

        // Button1の設定
        ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams)button1.getLayoutParams();
        layoutParams.topMargin = 8;
        layoutParams.rightMargin = 16;
        layoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.verticalBias = (float) 0.1;
        layoutParams.horizontalBias = (float) 0.1;
        button1.setLayoutParams(layoutParams);

        // Button2の設定
        layoutParams = (ConstraintLayout.LayoutParams)button2.getLayoutParams();
        layoutParams.leftMargin = 16;
        layoutParams.startToEnd = button1.getId();
        layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.verticalBias = (float)0.1;
        layoutParams.horizontalBias = 0;
        button2.setLayoutParams(layoutParams);
    }
}

実行例


さらに


ConstraintSetを使うと同じ様に設定できるらしい.


が、この方法だと、Biasが設定できない?

また、Viewが実際に配置される(?)まで、そのIDが取得できないみたいなので、注意が必要.
実際に、上の適用例を次のようにしてみる.

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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/constraintLayoutParamsLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.blogspot.ko-ika.constraintlayoutparams.MainActivity">

</android.support.constraint.ConstraintLayout>

activity_main.xml
        

        Button button1 = new Button(this);
        Button button2 = new Button(this);
        ConstraintLayout constraintLayout = findViewById(R.id.constraintLayoutParamsLayout); 

        // Button1の設定
        constraintLayout.addView(button1);
        button1.setText("Button1"); 
        ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams)button1.getLayoutParams();
        layoutParams.topMargin = 8;
        layoutParams.rightMargin = 16;
        layoutParams.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.verticalBias = (float) 0.1;
        layoutParams.horizontalBias = (float) 0.1;
        button1.setLayoutParams(layoutParams);

        // Button2の設定
        constraintLayout.addView(button2);
        button2.setText("Button2"); 
        layoutParams = (ConstraintLayout.LayoutParams)button2.getLayoutParams();
        layoutParams.leftMargin = 16;
        layoutParams.startToEnd = button1.getId();
        layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
        layoutParams.verticalBias = (float)0.1;
        layoutParams.horizontalBias = 0;
        button2.setLayoutParams(layoutParams);

変更箇所をマークアップした. すると、次のようになってしまう.

参考にしたサイト



removeViewが動かない

ViewGroup#removeViewメソッドの引数がnullでないのにnullだったため、動かなくなった.

まず、引数がnullかどうか確かめよう(引数をnullにしても例外が発生しないので気づかなかった).
以下の方法は、消去したいViewに変数を割り当てなくてもできる方法である

コードは以下のようである.
parentLinearLayout.removeView(childLinearLayout);

どうも描画中のアニメーションのせいで、動かないらしい.


上記サイトの方法だと、LinearLayoutを継承したクラスを作って、削除するときのアニメーションを表示させないようにしているようだ.

この方法だとかなりの手間がかかりそうだったので、代わりにViewGroup#removeViewAtメソッドを使ったところ、うまくいった.
parentLinearLayout.removeViewAt(index);

indexとしてchildLinearLayoutのポジションを指定する.

ちなみに、子Viewのindexを取得するには、ViewGroup#indexOfChildメソッドを使う.
int index = parentLinearLayout.indexOfChild(childLinearLayout);

参考にしたサイト


LinearLayoutのサイズをJavaコード中で変更する

タイトルにある通り

LinearLayoutでは、setHeight等が使えないので、android.view.ViewGroup.LayoutParamsを使う.
LinearLayout linearLayout = findViewById(R.id.LinearLayout);
ViewGroup.LayoutParams layoutParams  = linearLayout.getLayoutParams();

layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.width = 200;

ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams)linearLayout.getLayoutParams();

marginLayoutParams.leftMargin = 8;
marginLayoutParams.rightMargin = 8;
marginLayoutParams.topMargin = 8;
marginLayoutParams.bottomMargin = 8;

linearLayout.setLayoutParams(marginLayoutParams);


単位は、pixelだそう. dip(dp)を使いたい場合は、例えば、5行目を
layoutParams.width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics());


とする. (試してない.)

この方法は、ConstraintLayoutや、ImageButton等でも使えるっぽい.

参考にしたウェブサイト

Bloggerにcode prettifyを導入する

自分のブログにソースコードをきれいに埋め込みたくなった.そこで、"SyntaxHighLighter"を導入しようとしたが、どうやっても導入できなかった(自分の能力不足). 他のソースコードを表示するスクリプトを調べてみると、Googleが開発した"code pretifer"というものにたどり着いた. 導入してみると簡単にできたので、皆さんにもその導入方法を紹介したいと思う.

方法その1


導入する


Boggerのトップページのサイドメニューの「レイアウト」をクリックし、「HTML編集」ボタンを押す.



HTML内で、</head>タグを探し、そのすぐ上に以下のコードを挿入する.

<script src='https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js'></script>

HTMLの上側にある「テーマを保存」ボタンを押す.



これでOK.

実際につかう


記事を作成する際は、HTMLの方がよい.



表示させたいソースコードを、<pre class="prettyprint linenums">...<pre>の中に入れる(基本は、<pre class="prettyprint">...<pre>だが、これにすると、さすがに不格好でだれも使わないと思ったので、linenumsクラスも用いた. ).入力の際は、<&lt;に変えないと、うまく表示されないので注意.
プレビューで確認するとちゃんと出力されていることがわかる.



テーマの変更


テーマの変更もできる. 以下の中から選べる.



CGIパラメータの"skin"を指定することでテーマを変更できる. 例えば、"Doxy"にしたいときには、「導入する」で挿入したコードを次のようにすればよい.

<script src='https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?skin=doxy'></script>

他のテーマでも大文字を小文字にすればよいはず(試してない).

方法その2


導入する


まず、必要なファイルを以下のサイトからダウンロードする.



右上の「Clone of download」ボタンを押し、メニューの中の「Download Zip」ボタンを押すとダウンロードが開始される.



.zipファイルを解凍したら、"loader"フォルダーに入っている"prettify.css"、及び、"prettify.js"を外部サーバーにアップロードする(Bloggerには無理なので、Googleドライブや、GitHub等. GitHubについては、「Github Pagesでウェブサイトを構築する方法」が役に立った).

次に、方法その1と同様に、HTMLを編集する. まず、</head>タグの上に以下のコードを挿入する.

<link href='[path]/prittyfy.css' rel='stylesheet' type='text/css'/>
<script src='[path]/prettify.js' type='text/javascript'/>

コード中の[path]は、"prettify.css"と、"prettify.js"をアップロードしたフォルダー名を指定する. 同じフォルダーでなくてもよい. 例えば、"https://hogehoge.com/styles/prettify.css"、"https://hogehoge.com/scripts/prettify.js"など. さらに、<body>タグ内を編集して、次のようにする.

<body ... onload='PR.prettyPrint()'>
...はすでに書かれている内容. 次の写真を見るとわかりやすいと思う.



後は、「テーマを保存」を押して変更を適用するだけ.

テーマの変更


「方法その1」の「テーマ変更」で見たサイト(その2の最初のほうでダウンロードしたフォルダーの"styles"フォルダーに入っている.)以外に次のサイトからテーマの閲覧、ダウンロードができる.



気に入ったデザインのテーマがあったら、必要ならダウンロードして、アップロードしよう. ここでは、"Doxy"を選んだとして説明する.今までと同様に、</head>タグの上に以下のコードを挿入する.

<link href='[path]/doxy.css' rel='stylesheet' type='text/css'/>
ただし、"prettify.css"を読み込んでいる箇所は削除する(prettifyのテーマが書かれたスタイルシートは1つだけ読み込む. 次の写真を参照).



最後に、「テーマを保存」を押す. これで、うまくいくはず.

表示される行番号を増やす


使用しているテーマによっては、表示される行番号が少ない場合がある. その場合は、まず、Bloggerの「テーマ」を開いて、「カスタマイズ」ボタンを押す.


そこで、上に出てきた「Bloggerテーマデザイナー」の一番左のメニューの「上級者向け」をクリックし、隣のメニューの一番下にある「CSSを追加」を押し、さらにその隣に次のコードを書く.

li.L0, li.L1, li.L2, li.L3,
li.L5, li.L6, li.L7, li.L8 {
  list-style-type: decimal !important;
}

そして、右上の「ブログに適用」ボタンを押す.



ソースコードが横にはみ出ないようにする


使用テーマによっては、ソースコードが横にはみ出てしまう場合がある(デフォルトが当てはまる). これを解決するには、「表示される行番号を増やす」で変更したCSSに次の内容を加えればよい.

pre {
  overflow: auto;
  resize: horizontal;
}

こうすると、ソースコードが横にスライドできるようになる. もし、すでに<pre>タグを他のことに使っているなら、次のように書けばよい.

pre.my-code {
  overflow: auto;
  resize: horizontal;
}

そして、面倒だが、ソースコードを書く際に毎回次のように書く.

<pre class="prettyprint linenums; my-code">
source code...
</pre>

使い方


最後に使い方をまとめる. すでにみたが、基本的な使い方は次である.
<pre class="prettyprint">
source code...
</pre>

すでに注意したが、ソースコード内の<&lt;に変える必要がある.
また、次のように入力してもよいが、Bloggerでは、うまく表示されない?(少なくとも、私が試したときはちゃんと表示されなかった)ので、推奨しない.

<pre><code class="prettyprint">
source code...
</code></pre>

"PR.prettyPrint()"がどの言語のコードかを自動で判定してくれるが、例えば、C++で書かれたことを特定したい場合は、language-cppスタイルクラスを使えばよい(htmlの場合は、lang-htmlでいいみたい. cssも?).

<pre class="prettyprint lang-cpp">
source code...
</pre>

linenumsクラスを付け加えれば、行番号をつけることができる. さらに、例えば、linenums:50と書けば、50番目から行数を数え始めるようにできる.

<pre class="prettyprint linenums:50">
source code...
</pre>

また、あまり使わないと思うが、nocodeクラスを使うことで、コードでない文字列も入れることができる.

<pre class="prettyprint">
source code... <span class="nocode">This is not code</span> ...
</pre>

使用例


次のようにhtmlに記述する(見やすさのため3行目の<はエスケープしてない).
<pre class="prettyprint linenums:30 language-cpp">
int foo(int arg) {
   cout << "Hello World from foo!" << endl; 
      /* 
        Hello World! <span class="nocode">This is not code</span>
      */ 
   return 0;
}
</pre>

参考にしたウェブサイト

とても参考になった. 本記事の大体はこのサイトに拠っている. ソースコードを見やすくする方法で、ここに書いてないことがたくさんあるので、見るべきである.

「その1の方法」の内容(Blogger向けではない)をもう少し詳しく簡潔にまとめてある.

英語が読めるなら、上記2つのサイトを見れば十分だと思う. また、前者のサイトには、code prettifyの対応言語が載っている.

FloatingActionButtonをスクロール中に消す

NestedScrollView を下にスクロールしている間は, FloatingActionButton を表示させないようにする. 少し変えれば, RecyclerView 等の他のScrollViewにも使えそう(未確認). 下に実際の挙動を示した. 目次 ...