石をひっくり返したい

スポンサーリンク
スポンサーリンク

いよいよコアな部分だー

まずは一方向だけのを作ってみたいな。

スポンサーリンク

考え方

は、から拝借しつつ参考にした。

でも結局番兵とかがよく分かんなかった。

いいんだ、作りながら考えるんだ。

タップされた位置を取得

GridLayoutのxmlを書いた時点でrow, columnという馴染み深い単語を目にしていたので、てっきり簡単にTextViewの位置を取得できるもんだと思っていたら、全然見つからないでやんの。

view.getid()は意味の分からん数字だし、getRow()的なメソッドも見当たらない。

唯一見つけれた

GridLayout.LayoutParams params = (GridLayout.LayoutParams) tv.getLayoutParams();
GridLayout.Spec p = params.rowSpec;

GridLayout.LayoutParamsで宣言して子View.getLayoutParams()使ってrowSpec使う、みたいなのをやってみたけど、rowSpecの取扱がよく分からず…。

最終的にTextViewのxmlに

android:tag="00"

とTagを設定した。64個。手動で。そして、これをInteger.ParseInt(tv.getTag().toString)してRC代わりに使ったろうと思う。

あー疲れた、絶対もっといいやり方があるんだろうなぁ。あとなんで0始まりにしちゃったかなぁ。

関数をいっぱい作る

どう書こう。処理の流れに沿って書いてく。

putStone()

public void putStone(TextView tv){
    String stone = tv.getText().toString();
    int Row = SplitStrRCtoIntRow(TagToString(tv));
    int Column = SplitStrRCtoIntColumn(TagToString(tv));

    parentView = (View) tv.getParent();

    if(!stone.equals("")){
        Toast.makeText(this.con,"そこに石は置けません", Toast.LENGTH_SHORT).show();
        return;
    }

    CheckReversibleUP(tv,Row,Column);

こうなった。

タップされたTextViewのTagを取得。TagからRow,Columnに分けてあげてCheckReversibleUPにviewとともに渡してあげる。

親Viewを取得する

初めは取得したTextViewをそのまま渡してて、findViewWithTagやっても取得できず詰まってた。で、

parentView = (View) tv.getParent();

と、親View取得してからやるもんだと分かってからが捗った。parentViewはBoardクラスのメンバ変数にして、どの関数からでも使えるようにした。

tagをRowとColumnに分ける
private String TagToString(TextView v){
    String s;
    s = v.getTag().toString();
    return s;
}
private Integer SplitStrRCtoIntColumn (String s){
    int c;
    c = Integer.parseInt(s.substring(1));
    return c;
}
private Integer SplitStrRCtoIntRow (String s){
    int r;
    r = Integer.parseInt(s.substring(0,1));
    return r;
}

TagがObject型だったのでStringに変換する。それをさらに1文字目と2文字目に分割する。戻り値が2個の場合の関数の作り方がわかんない、のでこうなった。関数作るの楽しい。でも作り過ぎかなぁ。

CheckReversibleUP()

private void CheckReversibleUP(TextView v,int r,int c){
    TextView tv;
    int StoneColor;
    int StoneCount = 0;

    for(int i = (r - 1) ; -1 < i ; i-- ){
        tv = getTextViewFromTag(i,c);

        if(!existStone(tv)){
            break;
        }

        StoneColor = getStoneColor(tv);

        if(PlayerTurn == StoneColor && StoneCount < 1){
            break;
        }

        if(PlayerTurn != StoneColor){
            StoneCount++;
            continue;
        }

        if(PlayerTurn == StoneColor){
            for(int j = (i + 1) ; (i + StoneCount + 1) > j ; j++){
                tv = getTextViewFromTag(j,c);
                setStoneToCell(tv);
            }

            setStoneToCell(v);
            PlayerTurn = PlayerTurn * (-1);
        }
     }
}

Checkと言いつつ、引っ繰り返すところまでやっている。良くないね。まぁそれは置いといて。

for文でタップ位置のRCを元に、上方向の石を調べていく。javaのfor文初めて書いたぜ。

上方向の場合、列は変わらないので行だけ-1していく。初期値を(タップされた行番号-1)にして、カウンタ変数が盤面外にあたる-1になったら終わり。

  1. 石があるか調べる。
  2. 石の色を調べる。
  3. 違う色の石だったらStoneCountに+1。
  4. 同じ色の石が出てきたら間を引っ繰り返す。

という流れ。まぁだらだら書いてこ。

getTextViewFromTag()
private TextView getTextViewFromTag (int a, int b){
    String s = String.valueOf(a) + String.valueOf(b);
    TextView res = parentView.findViewWithTag(s);
    return res;
}

Tagをがっちゃんこして指定Viewを取ってくる。ここで親Viewが必要だった。ずっと何度もできないできないと悩んで、これは詰んでしまったか…と諦めかけていた時に、デバッグしてみたらちゃんとViewを取れてないことを発見。ずっと子Viewの中を探していたことになる。なるほど、そりゃ無いわな、とホッとした思い出。

existStone()
private boolean existStone(TextView v){
    return !(v.getText().toString().equals(""));
}

この書き方IDEに教えてもらった。あいついいヤツだ。TextViewが空白でなかったらtrueを返す。

getStoneColor()
private int getStoneColor(TextView v){
    int sc = 0;

    switch (v.getText().toString()){
        case("●"):
            sc = BLACK;
            break;
        case("○"):
            sc = WHITE;
    }
    return sc;
}

色を数値で扱ったほうが具合が良いらしいので作ってみた。確かに手番の交換がPlayerTurn*(-1)するだけなのでスムーズ。

setStoneToCell
private void setStoneToCell(TextView v){
    switch(PlayerTurn){
        case(BLACK):
            v.setText("●");
            break;
        case(WHITE):
            v.setText("○");
            break;
    }
}

手番によって置く石を変える。引っ繰り返す石も新たに置く石も同じ色なので使い回せる。ただこの関数2回使ってるので減らしたい。

引っ繰り返す処理
if(PlayerTurn == StoneColor){

    setStoneToCell(v);

    for(int j = (i + 1) ; (i + StoneCount + 1) > j ; j++){
        tv = getTextViewFromTag(j,c);
        setStoneToCell(tv);
    }

    PlayerTurn = PlayerTurn * (-1);
    break;
}

同じ色の石をめっけたら戻りつつ石を引っ繰り返す。そんで手番を変える。

ここのif文でめっちゃ怒られてて、

Condition ‘PlayerTurn == StoneColor’ is always ‘true’ less… (Ctrl+F1)
This inspection analyzes method control and data flow to report possible conditions that are always true or false, expressions whose value is statically proven to be constant, and situations that can lead to nullability contract violations.
Variables, method parameters and return values marked as @Nullable or @NotNull are treated as nullable (or not-null, respectively) and used during the analysis to check nullability contracts, e.g. report NullPointerException (NPE) errors that might be produced.
More complex contracts can be defined using @Contract annotation, for example:
@Contract(“_, null -> null”) — method returns null if its second argument is null @Contract(“_, null -> null; _, !null -> !null”) — method returns null if its second argument is null and not-null otherwise @Contract(“true -> fail”) — a typical assertFalse method which throws an exception if true is passed to it
The inspection can be configured to use custom @Nullable
@NotNull annotations (by default the ones from annotations.jar will be used)

こんな。ここがちょっと理解できなくて、なんで常にtrueになっとるぞと言われるんだろう。breakとcontinueで離脱してるから…?てことはここのif文いらないのかな…。でもあったほうがあとで分かりやすいんじゃないかなぁ…。だから無視してる。

感想と展望

CheckReversibleUPはもうちょいスッキリ書けるんじゃなかろうか。なんというか置き石可能かどうかの調査と、実際に石を返す処理をもうちょっとキレイに分離させたい。けど、せっかくできたのを崩すのも惜しい。

ていうか、楽しくなっていっぱい書いちゃったけど、内部での処理でこんなに関数分割していいもんなんだろうか。クラス外部から利用する関数putStoneの一つしかないもんな。あと関数書く順もわからん。今は分けたかったらてきとーな位置に書いてる。から時々あれ?あの関数どこだ?ってなる。

あと、Boardが肝心の盤面の状態を保持してないような。画面のTextViewらがその役割を担っちゃってるし、てきとーだけどBoard.getStoneArrayとかBoard.getStateみたいなんがあった方がいいのかなぁ。なにに使うかは決まってないけど。なんかそっちのがかっこよそう。

とりあえず、上方向はできた!嬉しいなぁ。画面的には動きが少なくなってきているけど、思ったような制限を作れてるってことだもんな。あと7個おんなじようなのを書けばいいんだ。7個、7個ね。7個かぁ。めんどいな。正直おんなじような処理なんだし、まとめて書くような方法がないもんかね。どうなのかね。調べてみよ。

参考

findViewWithTag()の使い方
Androidでviewをidではなくtagを使って特定する、findViewWithTag()の使い方で詰まったのでメモ。 View.findViewWithTag()は、リファレンスに Look for a child view with the given...
数値から文字列への変換 - ラッパークラスを使った文字列と数値の変換 - Java入門
様々な数値から文字列へ変換する方法です
Android Tips #34 Android 2.1 から GridLayout を使う | Developers.IO
GridLayout が Android 2.1 から使える! GridLayout は Android 3.0 (APIレベル11) で新たに導入されたレイアウトです。View を Grid 式にレイアウトすることがで …
GridLayoutの子Viewから現在配置されているColumnとRowを取得する方法
タイトルのとおりです。 GridLayout上のViewを入れ替えるために、そのViewのColumnとRowを動的に取得したいです。 GridLayout.LayoutParamsなどから取得できるかと思ったのですが、そのようなメソッドも見つかりません。 座標から計算したり、Listenerにフィールドとして持た...
[Android] Button の onClick イベントで親の View を取得する
Button の onClick イベントで親の View を取得するメモ ボタンを押しても onClick で親Viewの情報が取れなくなったので、何でかなーとか思っていたら、レイアウトを見直していて、構成変わっていたのを忘れていた。 ソースコードbutton.setOnClickListener(new OnCli...

コメント

タイトルとURLをコピーしました