onClick, LongClick, onTouch 동시 구현 한 결과인테 평가 부탁드립니다.

조회수 4251회

제가 지금 간단한 메모장 어플 만들기로 안드로이드 프로그래밍을 기초부터 공부하고 있습니다.

메인 액티비티에 listview를 객체로 만들고 그 리스트뷰가 adapter를 설정합니다. 어뎁터 내부에서는 사요자가 입력한 메모들을 리스트뷰의 아이템으로 추가하여 화면에 보여주게 됩니다.

여기서 제가 터치 이벤트, 온클릭 이벤트 등을 추가하는데 처음에는 메인액티비티 클래스의 listview.setOnItemClickListener와 listview.setLongClick~~~ 이걸 등록하고, itemView 클래스 안에서는 public boolean onTouchEvent를 구현하였었습니다.(리니어 레이아웃을 상속받음)

그런데 클릭, 롱클릭, 터치 이 세가지가 동시에 작동을 잘 하지 않아서 여러 구글링을 통해 GestureDetector.OnGestureListener 를 통해 제스처를 사용하여 메인액티비티의 내용은 제거하고 itemView 안에서만 구현하였습니다. 그 결과가 밑의 코드입니다.

itemView 클래스의 일부분(터치 구현 부분)

  public boolean onTouchEvent(MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public void onShowPress(MotionEvent e) {
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {

        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        X_down = e1.getX();
        X_up = e2.getX();
        if (!isSwiped) {
            if ((X_down - X_up) > 0) {
                button.setVisibility(View.VISIBLE);
                button.startAnimation(translateLeftAnim);
                isSwiped = !isSwiped;
            }
        } else {
            if ((X_down - X_up) < 0) {
                button.startAnimation(translateRightAnim);
                button.setVisibility(View.INVISIBLE);
                isSwiped = !isSwiped;
            }
        }
        return true;
    }

    @Override
    public void onLongPress(MotionEvent e) {
        AlertDialog dialog = createDialogBox(textView);
        dialog.show();
    }

내부 동작은 아직 세세하게 정하진 않았습니다. toast나 log를 활용하여 정상적인 동작만 확인하였습니다.

이렇게 해서 버튼이 클릭, 롱클릭, 옆으로 드래그하여 삭제버튼 나오게 하기 등을 작동하는 것을 확인하였습니다.

이제 문제가 발생하는데, 왼쪽으로 스와이프(드래그)하면 삭제버튼이 나오는데, 삭제버튼을 클릭할 경우 메인 액티비티로 클릭이 되었다는 것을 전달해야하고, 삭제할 메모가 어떤것인지를 알려줘야 하는데 이 방법을 모르겠습니다. 몇가지 생각해 본것은 있는 데

  1. StartActivityForResult를 해볼까 했는데 itemView에서는 액티비티를 상속한 것이 아니라서 되지않더군요...
  2. 메인 액티비티에서 static 함수를 만들고 itemView 클래스에서 삭제버튼을 눌렀을 때 이 함수를 호출해서 해당 메모아이템을 전달해줘서 삭제하는 방법을 생각해 봤지만, static 메소드 안에서는 static 변수만 사용할 수 있다고 배운 것 같아서 아직 시도는 안해봤습니다.
  3. otto라는 것을 사용하면 이벤트 드리븐 방식으로 구현할 수 있다고 하는데, 안드로이드 어플이 돌아가는 것을 공부하기위해 기초부터 만드는 중이라서 라이브러리는 최대한 사용하지 않으려고 합니다.

이러한 것이 생각나는데, 혹시 다른 방법이 있을 까요?

아 그리고 혹시 제가 지금 터치 이벤트에 대한 것을 itemView에 적용을 해놨는데 이방법도 있지만 메인 액티비티의 listview.set~~listener를 통해 하는 방법도 있는데, 어떤 방법이 더 좋은가요? mainActivity에 리스너를 등록하는 방법은 위 문제가 쉽게 해결 될거 같지만....

  • (•́ ✖ •̀)
    알 수 없는 사용자

2 답변

  • otto라이브러리를 추천하고 싶지만 라이브러리를 사용하고 싶지 않다고 하셨으니 다른 방법을 설명해드릴게요.

    디자인패턴중에 옵저버패턴 이라고 있습니다.

    이 패턴을 이용해서 구현하시면 되는데요.(패턴설명은 따로 하지 않을게요. 위의 링크를 참고하세요.)

    itemView클래스는 Observerble이 되고 메인액티비티가 Observer가 되겠네요. 자바에서는 Observer패턴을 쉽게 구현할수 있도록 Observable과 Observer API를 제공하고 있습니다. 클릭이벤트나 터치이벤트가 발생될때 notifyObserver()를 날리면 메인액티비티에 노티가 날라와서 해당이벤트를 감지할 수가 있습니다.

    위의 옵저버패턴이 너무 무거운것 같으면 가장 간단한 방법으로는 interface를 이용해서 본인만의 이벤트리스너같은것을 만들어서 호출하는 방식도 있어요. 코드로 간략하게 설명드리자면 아래와 같은 식입니다.

    //itemView클래스에 리스너 하나를 만드세요.리스너이름은 제 마음대로 정의했습니다.
    public interface MyItemClickListener {
            void onDeleteButtClick(int index); //삭제버튼을 클릭했을때 호출할 함수
    }
    
    //그리고 itemView클래스에 리스너를 set해주는 메소드를 하나 만드세요. 메인액티비티에서 set을 해줄거에요.  
    MyItemClickListener listener;
    public void setMyItemClickListener (MyItemClickListener listener) { 
        this.listener = listener;
    }
    
    그리고 삭제버튼클릭했을때 호출되는 메소드안에서
    listener.onDeleteButtClick()를 클릭하면 메인액티비티가 호출되겠지요. 
    
    

    그리고 메인액티비티에서 itemView에서 정의한 listener를 구현해주는겁니다.

    public class MainActivity extends Activity implements MyItemClickListener {
    protected void onCreate(Bundle savedInstanceState) {
        ...
        itemView.setOnMyItemClickListener(this);
    
        }
    
    @Override
    public void onDeleteButtClick(int index){
    //itemView에서 삭제버튼을 클릭했을때 listener.onDeleteButtClick()를 호출했다면 이 메소드가 호출되겠지요.
    //index값을 넘겨 받았으니 listView에 보여주는 arrayList가 있을거잖아요. 그 arrayList에서 데이터값을 삭제하고
    //adapter.notifyDataSetChanged()를 호출하면 뷰가 갱신될겁니다. 
    }
    }
    

    제대로 설명이 되었는지 모르겠네요. 도움이 되셨기를 바랍니다.

    • (•́ ✖ •̀)
      알 수 없는 사용자
    • (•́ ✖ •̀)
      알 수 없는 사용자
    • 좋은 답변 정말 감사합니다!! 그런데 itemView 클래스의 객체가 main Activity에 없으면 어떻게 하죠... itemView 객체는 adapter클래스에서만 사용이되서.. ㄷㄷ 객체를 하나 만드는 것이 좋으려나요?? 알 수 없는 사용자 2016.2.6 12:32
    • 음...itemView객체가 매인액티비티에 없다면 adapter에 매인액티비티를 넘기고 adapter에서 다시 매인액티비티를 itemView로 넘기면 되겠네요. 그런데 이렇게 하면 각클래스간에 결합도가 높아지므로 좋지 않은 방법으로보이지만 위에 설명한 방법으로는 어쩔수 없네요ㅜㅜ; 그래서 otto라이브러리를 많이 사용합니다;; 아니면 옵저버패턴을 이용해서 itemView에서 바로 mainActivity로 노티를 날리는 방법을 구현을 해야겠지요 알 수 없는 사용자 2016.2.6 13:32
  • 어떤식으로 구현하셨는지 모르겠지만 메인 액티비티에서 삭제버튼의 처리를 하시는거라면 메인 액티비티에서 터치,클릭 리스너를 등록하는 방법이 더 편할것 같습니다.

    그래서 스와이프 이벤트를 받았을경우 해당 항목의 인덱스를 미리 저장하도 삭제버튼이 눌렸을때 미리 저장힌 인덱스를 받아와서 해당하는 항목을 리스트에서 제거해주는 방법으로 하시면 될것 같습니다.

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)