[Androidアプリ開発] iOS 7 みたいなラジオボタンを文字を切り抜いて作ってみた
今回は Android で、 drawable と、 RadioButtonクラスをカスタマイズして、iOS7 の UISegmentedControl みたいな ラジオボタン を作ってみます。
iOS7 の UISegmentedControl というのは、下の画像のようなものです。
実装
それでは作っていきます。未選択時の表示のみ drawable で実装します。
選択中のボタンは後ほどjavaで実装するので、drawable では透明にしておきます。
dimens.xml
res/valuesフォルダ 内の dimens.xml に値を追加します。
<resources> <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> <dimen name="radio_corner_radius">4dp</dimen> </resources>
color.xml
res/valuesフォルダ に color.xmlファイル を新規作成します。
<resources> <color name="radio_color">#007aff</color> <color name="radio_tap_color">#40007aff</color> <color name="radio_clear_color">#00000000</color> </resources>
radio_text_color.xml
res/drawable-xxhdpiフォルダ に radio_text_color.xmlファイル を新規作成します。
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 選択時 --> <item android:state_checked="true" android:color="@color/radio_clear_color"/> <!-- 未選択時 --> <item android:color="@color/radio_color"/> </selector>
radio_left.xml
res/drawable-xxhdpiフォルダ に radio_left.xmlファイル を新規作成します。
<?xml version="1.0" encoding="UTF-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 選択時 --> <item android:state_checked="true"> <shape android:shape="rectangle"> <solid android:color="@color/radio_clear_color" /> </shape> </item> <!-- タップ時 --> <item android:state_pressed="true" > <shape android:shape="rectangle"> <corners android:bottomLeftRadius="@dimen/radio_corner_radius" android:topLeftRadius="@dimen/radio_corner_radius" /> <solid android:color="@color/radio_tap_color" /> <stroke android:width="1dp" android:color="@color/radio_color" /> </shape> </item> <!-- 未選択時 --> <item> <shape android:shape="rectangle"> <corners android:bottomLeftRadius="@dimen/radio_corner_radius" android:topLeftRadius="@dimen/radio_corner_radius" /> <solid android:color="@color/radio_clear_color" /> <stroke android:width="1dp" android:color="@color/radio_color" /> </shape> </item> </selector>
radio_center.xml
res/drawable-xxhdpiフォルダ に radio_center.xmlファイル を新規作成します。
<?xml version="1.0" encoding="UTF-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 選択時 --> <item android:state_checked="true"> <shape android:shape="rectangle"> <solid android:color="@color/radio_clear_color" /> </shape> </item> <!-- タップ時 --> <item android:state_pressed="true" > <shape android:shape="rectangle"> <solid android:color="@color/radio_tap_color" /> <stroke android:width="1dp" android:color="@color/radio_color" /> </shape> </item> <!-- 未選択時 --> <item> <shape android:shape="rectangle"> <solid android:color="@color/radio_clear_color" /> <stroke android:width="1dp" android:color="@color/radio_color" /> </shape> </item> </selector>
radio_right.xml
res/drawable-xxhdpiフォルダ に radio_right.xmlファイル を新規作成します。
<?xml version="1.0" encoding="UTF-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 選択時 --> <item android:state_checked="true"> <shape android:shape="rectangle"> <solid android:color="@color/radio_clear_color" /> </shape> </item> <!-- タップ時 --> <item android:state_pressed="true" > <shape android:shape="rectangle"> <corners android:bottomRightRadius="@dimen/radio_corner_radius" android:topRightRadius="@dimen/radio_corner_radius" /> <solid android:color="@color/radio_tap_color" /> <stroke android:width="1dp" android:color="@color/radio_color" /> </shape> </item> <!-- 未選択時 --> <item> <shape android:shape="rectangle"> <corners android:bottomRightRadius="@dimen/radio_corner_radius" android:topRightRadius="@dimen/radio_corner_radius" /> <solid android:color="#00000000" /> <stroke android:width="1dp" android:color="@color/radio_color" /> </shape> </item> </selector>
CustomRadioButton.java
選択中の表示の描画処理を作成します。
drawable で文字の領域を切り抜く方法はよくわからなかったので、ここだけjavaでコーディングします。
import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.RoundRectShape; import android.util.AttributeSet; import android.widget.RadioButton; public class CustomRadioButton extends RadioButton { public static final String POSITION = "position"; public static final String POSITION_LEFT = "left"; public static final String POSITION_CENTER = "center"; public static final String POSITION_RIGHT = "right"; /** ボタンカラー */ private int mColor; /** 角丸半径 */ private float mRadius; /** ポジション(左側、中央、右側) */ private String mPosition = POSITION_LEFT; public CustomRadioButton(Context context, AttributeSet attrs) { super(context, attrs); // layout.xml で指定したポジション(左側、中央、右側)を取得して保持します。 mPosition = attrs.getAttributeValue(null, POSITION); // あらかじめ値を取得しておきます。 mRadius = getResources().getDimension(R.dimen.radio_corner_radius); mColor = getResources().getColor(R.color.radio_color); } @Override protected void onDraw(Canvas canvas) { if (isChecked()) { // 背景画像を生成します。 Bitmap bgBm = createBg(); // マスク画像を生成します。 Bitmap maskBm = createMask(); // マスク処理を行います。 mask(bgBm, maskBm, canvas); } else { // 未選択時はdrawableで表示します。 super.onDraw(canvas); } } private Bitmap createBg() { Bitmap bgBm = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); float[] outerR = null; if (POSITION_LEFT.equals(mPosition)) { outerR = new float[] { mRadius, mRadius, 0, 0, 0, 0, mRadius, mRadius }; } else if (POSITION_CENTER.equals(mPosition)) { outerR = new float[] { 0, 0, 0, 0, 0, 0, 0, 0 }; } else if (POSITION_RIGHT.equals(mPosition)) { outerR = new float[] { 0, 0, mRadius, mRadius, mRadius, mRadius, 0, 0 }; } ShapeDrawable shapeDrawable = new ShapeDrawable(new RoundRectShape( outerR, null, null)); shapeDrawable.setBounds(0, 0, getWidth(), getHeight()); shapeDrawable.getPaint().setAntiAlias(true); shapeDrawable.getPaint().setStyle(Paint.Style.FILL); shapeDrawable.getPaint().setColor(mColor); Canvas bgCv = new Canvas(bgBm); shapeDrawable.draw(bgCv); return bgBm; } private Bitmap createMask() { Bitmap maskBm = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint.setTextSize(getTextSize()); textPaint.setTextAlign(Paint.Align.CENTER); new Canvas(maskBm).drawText(getText().toString(), getWidth() / 2.0f, getBaseline(), textPaint); return maskBm; } private void mask(Bitmap baseBm, Bitmap maskBm, Canvas canvas) { Bitmap maskedBm = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas maskedCv = new Canvas(maskedBm); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setFilterBitmap(false); maskedCv.drawBitmap(baseBm, 0, 0, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); maskedCv.drawBitmap(maskBm, 0, 0, paint); paint.setXfermode(null); canvas.drawBitmap(maskedBm, 0, 0, new Paint(Paint.ANTI_ALIAS_FLAG)); } }
activity_main.xml
最後に res/layoutフォルダ の activity_main.xmlファイル を修正して、
作成した drawable が反映されるように設定したボタンを配置します。
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <RadioGroup android:id="@+id/radioGroup1" android:layout_width="wrap_content" android:layout_height="30dp" android:layout_marginTop="20dp" android:checkedButton="@+id/testleftBtn" android:orientation="horizontal" > <jp.classmethod.sampleradiobuttonmask.CustomRadioButton android:id="@+id/testleftBtn" android:layout_width="80dp" android:layout_height="match_parent" position="left" android:background="@drawable/radio_left" android:button="@null" android:gravity="center" android:text="左側" android:textColor="@drawable/radio_text_color" /> <jp.classmethod.sampleradiobuttonmask.CustomRadioButton android:layout_width="80dp" android:layout_height="match_parent" position="center" android:background="@drawable/radio_center" android:button="@null" android:gravity="center" android:text="中央" android:textColor="@drawable/radio_text_color" /> <jp.classmethod.sampleradiobuttonmask.CustomRadioButton android:layout_width="80dp" android:layout_height="match_parent" position="right" android:background="@drawable/radio_right" android:button="@null" android:gravity="center" android:text="右側" android:textColor="@drawable/radio_text_color" /> </RadioGroup> </RelativeLayout>
これで完成です。Android2.3系で動かす場合は、drawable で設定した下側の角丸が左右逆になってしまうのでご注意ください。
もし対応する場合は、drawable-xxhdpi-v11フォルダを作成して、Android2.3以前と以後でファイルを分けるようにしてください。
動作確認
では、動かしてみます。
はい。できました。
背景が薄いグレーなのでわかりにくいですが、ちゃんと文字の部分で切り抜かれていています。
ではでは。