ObjectAnimatorでTextViewの内容を書き換える

ObjectAnimatorおもろいなー setNumber(int)を持つNumberTextViewというクラスを作り

public class NumberTextView extends AppCompatTextView {

    public NumberTextView(Context context) {
        super(context);
    }

    public NumberTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public NumberTextView(Context context, AttributeSet attrs,
            int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setNumber(int number) {
        setText(String.valueOf(number));
    }
}

ObjectAnimatorでエイッとすると

ObjectAnimator objectAnimator = ObjectAnimator.ofInt(binding.numberTextView, "number", 0, 10000);
objectAnimator.setDuration(10 * 1000);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();

数値がテキストとして表示される

f:id:tomorrowkey:20160301093402g:plain

単にリフレクション使って値を設定しているだけなのかなー。

ProgressBarの値をアニメーションで変更する

f:id:tomorrowkey:20160229190212g:plain ↑こういうことやりたいんだけど、ProgressBar#setProgress(:int)だとアニメーションしてくれない。

そうだ、ObjectAnimatorを使ってアニメーションを実現しよう。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  tools:context=".activity.MainActivity"
  >
  <LinearLayout
    android:orientation="vertical"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ProgressBar
      style="?android:attr/progressBarStyleHorizontal"
      android:id="@+id/progress_bar"
      android:layout_margin="16dp"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"/>

    <Button
      android:id="@+id/update_progress_button"
      android:text="Update progress"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"/>
  </LinearLayout>
</layout>
public class MainActivity extends AppCompatActivity {

  ActivityMainBinding binding;

  ObjectAnimator objectAnimator;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    binding.updateProgressButton.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        int progress = (int) (Math.random() * 100);
        setProgressWithAnimation(progress);
      }
    });
  }

  private void setProgressWithAnimation(int progress) {
    int[] values = {binding.progressBar.getProgress(), progress};
    if (objectAnimator == null) {
      objectAnimator = ObjectAnimator.ofInt(binding.progressBar, "progress", values);
    } else {
      objectAnimator.cancel();
      objectAnimator.setIntValues(values);
    }
    objectAnimator.setDuration(1000);
    objectAnimator.setInterpolator(new DecelerateInterpolator(2.0f));
    objectAnimator.start();
  }

}

Activityに書いちゃうとアレなんでカスタムViewでやるとスッキリしそうでよさそう。 アニメーションしている最中はそのときのProgressの値になってしまうので、getProgress()を使いたいケースではこれは使えない。

Androidエミュレータにテキストをペーストする

Androidエミュレータは母艦とクリップボードの共有がされないので、パスワードなどの複雑なテキストを毎回手打ちしなくてはならなくて面倒でした。 そこで、簡単にコピペできるような関数を作りました。

function paste_in_android() {
  e=$(pbpaste | sed -e "s/ /\\\\ /g" | sed -e "s/'/\\\\'/g" | sed -e 's/"/\\"/g')
  adb shell input text "$e"
}

クリップボードに入っているテキストをadb経由で入力します。

input textを使っているためマルチバイト文字を渡すことはできなく、さらにIMEの状態によって入力のされ方が変わります。
adbを経由するので、エミュレータだけではなく実機でも使うことができます。面倒なURLの入力にも使えるんじゃないでしょうか。
うまくうごかなかったら、エスケープすべき文字があるんだと思います。sedでエスケープ処理を追加してください。

私はターミナルで入力するのすら面倒なのでAlfred workflowにしておきました。(リポジトリgifアニを見ればどういう風に使うのか一発で分かると思います。) github.com

べんり〜

コマンドラインから特定のクラスのみテストする

次のコマンドで実現できる

./gradlew connectedAndroidTest -Pcom.android.tools.instrumentationTestRunnerArgs=class=com.example.Util

com.android.tools.instrumentationTestRunnerArgsにはいろんなオプションが指定でき、どんなオプションがあるかは次のURLから見ることができる

AndroidJUnitRunner | Android Developers

複数の引数を指定する場合はカンマで区切る

./gradlew connectedAndroidTest -Pcom.android.tools.instrumentationTestRunnerArgs=class=com.example.Util,size=medium

キーが重複するハッシュをマージする

2つのハッシュをマージするときにキーが重複するとレシーバを優先されてしまう

$ pry
[1] pry(main)> h1 = {a: [1, 2, 3], b: [1, 2, 3]}
=> {:a=>[1, 2, 3], :b=>[1, 2, 3]}
[2] pry(main)> h2 = {a: [10], c: [10,11]}
=> {:a=>[10], :c=>[10, 11]}
[3] pry(main)> h1.merge(h2)
=> {:a=>[10], :b=>[1, 2, 3], :c=>[10, 11]}

Hash#mergeはブロックを受け取ることができて、キーが重複した時の挙動を指定することができる

$ pry
[1] pry(main)> h1 = {a: [1, 2, 3], b: [1, 2, 3]}
=> {:a=>[1, 2, 3], :b=>[1, 2, 3]}
[2] pry(main)> h2 = {a: [10], c: [10,11]}
=> {:a=>[10], :c=>[10, 11]}
[3] pry(main)> h1.merge(h2) { |key, h1v, h2v| h1v + h2v }
=> {:a=>[1, 2, 3, 10], :b=>[1, 2, 3], :c=>[10, 11]}

class Hash (Ruby 2.2.0)

Array#shuffleで同じ結果を得る

shuffleの引数に同じRandomオブジェクトを渡せば何度シャッフルしても同じ結果になる

$ pry
[1] pry(main)> Array(1...10).shuffle(random: Random.new(473))
=> [6, 4, 9, 5, 8, 3, 2, 1, 7]
[2] pry(main)> Array(1...10).shuffle(random: Random.new(473))
=> [6, 4, 9, 5, 8, 3, 2, 1, 7]
[3] pry(main)> Array(1...10).shuffle(random: Random.new(473))
=> [6, 4, 9, 5, 8, 3, 2, 1, 7]

class Array (Ruby 2.2.0)

配列の重複を取得する

たしかに〜

$ pry
[1] pry(main)> Array(1..10) && Array(5..6)
=> [5, 6]

超簡単だった。


重複を除外するならuniq使えばいいけど、重複を取得するのはメソッドがなかった。 こんな感じに書けば重複した値が取得できる

$ pry
[1] pry(main)> require 'active_support/all'
=> true
[2] pry(main)> (Array(1..10) + Array(5..6)).group_by{|i| i}.reject{|k, v| v.one?}.keys
=> [5, 6]