[Android/Java] 버튼 이벤트의 베스트 실장 패턴을 모색

Android Studio 4.1.3 (windows 버전)에서 작동 확인
Java에서 구현 중심의 이야기입니다.

Android에서 버튼 이벤트를 구현하는 방법에는 여러 가지가 있습니다.
여러분은 어떤 실장 패턴을 채용하고 있습니까?
이미 여러분이 같은 내용을 정리해주고 있습니다만
구현에 헤매는 분도 있을지도 모르기 때문에 굳이 이쪽으로 정리하고 있습니다

Android Developer의 Button 설명은 다음과 같습니다.

findViewById()로 버튼을 가져와 직접 setOnClickListener()로 구현 (익명 클래스로 구현)



Activity/Fragment에서 구현 방법이 거의 동일하기 때문에
onCreate()당 다음과 같은 구현을 하는 패턴이 많은 것은 아닐까요?
나도 뇌사 상태에서 자주 이 패턴을 코피페로 구현해 버리고 있습니다

자바
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    }
});

Fragment의 경우 다음과 같은 구현 패턴이 많지 않을까요?

자바
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    final View view = inflater.inflate(R.layout.fragment_main, container, false);
    final Button button = view.findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
        }
    });
    return view;
}

클래스에 View.OnClickListener를 상속하는 패턴



버튼의 이벤트 처리를 정리하고 싶은 경우는 이쪽을 사용하는 경우도 있습니다
비슷한 패턴으로 이벤트 처리를 다른 클래스로 설정하여 setOnClickListener()로 설정하는 패턴도 있습니다.

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

        findViewById(R.id.button).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.button) {
        }
    }
}

Layout/Design Editor에서 구현



VC#에서 Form 애플리케이션이나 iOS 등에서 storyboard에서 구현 경험이 있다면
어쨌든 Design Editor에서 이벤트 구현하지 않으면 행의가 나쁜 생각이 들고 기분 나쁘게 느낄지도 모릅니다

이 방법은 Fragment의 소스에 직접 이벤트를 구현할 수없는 것이 불편합니다.
※무리하게 Fragment를 지정했을 경우는 앱이 크래쉬 합니다
  • 이벤트를 추가하고 싶은 버튼의 Attributes onClick에 설정하고 싶은 이벤트의 메소드명 (여기에서는 onClickButton)을 입력
  • Code를 열어 이벤트를 설정한 Button의 android:onClick의 부분에서 windows의 경우는 Alt+Enter등으로
    Create onClick event handler를 선택합니다.
  • 설정한 Button이 있는 Activity나 Fragment의 부모의 Activity를 선택합니다.
    자동으로 이벤트 메소드가 추가되었습니다.


  • (덤) Kotlin의 경우



    Kotlin의 경우 깔끔한 코드로 구현할 수 있습니다.

    Kotlin
    findViewById<Button>(R.id.button).setOnClickListener {}
    

    Jetpack ViewBinding을 채택하여 setOnClickListener()로 구현



    findViewById()는 사용하지 않고 다음과 같습니다.
    ViewBinding에 대해서는 아래 등을 참조하십시오.


    자바
    binding.button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
        }
    });
    

    Jetpack DataBinding으로 구현



    아래를 참조하십시오.

    그래서 최고의 패턴은?



    일관성을 얻거나 사례별로해야 할지 모색 중입니다 ...

    (시작) Jetpack Navigation에서 Layout / Design Editor에서 이벤트를 설정하면서 Fragment에서 이벤트 처리



    [주의] 문제가없는 구현인지 충분히 검증 할 수 없습니다.
       이 구현 패턴을 추천하는 것은 아닙니다

    뭔가 좀 더 깔끔한 구현을 할 수 없는지 시도해 보았습니다.
    Navigation의 경우, NavHost에서 실제로 표시되는 Fragment에 대해 Callback을 하면 좋을 것 같다

    [준비]
    NavHost에 MainFragment/NextFragment를 추가하고 MainFragment→NextFragment→MainFragment...로 전환하도록 지정
    각 Fragment에 Button을 하나 추가하고 onClick에 MainActivity에 정의된 onClickButton을 등록
    각 Fragment가 View.OnClickListener를 상속받습니다.
    각 Fragment에 ViewBinding 구현
    ※ 실제로 사용하는 경우는 null 체크나 Button id 체크 등 적절한 구현이 필요합니다
    ※ Jetpack Navigation의 safeargs를 적응하지 않은 코드입니다
    safeargs의 구현을 알고 싶다면 아래를 참조하십시오.


    MainActivity.java
    // 表示されているFragmentのonClick()を呼び出す
    public void onClickButton(View view) {
        NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager().getFragments().get(0);
        for (Fragment fragment : navHostFragment.getChildFragmentManager().getFragments()) {
            if (fragment.isVisible()) {
                ((View.OnClickListener)fragment).onClick(view);
            }
        }
    }
    

    ※ navHostFragment.getChildFragmentManager().getFragments()의 size()는 항상 1과 같습니다.
    사양을 정확하게 이해할 수 없어서 죄송합니다.

    MainFragment.java
    public class MainFragment extends Fragment implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            // ボタンを押したらNextFragmentに遷移
            Navigation.findNavController(binding.getRoot()).navigate(R.id.action_mainFragment_to_nextFragment);
        }
    }
    

    NextFragment.java
    public class NextFragment extends Fragment implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            // ボタンを押したらMainFragmentに遷移
            Navigation.findNavController(binding.getRoot()).navigate(R.id.action_nextFragment_to_mainFragment);
        }
    }
    

    좋은 웹페이지 즐겨찾기