mikulogic-tomo’s diary

NagaokaMikunologicalLive主催者のブログです。色んなこと書きます。

Android開発日記 ラジオボタンを使わずにラジオボタンを作る

こんにちは、ともです。

 

 今回は、ラジオボタンを使わずにButtonにラジオボタンの機能を持たせる方法です。

Android開発関連の初記事)

 

 普通はラジオボタンを使えばいいんですけど、実装してて後から普通のボタンからラジオボタンに変更したいってことがありました。その時は既にボタンを使って色々と実装していたので、このコードを変更するのは大変だな・・・と思ってこの形を取りました。

つまり、既にあるボタンにラジオボタンみたいな機能を持たせたって感じです。

ただ、後付けなのでコードの見た目がダサいです。嫌な方は最初からラジオボタンを使ってください。
また、調べてもそんな方法は見当たらなかったので、書いておきます。
(まあ、ラジオボタンを使えってことですね)



さて、ラジオボタンでは今選択しているものを表す印があると思います。
f:id:mikulogi-tomo:20180101142639p:plain
左図:RadioButtonを表示
右図:Buttonを表示

 
今回は左図のように、この丸いやつではなく、右図のようにボタンの背景を変更することによって選択されたことを表現します。

ボタンの背景を動的に変更させるためにはdrawableとViewクラス内のSelectedを使います。
app>res>drawableフォルダの中にselectorタグを持ったxmlを1つと、shapeタグを持ったxmlを2つ作ります。(AndroidStudio-Androidでのフォルダ階層)
xml」のファイル名は任意でOKです。
(今回の例では以下)
select_style.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_selected="false"
        android:drawable="@drawable/default_style"/>
    <item
        android:state_selected="true"
        android:drawable="@drawable/pressed_style"/>
</selector>

default_style.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#3CB371" />
    <corners android:radius="8dp"/>
</shape>

pressed_style.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#FFFF00" />
    <corners android:radius="8dp"/>
</shape>


メインとなるレイアウトのxml配置するボタンの背景(background)を以下のように設定します。
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    tools:context="com.example.○○○.buttonselect.MainActivity">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/select_style"
        android:text="@string/button1"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:background="@drawable/select_style"
        android:text="@string/button2"/>

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:background="@drawable/select_style"
        android:text="@string/button3"/>

    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:background="@drawable/select_style"
        android:text="@string/button4"/>

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:text="test"/>

</LinearLayout>

上記のようにxmlファイルを作成したら、後はメインのコード上で
「(Viewクラス).isSelected()」でselectフラグを確認でき、
「(Viewクラス).setSelected()」でselectフラグを変更できます。

しかし、ラジオボタンとは異なり、一つ選択されたら自動的に選択されていないボタンが変更されることはありません。
そのため、押されたボタン以外のselectフラグをfalseにしてあげる必要があります。
onClick関数は押されたボタンのみを通知するので、予め全てのボタンを配列にコピーしておいて、押されたボタン以外のselectフラグをfalseにしてあげます。

mainActivity.java

package com.example.○○○.buttonselect;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity{

    private Button btn1;
    private Button btn2;
    private Button btn3;
    private Button btn4;
    //コピー用の配列を用意
    private Button btn[] = new Button[4];
    private TextView textView;


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

        //activity_mainレイアウトとMainActivityを関連付ける
        btn1 = (Button)findViewById(R.id.button1);
        btn2 = (Button)findViewById(R.id.button2);
        btn3 = (Button)findViewById(R.id.button3);
        btn4 = (Button)findViewById(R.id.button4);
        textView = (TextView)findViewById(R.id.text);

        //クリックリスナーを登録する
        btn1.setOnClickListener(btnOCL);
        btn2.setOnClickListener(btnOCL);
        btn3.setOnClickListener(btnOCL);
        btn4.setOnClickListener(btnOCL);

        /*
        * 既にあるボタンを配列の中にコピーする
        この部分でfor文などが使えないため、どうしてもコードがダサくなる
   */
        btn[0] = btn1;
        btn[1] = btn2;
        btn[2] = btn3;
        btn[3] = btn4;

    }


    View.OnClickListener btnOCL = new View.OnClickListener() {
        /*
        * 今回はリスナーを一つにして処理を行っているが
          下記onClick内の処理を他の関数にしてしまえば、
        * 現状のonClick関数に影響せずに実装することが出来る
  */
        @Override
        public void onClick(View v) {
            for(int i=0; i<btn.length; i++){
                if(v == btn[i]){//押されたボタンのみsetSelectedをtrueにする
                    btn[i].setSelected(true);
                    textView.setText("Button" + String.valueOf(i+1));
                }else{//押されていないボタンはsetSelectedをfalseにする
                    btn[i].setSelected(false);
                }
            }
        }
    };
}

注意する点として、
・初期状態ではselectが全てfalseになっていること
xmlにある「android:state_selected」以外の状態フラグ(例えば、android:state_pressedなど)が入ると意図しない動作になること
特に2つ目はハマりました・・・
これに関しては以下のブログにある内容の「4. selector 要素の子要素は、上から順に評価され、最初に一致したものが使われる」が参考になると思います。
listSelector のハマりどころ - Qiita



他にもっといい方法があるかもしれませんが、この方法のいいところはリスト状以外のラジオボタンを作れるところにあります。
ラジオボタンを使用すると、どうしてもリスト形式になります。(リスト状以外のラジオボタンもあるのかもしないですが)
好きに配置して、好きなデザインのボタンにラジオボタンの機能を持たせられるのはメリットなのかなと思っています。
ただ、ラジオボタンにある便利な機能(リスナーとか)は無いのでケースバイケースなのは間違いないですね


参考
【Android】selecterを使ってみる - It’s now or never
Android drawableは画像を入れておくだけじゃない - Qiita