17
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

カスタムビューでボタンを作る

Posted at

例えば以下のようなボタンを作りたいとします。

custom_button_view.png

デフォルトのボタンでもアイコンは付けられるみたいですが、複雑なレイアウトを作るのはできないみたいです。
なので、カスタムビューを使って実現します。

レイアウト作成

まずはボタンにしたいカスタムビューのレイアウトファイルを作成します。

custom_button_view_layout.png

ルートレイアウトはなんでもいいのですが、今回はConstraintLayoutを使用しました。

次にこのレイアウトをボタンのような外見にします。
手順はルートレイアウトのstyle属性にボタンのスタイルを設定するだけです。

<!-- view_custom_buttom.xml -->

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    style="@style/Widget.AppCompat.Button">
    <!-- ↑ここ! -->
    
    ...

</android.support.constraint.ConstraintLayout>

これで、見た目はボタンそっくり、押したときのリップルもちゃんと表示されデフォルトと瓜二つになりました。

custom_button_view_style.png

カスタムビュークラス作成

次にカスタムビューのクラスを作成します。

// CustomButtonView.java

public class CustomButtonView extends ConstraintLayout {
    public CustomButtonView(Context context) {
        this(context, null);
    }

    public CustomButtonView(Context context, AttributeSet attrs) {
        super(context, attrs);

        init();
    }

    private void init() {
        LayoutInflater.from(getContext()).inflate(R.layout.view_custom_button, this);
    }
}

完成?

これでいったんActivityに埋め込んでみましょう。

<!-- activity_main.xml -->

....

<***.***.CustomButtonView
        android:id="@+id/custom_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

...

onClickリスナーもセットしておきます。

// MainActivity.java

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // インスタンスを取得
        CustomButtonView customButton = findViewById(R.id.custom_button);
        
        // onClickにリスナーを設定
        customButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this, "Click!", Toast.LENGTH_LONG).show();
            }
        });
    }
}

さて、これで上手くいきそう思えましたが、動かしてみるとカスタムボタンを押してもonClickリスナーが呼ばれません。

調べてみると以下の記事が参考になりました。

CustomViewのOnClickが反応しない時の対処方法 - Qiita

どうやらカスタムビューの場合はOnClickの処理は自分で書かないといけないみたいです。

CustomButtonViewにonClickの処理を追加

上記の記事を参考に以下を追加します。

// CustomButtonView.java

public class CustomButtonView extends ConstraintLayout {
    ...

    @Nullable
    private OnClickListener listener;

    @Override
    public void setOnClickListener(@Nullable OnClickListener l) {
        this.listener = l;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_UP) {
            if (listener != null) {
                post(new Runnable() {
                    @Override
                    public void run() {
                        listener.onClick(CustomButtonView.this);
                    }
                });
            }
        }

        return super.dispatchTouchEvent(ev);
    }
}
custom_button_view_click.png

これでonClickが走るようになりました。

ボタンの範囲外にずらしたときの動作を修正

これで完成のように思えますがデフォルトのボタンとは微妙に異なる動作があります。

  • ボタンをタップ
  • ボタンを押したまま指をボタンの範囲外にずらす
  • 画面から離す

上の手順を行うとデフォルトのボタンではonClickは起こらないのですが、作成したCustomButtonViewではイベントが発生してしまいます。

これを修正するには以下のようにACTION_UPのときの座標がボタンの範囲内にあるかをチェックして、範囲内の場合にだけリスナーを呼ぶようにします。

// CustomButtonView.java

class CustomButtonView extends ConstraintLayout {
    ...
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_UP) {
            // 範囲内のチェックを追加
            if (listener != null && checkInside(ev)) {
                post(new Runnable() {
                    @Override
                    public void run() {
                        listener.onClick(CustomButtonView.this);
                    }
                });
            }
        }

        return super.dispatchTouchEvent(ev);
    }

    private boolean checkInside(MotionEvent ev) {
        int[] point = new int[2];

        getLocationOnScreen(point);

        int x = point[0];
        int y = point[1];

        return (ev.getRawX() >= x && ev.getRawX() <= x + getWidth()) &&
                (ev.getRawY() >= y && ev.getRawY() <= y + getHeight());
    }
}

完成!

これで、見た目も動作もデフォルトのボタンとほぼ同様なカスタムビューボタンを作ることができました。

17
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?