이런식으로 만들 수 있나요?

조회수 1128회

이미지

솔직히 이런 모양을 만드는 건 간단하다고 생각합니다. 문제는 저 창에 버튼을 넣고 그 버튼을 눌렀을 때 다른 프래그먼트나 액티비티로 넘어가고 싶습니다.

현재 밑에 처럼 만드는 방법은 PopupWindow를 사용하느냐, Layout을 하나 만들고 그 위에 Intent 하느냐인데,

PopupWindow로 만들면, 안에 버튼을 눌렀을 때 불러오는 Fragment나 Activity를 PopupWindow 창에만 뜨게 하는 방법을 모르겠고,

Intent로 하자니 Handler 로 아무런 터치나 동작이 없을 시 몇 초 후에 자동으로 닫혀라 라는 명령을 못 내리겠습니다...

Theme.dialog 로도 해봣습니다만, 안되더군요. 아마 이 부분은 자바에서는 되는데 C#으로는 안되는 느낌이에요. 제가 못하는걸수도 있지만 ㅠ

다시 정리해서 말하자면 현재 만들고자 하는건 이런겁니다.

  1. 화면 오른쪽에 버튼을 눌렀을 때, 왼쪽 레이아웃 하단 부분에 PopupWindow 혹은 Layout을 띄워 새로운 레이아웃을 불러오는 것
  2. 불러온 레이아웃의 버튼을 배치하고 그 버튼을 눌렀을 때 화면 전체가 아닌 그 창 내에서만 변하도록
  3. 그리고 아무런 터치 동작이 없을 때 10초 정도의 시간을 주고 자동으로 사라지도록

어떻게 해야 좋을까요?ㅠㅠ

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

3 답변

  • PopupWindow 사용 layout 을 배치해놓고 visibility 를 변경 하는 방법 Fragment 를 show, hide 하는 방법 BottomSheetDialog 사용하기 Theme.Dialog 사용 이외에도 추가적인 방법이 있을수 있지만, 해당 화면이 UX 상으로 출현빈도가 높은지 여부와 UI 배치가 어떤구조인지에 따라 달라질것 같습니다..

    Touch Event 를 핸들링 하는 적절한 메소드를 Override 하고, 아래와 같은 방향으로 처리하면 될 것 같습니다.

    public class TestLayoutActivity extends AppCompatActivity {
        private final static long TRIGGER_TIME = 10000;
        private Handler mHandler = new Handler();
    
        @Override
        protected void onCreate(final Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.test_layout);
            start();
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                restart();
            }
            return super.dispatchTouchEvent(ev);
        }
    
        private void start() {
            mHandler.postDelayed(runnable, TRIGGER_TIME);
        }
    
        private void restart() {
            mHandler.removeCallbacks(runnable);
            start();
        }
    
        private Runnable runnable = new Runnable() {
    
            @Override
            public void run() {
                somethingView.setVisibility(View.GONE);
            }
        };
    }
    
    • (•́ ✖ •̀)
      알 수 없는 사용자
    • 혹시 정말 죄송한데 다른 질문인데, Fragment와 Fragment 사이에 TextView를 변경하고 싶을 때는 어떻게 해야하는지 아시나요? ㅠ Fragment1에 TestText 라는 뷰가 있고 이 부를 Fragment2에서 건드리고 싶은데 ㅠ Activity를 중간에 끼고가 아닌 직접적으로 알 수 없는 사용자 2018.6.29 17:16
    • React 프로그래밍과 Event Bus 를 사용하는 경우를 제외하면, 중간에 Activity 를 거치지 않고 하려면 Broadcast 를 사용할순 있겠네요. 또한 static 변수를 쓰는 방법도 있겠지만 추천드리고 싶진 않구요. 어떤 구조를 갖는지 자세힌 모르지만, Activity 를 거치지 않고 바로 컨트롤 하려는 목적이 무엇인가요? 알 수 없는 사용자 2018.6.29 17:41
    • 답변 작성으로 그림을 하나 올리겠습니다. 제가 말로 설명을 잘못해서 ㅠ 알 수 없는 사용자 2018.6.29 17:51
    • 네 맞습니다. Button1,2는 각각 다른 Fragment를 연결합니다. 알 수 없는 사용자 2018.6.29 23:36
  • 이미지

    원래하려던 것은 질문했던 것처럼, Fragment가 1,2,3 이 있고, 1이 현재 Fragmnet1 창, 2가 지금 그림에 있는 창이 아닌 버튼들이 모여있는 창으로 1이 시작될때 자동으로 불러오도록 설정했었습니다. 그렇게 하면 TextView를 Button1과 Button2를 누를 때, 다른 글로 바뀌어야 하는데, 이 부분이 추천하고 싶지않으시다면 그림처럼은 할 수 없을까요?ㅠ

    Button 을 눌렀을 때, 작은 화면에 메뉴창이 뜨도록 하고 그 메뉴창에서 이제 그림처럼 하려고 하는데,

    • (•́ ✖ •̀)
      알 수 없는 사용자
    • Button1 을 누르면 타이틀영역 하단 영역이 Fragment2 로 교체되고, Button2 를 누르면 또다른 Fragment 로 교체되면 되는 것인가요? 그리고 위 내용은 답변 부분이 아닌 문의내용에 업데이트 해주셨으면 좋겠습니다 ^^; 알 수 없는 사용자 2018.6.29 19:39
  • MasterFrameFragment

    layout1 에 표시 될 Fragment 입니다. 타이틀 영역 + SlaveFragmentContainer 로 구성 됩니다. MasterFrameFragment 는 아래와 같이 Show 버튼을 눌렀을때 나타납니다.

    findViewById(R.id.showBtn).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    final FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
                    if (mMasterFrameFragment.isAdded()) {
                        if (mMasterFrameFragment.isHidden()) {
                            tr.show(mMasterFrameFragment);
                        } else {
                            tr.hide(mMasterFrameFragment);
                        }
                    } else {
                        tr.add(R.id.fragmentContainer, mMasterFrameFragment);
                    }
                    tr.addToBackStack(null);
                    tr.commit();
                }
            });
    

    또한 back 버튼에 대한 동작을 구현합니다. 아래에서 SlaveFragment 간의 backstack 이 구현 되있으므로, stack count 를 체크하여 구현합니다.

    getView().findViewById(R.id.backBtn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final FragmentManager cm = getChildFragmentManager();
                if (cm.getBackStackEntryCount() > 0) {
                    cm.popBackStack();
                } else {
                    if (getActivity() != null) {
                        getActivity().onBackPressed();
                    }
                }
            }
        });
    

    SlaveChoiceFragment

    타이틀 영역 하단부를 차지하는 SlaveFragment 입니다. Button1 과 Button2 가 있어서 SlaveFragment1 또는 SlaveFragment2 를 선택 합니다. SlaveChoiceFragment 는 SlaveContainer 에서 최상단에 위치할 Fragment 이므로 MasterFrameFragment 의 onActivityCreated() 에서 add 하고 시작됩니다.

    onActivityCreated() in MasterFrameFragment

     @Override
     public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    
        final FragmentTransaction tr = getChildFragmentManager().beginTransaction();
        tr.add(R.id.slaveFragmentContainer, mSlaveChoiceFragment);
        tr.commit();
    }
    

    이제 Button1, Button2 를 눌렀을떄 각각 SlaveFragment1 , SlaveFragment2 가 보여지도록 클릭 리스너를 아래와 같이 구현합니다. 이때 FragmentContainer 는 MasterFrameFragment 에 있기 때문에 parent fragment 의 fragment manager 를 가져와서 Transaction 동작을 구현합니다.

    @Override
        public void onClick(View v) {
            final FragmentTransaction tr = getParentFragment().getChildFragmentManager().beginTransaction();
            tr.hide(this);
            switch (v.getId()) {
                case R.id.Button1:
                    if (mSlaveFragment1.isAdded()) {
                        tr.show(mSlaveFragment1);
                    } else {
                        tr.add(R.id.slaveFragmentContainer, mSlaveFragment1);
                    }
                    break;
    
                case R.id.Button2:
                    if (mSlaveFragment2.isAdded()) {
                        tr.show(mSlaveFragment2);
                    } else {
                        tr.add(R.id.slaveFragmentContainer, mSlaveFragment2);
                    }
                    break;
            }
            tr.addToBackStack(null);
            tr.commit();
        }
    

    여기서 addToBackStack() 을 통해 타이틀영역의 Back 버튼을 눌렀을때 다시 SlaveChoiceFragment 가 나타나도록 처리 합니다.

    SlaveFragment1

    Button1 를 눌렀을 때 표시 될 Fragment 입니다

    SlaveFragment2

    Button2 를 눌렀을 때 표시 될 Fragment 입니다

    • (•́ ✖ •̀)
      알 수 없는 사용자
    • JLPT 시험 때문에 이제 확인했네요! 현재 이 코드에서 말씀하시는게 혹시 Fragmnet1을 타이틀부와 하단부로 나눠서 하는건가요? 이렇게 되면 하단부의 버튼을 눌러서 다른 Fragment로 넘어가도 TextView에 대한 변경이 안되는데..ㅠ 알 수 없는 사용자 2018.7.2 09:24
    • 하단부에서 getParentFragment() 를 호출하면 해당 TextView 를 갖고있는 Fragment 의 참조를 가져올수 있습니다. 이후 해당 Fragment 의 TextView 변경사항을 적용하시면 됩니다. 알 수 없는 사용자 2018.7.2 10:55
    • findViewById 같은 형식이라는 말씀이신가요? 알 수 없는 사용자 2018.7.2 13:37
    • 타이틀영역이 구현 된 fragment 에 TextView 변경사항을 적용하는 메소드를 두고 하단부 Fragment 에서 parentFragment 의 해당 메소드를 호출하는 방식이 될 수 있습니다. 알 수 없는 사용자 2018.7.2 13:53
    • 말씀하신대로 Fragment1 에 메소드를 구현해두고 2 에서 호출하는 것을 의미합니다 :) 알 수 없는 사용자 2018.7.2 14:21
    • 솔직히 잘 이해가 안가는데; 지금 이해가 되는건 Fragment1 그러니까 타이틀 프래그먼트에 OnCreateView가 아닌 OnCreate 부분에 하단부 Fragment를 불러오는 코드를 넣고, show 버튼을 눌렀을 때, Fragment1을 가져오면서 2도 같이 가져오도록 하는거까진느 이해가 됩니다. 알 수 없는 사용자 2018.7.2 14:22
    • 그런 후에 Fragment1에 OnCreateView 부분에서 Fragment2의 버튼들의 정의를 넣어주는건가요? 그러면 어디서 그 버튼들의 정의를 내려주는건가요...? 방금 제가 이해한대로 해봣는데 button에 대한 액션은 아무것도 표시가 되지 않습니다ㅠㅠ 알 수 없는 사용자 2018.7.2 14:23
    • 지금 올려주신 게 일단 제일 처음 Layout1이 존재하는 칸에서 Show 버튼을 눌렀을 때 올려주신 코드의 MasterFrameFragment를 호출하는제일 상단 부분이 실행이 된다는거잖아요? 그 후에 MasterFrameFragment의 OnCreate 부분에 SlaveChoiceFragment를 호출하는 코드를 넣고 이게 3번째 코드, OnCreateView 부분에 2번째 코드로 BackButton에 대한 정의를 내려주고, 마지막으로 SlaveChoiceFragment의 OnCreateView 부분에 4번째 코드를 넣어주신거죠?; 그리고 MasterFrameFragment의 메소드로 TextView에 대한 걸 만들고, 그걸 SlaveChoiceFragment에서 불러오는? 알 수 없는 사용자 2018.7.2 14:38
    • 1. 타이틀 하단 영역은 SalveChoiceFragment 로 구현되어있고 여기에 버튼들에 대한 구현이 들어갑니다. 2. MasterFrameFragment 는 타이틀 영역만 갖는게 아닙니다. 하단 영역에 FramgmentContainer 에 해당하는 별도의 layout 이 있고 여기에 SlaveChoiceFragment 가 들어갑니다 해당 코드들이 어느 fragment 에 해당하는지는 위에 표시 되어있습니다 알 수 없는 사용자 2018.7.2 15:19
    • 네 그러니까 말씀하시는게 타이틀 프래그먼트 안에 하나의 레이아웃이 더 있어서 이 레이아웃이 초이스 프래그먼트를 연결해주시는거잖아요? 그리고 타이틀 프래그먼트에 코드에는 백버튼에 대한 코드가 있고, 초이스 프래그먼트에 코드에는 각 버튼들의 이벤트가 들어있는거죠? 알 수 없는 사용자 2018.7.2 15:33
    • 만약 제가 말한게 맞다면, 타이틀 프래그먼트 내에서 메소드를 만들어서 초이스 프래그먼트에서 불러온다는 말씀이신데 이 부분은 어떻게 불러오는건가요? 알 수 없는 사용자 2018.7.2 15:34
    • choiceFragment 가 MasterFragment 내에서 트랜잭션 동작으로 attach 되기 때문에 getParentFragment() 를 호출하면 MasterFragment 를 불러오게 됩니다. 캐스팅 해서 사용하시면 됩니다. 알 수 없는 사용자 2018.7.2 15:44
    • 제가 이해력이 심하게 딸리는거 같은데, backBtn은 왜 Slave에 있는건가요? Back 버튼은 타이틀 부분이니 Master에 있어야 하는거 아닌가요? 그리고 ChoiceFragment에서 ParentFragment를 호출한 후에 제가 만든 메소드를 찾으려고 하니 없던데... 알 수 없는 사용자 2018.7.2 15:53
    • 코드를 따라하시기 보다 흐름을 파악하시는게 중요해요. 그 목적으로 예제코드를 올려 드린거구요 :) 제가 제시한 방법이 베스트가 아닐수도 있습니다. backBtn 는 MasterFragment 에 있습니다. 잘못 보신것 같아요. 그리고... getParentFragment() 의 return type 은 Fragment 입니다. 캐스팅 해서 사용하셔야지요. 알 수 없는 사용자 2018.7.2 16:02
    • 지금까지 답변해주시고 도와주셔서 감사합니다. 다른 방법을 찾아야할 것 같네요 도무지 이해가 안되요 일단 전부 다 알겠는데 ParentFragment를 불러온다 라는 곳에서 부터 이해가 안가네요.. 알 수 없는 사용자 2018.7.2 16:20
    • 네 안드로이드 개발자 사이트에서 Fragment 에 대한 설명을 훑어보시면 이해하시는데 도움이 좀 되실거에요. 언제든 문의주세요 :) 알 수 없는 사용자 2018.7.2 16:31
    • 아 드디어 됐네요.. 진짜 멍청하게 선언을 제대로 안해줫었네요 ㅠㅠㅠ 알 수 없는 사용자 2018.7.2 16:49
    • 아 어떻게 해결하셨어요? 알 수 없는 사용자 2018.7.2 17:14
    • MasterFragment에 internal void ChangeText(string v) { _Title_Text.Text = v; } 이런식으로 메소드를 만들고, Sub_Main_Fragment parentFrag = ((Sub_Main_Fragment)this.ParentFragment); Choice 부분에서 이런식으로 호출했어요 알 수 없는 사용자 2018.7.2 17:43
    • 넵 해결되셔서 다행이네요 :) 알 수 없는 사용자 2018.7.2 17:56
    • 감사합니다 ㅎ 알 수 없는 사용자 2018.7.2 18:02

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

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

(ಠ_ಠ)
(ಠ‿ಠ)