動的に並び替えできるRadioButtonのレイアウトを作ってみた



先日、僕の作成したかんたん家計簿でViewの並びかえを行う処理を実装しました。
今回は、その時に使ったソースコードの一部を抜粋して、動的に並び替えできるRadioButtonについて解説したいと思います。

何故ListViewでなくRadioButtonなのか



理由は簡単。ListViewで実装するより、RadioButtonの方が簡単にできると思ったからです…(^^;
ListViewの動的な並び替えはかなり難易度が高そうです。実際ググッてもピンとくる情報がヒットしなかったので、今回はRadioButtonをListViewに見立てて、強引にそれっぽいものを作ってみました。

レイアウト構成

ListViewのようにスクロールできることが前提条件となりますので、
RelativeLayoutの下にScrollView、ScrollViewの下にRadioGroupを配置します。
具体的には次のようにします。

<RelativeLayout>
  <ScrollView>
    <RadioGroup>
    </RadioGroup>
  </ScrollView>
  <LinearLayout>
    < ... 操作するボタン群>
  </LinearLayout>
</RelativeLayout>


並び替えの実現方法

実装は至って単純です。
RadioButtonをレイアウトのRadioGroupから削除、追加するだけで実現できます。


使用するメソッドについて

int getCheckedRadioButtonId()
View findViewById(int id)
View getChildAt(int position)
void removeViewAt(int index)
void addView(View child, int index)

上記メソッドでは、チェックされているRadioButtonの取得、
idを指定したViewの取得、Viewの追加、削除などを行うのに利用します。

void setFocusable(boolean f)
void setFocusableInTouchMode(boolean f)
void requestFocus()

上記はフォーカス遷移に必要なメソッドです。
何故フォーカスの遷移が必要なのか?と言いますと、RadioButtonが画面の外にはみ出たとき、
それに合わせて画面をスクロースする処理が必要になります。
ScorllViewを直接操作する方法が分らなかったので、フォーカスを遷移する事で画面をスクロールさせます。

実際に動かしてみました



ソースコード

選択しているRadioButtonを上に移動させる場合の処理です。

  • チェックしているラジオボタンのViewを取得
  • 一番上に存在するViewも取得
  • 選択しているViewが、一番上のViewなら何もしない
  • そうでなければ、Viewをレイアウトから一旦削除
  • 削除したViewを、インデックスを指定して挿入する

    //すぐ上のラジオを入れ替える
    //一番上の場合は何もしない
    private void swapViewFromUp() {
        
        int id = mRadioLayout.getCheckedRadioButtonId();
        RadioButton r = (RadioButton)mRadioLayout.findViewById(id);
        RadioButton first = (RadioButton)mRadioLayout.getChildAt(0);
        
        if (r != first) {
            
            int equalIdx = 0;
            for (int i = 0; i < mRadioLayout.getChildCount(); i++) {
                
                RadioButton tmp = (RadioButton)mRadioLayout.getChildAt(i);
                if (tmp == r) {
                    
                    equalIdx = i;
                }
            }
            
            if (equalIdx > 0) {
                
                RadioButton tmp = 
                    (RadioButton)mRadioLayout.getChildAt(equalIdx - 1);
                mRadioLayout.removeViewAt(equalIdx - 1);
                mRadioLayout.addView(tmp, equalIdx);
                
                r.setFocusable(true);           // ※1
                r.setFocusableInTouchMode(true);
                r.requestFocus();
                r.setFocusableInTouchMode(false);
                r.setFocusable(false);
            }
        }
    }

※1 やや不自然な気もしますが、SDK1.6だとこうしないとフォーカスが遷移してくれません。しかも、フォーカスを遷移した後、解除してやらないと黄色く反転したままになります。




上記メソッドと殆ど同じ内容ですが、今度は一つ下に移動させる処理です。

    //すぐ下のラジオを入れ替える
    //一番下の場合は何もしない
    private void swapViewFromDown() {
        
        int id = mRadioLayout.getCheckedRadioButtonId();
        RadioButton r = (RadioButton)mRadioLayout.findViewById(id);
        int lastIdx = mRadioLayout.getChildCount() - 1;
        RadioButton last = (RadioButton)mRadioLayout.getChildAt(lastIdx);
        
        if (r != last) {
            
            int equalIdx = 0;
            for (int i = 0; i < mRadioLayout.getChildCount(); i++) {
                
                RadioButton tmp = (RadioButton)mRadioLayout.getChildAt(i);
                if (tmp == r) {
                    
                    equalIdx = i;
                }
            }
            
            if (equalIdx < lastIdx) {
                
                RadioButton tmp = 
                    (RadioButton)mRadioLayout.getChildAt(equalIdx + 1);
                mRadioLayout.removeViewAt(equalIdx + 1);
                mRadioLayout.addView(tmp, equalIdx);
                
                r.setFocusable(true);
                r.setFocusableInTouchMode(true);
                r.requestFocus();
                r.setFocusableInTouchMode(false);
                r.setFocusable(false);
            }
        }
    }





初期化処理では、RadioButtonをクラス内で作成しています。
その際、IDとテキストも一緒にセットしてやります、

    RadioGroup.LayoutParams params = new RadioGroup.LayoutParams(
                RadioGroup.LayoutParams.FILL_PARENT,
                RadioGroup.LayoutParams.WRAP_CONTENT);
        
    RadioButton []radioArray = new RadioButton[arr.length];
    for (int i = 0; i < arr.length; i++) {
        
        radioArray[i] = new RadioButton(this);
        radioArray[i].setLayoutParams(params);
        radioArray[i].setTextColor(0xffffff);
        radioArray[i].setTextSize(22.0f);
        radioArray[i].setGravity(Gravity.LEFT);
        radioArray[i].setText(arr[i]);
        radioArray[i].setId(i);
        
        
        mRadioLayout.addView(radioArray[i]);
    }
    
    radioArray[0].setChecked(true);



ボタンが押されたタイミングで、上記メソッドを呼んであげればRadioButtonを移動させることが出来ますね。
(onClick()など)


それから、いつになるか分かりませんが、ListViewでの動的な並び替えについても調査したいと思います。