Unknown Region

プログラムでハマったエラーとその解決方法についての備忘録メモ

【kotlin】ViewGroup.LayoutParamの落とし穴

恐らくJavaAndroidを組んでいればパッと分かる話なのですが。

val view: View = View()    
view.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT

このコードはkotlin上では警告が出ないんですが、実際に動かすとNullPointerExceptionで落ちます。

理由はこのViewGroup.LayoutParamsがJava由来のコードだからだろうけど……。

意外とやりがちなので、警告出して欲しいなあ。

 

【Ruby】RMagicで画像を分割する

ひょんなことからRPGツクールVXの歩行グラフィックス(通称:歩行グラ)のトリミングを行うことになったのだけど、丁度いい手頃なツールが見当たらなかったので作ることに。

下の画像がRPGツクールVX規格の歩行グラの例です。簡単に言えばこれを12分割したいということです。

f:id:PlugOut:20180714234804p:plain

今回は説明用に「ゆらゆらドット。」様の素材を使用させて頂きました。

dotrpg.blog.fc2.com

 

今回は画像を手軽に扱いたかったので、RubyRMagicを採用することにしました。

RMagicでの画像のトリミングは本当に簡単で、単純に切り抜くだけなら以下の数行で終わります。

サンプル(Ruby) :

require 'RMagick'
original = Magick::ImageList.new("input.png") # input.pngを取り込む
image = original.crop(0, 0, 48, 80) # (x = 0px, y = 0px) を左上として(width = 48px, height = 80px)を切り取る
image.write("output.png") # output.pngとして出力する

 

実行結果 : output.png

f:id:PlugOut:20180714235423p:plain

しっかり指定サイズで切り抜かれています。

 

そんでもって今の作業を12回繰り返さなければならないので、これを汎用化した便利プログラムを作成しました。

プログラム : splitter.rb

#!/usr/bin/ruby
require 'RMagick'
require 'optparse'

file = ARGV[0]
cols = 3
rows = 4
opt = OptionParser.new
opt.on('-c', '--cols ITEM', '分割する列の数') { |v| cols = v.to_i }
opt.on('-r', '--rows ITEM', '分割する行の数') { |v| rows = v.to_i }

opt.banner += ' file'
begin
  opt.parse(ARGV)
rescue OptionParser::InvalidOption => e
  abort("無効なオプション指定が含まれています")
end

if (!file || !cols || !rows)
  abort("必要な引数が与えられていません")
end

begin
  original = Magick::ImageList.new(file)
rescue => error 
  abort("ファイルが読み込めません")
end

width = original.columns / cols #幅
height = original.rows / rows #高さ

file_parts = file.split("\.")
output_file_base_name = file_parts[0]
extension = ""
if file_parts[1]
  extension = ".#{file_parts[1]}"
end

for i in 1..cols
  for j in 1..rows
    image = original.crop(width * (i - 1), height * (j - 1), width, height)
    image.write("#{output_file_base_name}.#{i}-#{j}#{extension}")
  end 
end

使い方は単純に、ruby splitter.rb input.pngで、input.pngが12分割されます。

分割された後の名前は、「(元ファイルの拡張子以外).横x縦(元ファイルの拡張子)」になります。

またオプションで分割する列の数と行の数を指定することも可能にしてあります。

何か他に使い道あるかな?笑

 

【Android】ImageViewやImageButtonの画像をリセットする

タイトルの通り。

単純にどちらもsetImageBitmapにnullを渡してあげれば良いみたい。

これはJavaでもKotlinでも同様。

// imageView
imageView.setImageBitmap(null)

// imageButton
imageButton.setImageBitmap(null)

 

【Android】SpannableStringBuilderでの一部文字色変更について

TextViewでテキストの一部分の文字色だけを変更しようと思い、調べていたのだけれど、TextAppearanceSpanのサンプル例ばかり出てくる。

確かにアンダーラインや文字のスタイル(Bold)などを指定する場合はstyle用の

XMLを用意するのが筋かもしれないが、ただ色を変えるだけならばちょっと大掛かりすぎるように思える。

また、色のカラーコードをXML外で管理している場合もこれまた億劫だ。

 

色々調べていくうちに、解決策としてForegroundColorSpanとを使えば良いということが分かった。

単純にコンストラクタに色情報のIntegerを渡すだけで良い。

 

サンプル(Java):

※ textViewはXMLのレイアウトにR.id.text_viewとしてあらかじめ用意されているものとする

// 〜前段処理〜

TextView textView = findViewById(R.id.text_view);
String message = "message";
int spanColor = Color.RED;
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(message);
spannableStringBuilder.setSpan(new ForegroundColorSpan(spanColor), 0, 1, 0);
textView.setText(spannableStringBuilder.subSequence(0, spannableStringBuilder.length()));

// 〜後続処理〜

 

サンプル(Kotlin):

※ textViewはXMLのレイアウトにR.id.text_viewとしてあらかじめ用意されているものとする

// 〜前段処理〜

val textView: TextView = findViewById(R.id.text_view)
val message: String = "message"
val spanColor: Int = Color.RED
SpannableStringBuilder(message).let {
    it.setSpan(ForegroundColorSpan(spanColor), 0, 1, 0)
    textView.text = it.subSequence(0, it.length)
}

// 〜後続処理〜

これで、messageの出力が先頭の「m」だけ赤くなる。

 

結果:

f:id:PlugOut:20180713184049p:plain



 

【JavaScript】Infinityの判定について

JavaScriptでは0除算を行うと結果がInfinityになります。

基本的には他の言語と同様に0除算自体を回避すべきだと思うんですが、Infinityでないことをチェックするには以下のメソッドを使います。

Number.isFinite(128); // true
Number.isFinite(128 / 0); // false
Number.isFinite(Infinity); // false

ただしNumber.isFinite()は以下の様にnumber以外の変数についてはfalseを返すので注意が必要です。

Number.isFinite(null); // false
Number.isFinite([]); // false
Number.isFinite(new Object()); // false

何が言いたいかというと、Infinityであるということを判定したい場合はNumber.isFinite()を使うのではなく以下の様にするのが正解っぽいです。

Math.abs(1 / 0) === Infinity; // true
Math.abs(-1 / 0) === Infinity; // true

何故Math.abs()を使用しているかというと、InfinityにはNumber.POSITIVE_INFINITYとNumber.NEGATIVE_INFINITYの2種類あるからです。

数学でいうところの「正の無限大」と「負の無限大」ですね。

この2つを明確に区別したい場合は以下の様にします。

1 / 0 === Number.POSITIVE_INFINITY; // true
-1 / 0 === Number.NEGATIVE_INFINITY; // true

 

【kotlin】MD5文字列を生成するStringのExtension

kotlinでMD5文字列を生成するStringのExtensionを作った。

ちょっとある用途で使おうと思ったのだけれど、使い道がなくなってしまったのでここに上げておく。

StringExtension.kt:

import java.security.MessageDigest

fun String.toMd5Hex(): String {
    val HEX_CHARS: String = "0123456789ABCDEF"
    val bytes: ByteArray = MessageDigest.getInstance("MD5").digest(this.toByteArray())
    val result: StringBuilder = StringBuilder(bytes.size * 2)
    bytes.forEach {
        val index: Int = it.toInt()
        result.append(HEX_CHARS[index shr 4 and 0x0f])
        result.append(HEX_CHARS[index and 0x0f])
    }
    return result.toString()
}

 

【Perl】マージミスによるヒアドキュメントの落とし穴にご用心

複数人で開発を行うときにGITなどのバージョン管理ツールはとても便利ですが、誰かが変なコードをコミットしていた場合にあとあと関係ない場所でおかしなトラブルが発生する事があります。

こういうケースに実際に遭遇したので備忘録がてらにメモ。

 

問題のコード例: 

#!/usr/bin/perl
use strict;
use warnings;
use utf8;

my $str="";
# IF文A
if (1) {
  $str=<<"EOS";
  print "hoge\n";
}

# IF文B
if (0) {
  $str=<<"EOS";
EOS
  print "fuga\n";
}

exit;

ぱっと見、IF文Bの中は絶対に実行されないデッドコードのように見える。

そのため「hoge」という文字列が表示されるのが想定されるだろう。

だがしかし、実際に出力されるのは「fuga」という文字列だ。

 

IF文Aの中のヒアドキュメントの終了文字「EOS」が欠落しているため、IF文B内部のEOSまでがヒアドキュメントとして扱われてしまう。

 

赤く色をつけた部分が$strのヒアドキュメントの範囲:

my $str="";
# IF文A
if (1) {
  $str=<<"EOS";
  print "hoge\n";
}

# IF文B
if (0) {
  $str=<<"EOS";
EOS
  print "fuga\n";
}

これではIF文Bを延々と数時間眺めていても分からない。

怖い怖い。