달달한 스토리

728x90
반응형

하하 오늘 자바의 정석 강의 영상에서 얻은 영상 중에서 캡처한 사진이다.

 

super() 메서드가 이해가 잘 안 갔었는데, 정말 이해가 잘 가는 영상이다.

 

보통은 부모한테 상속받은 자식 클래스안에 메서드 인자가 부모 클래스에도

 

있을 때, 사용하는 것이라고 한다.

 

밑에 super(x, y)처럼 표기해주면 된다.

 

아무튼 오늘은 이 사실도 배웠지만,

 

더 중요한 것을 해보려고 한다.

 

보통 fragment 끼리 정보를 전달할 때는 보통 번들을 사용하지만

 

실험을 해본 결과 Viewpager위에 프래그먼트 끼리 번들로 

 

데이터 전송이 안된다.(방법이 있을 것인데, 아직 내가 초보라 모르겠다.)

 

그러다가 ViewModel을 이용한 데이터 전달 방법을 알게 되었다.

 


해결법

우선 저번에 사용하던 뷰 페이저를 똑같이 응용하겠습니다.

 

그대로 가져오겠습니다.

 

뷰페이저 부분은 설명을 생략하고 전부 올리겠습니다.

 

프래그먼트를 띄울 메인 액티비티와

 

그 위에 프래그먼트 그리고 그 위에 뷰 페이저와 프래그먼트들입니다.!!

 

MainActivity.java

 

public class MainActivity extends AppCompatActivity {

   HomeFragment fragment; //홈프래그먼트를 선언한다.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        fragment = new HomeFragment(); //초기화해준다.

        getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment).commit();
        ////프래그먼트를 여러개 사용할 수 있으므로, 비긴트랜잭션을 사용한다.
        //간단히 프래그먼트에서 사용되는 트랜잭션이란 어떤 대상에 대해 추가, 제거, 변경등의 작업들이
        //발생하는 것을 묶어서 이야기 하는 것이다.
        // 끝에 커밋을 해줘야지 작동한다.
    }
}

activity_main.xml

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
    </FrameLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

HomeFragment.java

 

public class HomeFragment extends Fragment {

    private ViewGroup viewGroup; //뷰그룹 객체 선언

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        viewGroup = (ViewGroup) inflater.inflate(R.layout.home_fragment, container, false);

        //뷰그룹 인플레이션 한 뒤 viewGroup에 리턴해 줍니다.



        //그리고 이 프래그먼트에 메서드를 하나 임의로 호출합니다.
        setInit(); //뷰페이저2 실행 메서드

        return viewGroup;
    }

    private void setInit() { //뷰페이저2 실행 메서드

        /* setup infinity scroll viewpager */
        ViewPager2 viewPageSetUp = viewGroup.findViewById(R.id.viewPager2); //여기서 뷰페이저를 참조한다.
        FragPagerAdapter SetupPagerAdapter = new FragPagerAdapter(getActivity()); //프래그먼트에서는 getActivity로 참조하고, 액티비티에서는 this를 사용해주세요.
        viewPageSetUp.setAdapter(SetupPagerAdapter); //FragPagerAdapter를 파라머티로 받고 ViewPager2에 전달 받는다.
        viewPageSetUp.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL); //방향은 가로로
        viewPageSetUp.setOffscreenPageLimit(3); //페이지 한계 지정 갯수
        // 무제한 스크롤 처럼 보이기 위해서는 0페이지 부터가 아니라 1000페이지 부터 시작해서 좌측으로 이동할 경우 999페이지로 이동하여 무제한 처럼 스크롤 되는 것 처럼 표현하기 위함.
        viewPageSetUp.setCurrentItem(1000);

        final float pageMargin = (float) getResources().getDimensionPixelOffset(R.dimen.pageMargin); //페이지끼리 간격
        final float pageOffset = (float) getResources().getDimensionPixelOffset(R.dimen.offset); //페이지 보이는 정도

        viewPageSetUp.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);

            }
        });
        viewPageSetUp.setPageTransformer(new ViewPager2.PageTransformer() {
            @Override
            public void transformPage(@NonNull View page, float position) {
                float offset = position * - (2 * pageOffset + pageMargin);
                if(-1 > position) {
                    page.setTranslationX(-offset);
                } else if(1 >= position) {
                    float scaleFactor = Math.max(0.7f, 1 - Math.abs(position - 0.14285715f));
                    page.setTranslationX(offset);
                    page.setScaleY(scaleFactor);
                    page.setAlpha(scaleFactor);
                } else {
                    page.setAlpha(0f);
                    page.setTranslationX(offset);
                }
            }
        });

    }
}

home_fragment.xml

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager2"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>


</androidx.constraintlayout.widget.ConstraintLayout>

FragPagerAdapter.java

 

public class FragPagerAdapter extends FragmentStateAdapter { //뷰페이저2에서는 FragmentStateAdapter를 사용한다.
    // Real Fragment Total Count
    private final int mSetItemCount = 3; //프래그먼트 갯수 지정

    public FragPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
        super(fragmentActivity);
    }


    @NonNull
    @Override
    public Fragment createFragment(int position) {
        int iViewIdx = getRealPosition(position);
        switch( iViewIdx ) {
            case 0    : { return new Frag1(); } //프래그먼트 순서에 맞게 넣어주세요.
            case 1    : { return new Frag2(); }
            case 2    : { return new Frag3(); }
//            case 3    : { return new Frag4(); }
//            case 4    : { return new Frag5(); }
//            case 5    : { return new Frag6(); }
            default   : { return new Frag1(); } //기본으로 나와있는 프래그먼트
        }

    }

    public int getRealPosition(int _iPosition){
        return _iPosition % mSetItemCount;
    }

    @Override
    public long getItemId(int position) {
        return super.getItemId(position);
    }

    @Override
    public int getItemCount() {
        return 3;
    }
}

여기 어뎁터에서 새로 안 사실이 있는데

 

아래 getItemCount() 메서드가 원래는 

 

Integer.MAX_VALUE라고 표기가 되어있더라고요.

 

화면에 몇 개의 프래그먼트를 반환해주냐는 메서드인데,

 

위에 같이 입력하면 

 

앞에 HomeFragment에서 setInit() 메서드에서 입력한

 

viewPageSetUp.setCurrentItem(1000);

 

여기 들어간 수만큼 반환이 됩니다.

 

그러니 위에 같이 원하지 않는 분들은 원하는 화면 개수를 써주시면 됩니다.

 

이제 뷰 페이저 프래그먼트들입니다.

 

우선 오늘 해볼 것은 ViewModel을 이용해 프래그먼트 간에 데이터 전달인데,

 

다른 곳에도 전달이 되는지는 다음에 실험을 해보겠습니다.

 

우선 데이터를 받고 내보내 주는 저장소를 만들겠습니다.

 

public class SharedViewModel extends ViewModel {

    //String
    private MutableLiveData<String> liveDataString = new MutableLiveData<>();

    public LiveData<String> getLiveDataString(){
        return liveDataString;
    }

    public void setLiveDataString(String str){
        liveDataString.setValue(str);
    }
    //int
    private MutableLiveData<Integer> liveDataInt = new MutableLiveData<>();

    public LiveData<Integer> getLiveDataInt(){
        return liveDataInt;
    }

    public void setLiveDataInt(Integer integer){
        liveDataInt.setValue(integer);
    }
    //long
    private MutableLiveData<Long> liveDataLong = new MutableLiveData<>();

    public LiveData<Long> getLiveDataLong(){
        return liveDataLong;
    }

    public void setLiveDataLong(long longa){
        liveDataLong.setValue(longa);
    }
}

 

String형 Int형 Long형 별로 만들었습니다.

 

원하는 형태로 바꿔서 쓸 수 있다는 점을 알려드려 했습니다.

 

Frag1 아래를 보시겠습니다.

 

Frag1.java

 

public class Frag1 extends Fragment {

    private SharedViewModel sharedViewModel;
    TextView textView;
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_1, container, false );
        textView = view.findViewById(R.id.textView);

        Button button = view.findViewById(R.id.button2);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String hi = "안녕";
                long money = 5000000;
                try {

                    sharedViewModel.setLiveDataString(hi);//Frag2
                    sharedViewModel.setLiveDataLong(money); //Frag3
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });

        //이렇게 각각 데이터를 보내줍니다.
        
        return view;

    }

    //onCreateView에서 리턴해준 View(rootView)를 가지고 있다.
    //저장된 뷰가 반환된 직후에 호출됩니다.
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { //뷰가 제공되는 경우 반환된 뷰를 가져옵니다.
        super.onViewCreated(view, savedInstanceState);

        sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);

        //지정된 Factory를 통해 ViewModel을 만들고 지정된 ViewModelStoreOwner의 저장소에 유지하는 ViewModelProvider를 만듭니다.

    }
}

 

우선 SharedViewModel sharedViewModel; 를 변수로 선언을 해줍니다.

 

그리고 아래에 onViewCreated라는 함수를 컨트롤 + O 버튼을

 

눌러서 호출해줍니다.

 

안드로이드 JetPack요소 중 하나인 ViewModel에 대해서

 

간략하게 설명하겠습니다.

 

저도 아직 공부 중이라 잘 모릅니다.

 

헤헤

 

우리가 먼저 ViewModel 인스턴스를 얻고 싶은 상황입니다.

 

우선 ViewModelProvider을 호출하고 -> ViewModel 인스턴스를 요청합니다.

 

그러면 ViewModelProvieder내부에서 ViewModelStoreOwner를 참조하여, ViewModelStrore(저장소)을 가져오게

 

됩니다.. 복잡하죠 ㅎㅎ

 

그리고 

 

ViewModelStore에 이미 저장됐거나 생성된 ViewModel 인스턴스를 요청합니다.

 

만약 ViewModelStoreViewModel 인스턴스가 없다면,

 

ViewModelProvider.Factory를 통해 ViewModel인스턴스를 생성합니다.

 

그렇게 생성한 ViewModel 인스턴스나 ViewModelStore에 저장되거나 만들어진

 

ViewModel 인스턴스를 요청한 우리(클라이언트)한테 반환하는 것입니다.

 

이러한 요청이 여러 번 들어오면, 계속 반복하게 됩니다.

 

실시간 데이터를 구현하기에 아주 적합하다고 생각이 됩니다!!

 

헷갈리실까 봐 색깔로 표현했는데, 어려우실 거 같아요ㅠㅠ

 

https://charlezz.medium.com/viewmodel%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-viewmodel-%EC%B4%88%EB%B3%B4%EB%A5%BC-%EC%9C%84%ED%95%9C-%EA%B0%80%EC%9D%B4%EB%93%9C-e1be5dc1ac18

 

ViewModel이란 무엇인가? ViewModel 초보를 위한 가이드

ViewModel이란?

charlezz.medium.com

여기 개발자님이 잘 정리해서 올리셔서, 저도 이 글을 참고했으니, 보시면 좋을 거 같아요!

 

 

아무튼 이야기가 길어졌네요.

 

String형 Int형 Long형을

 

각각 데이터를 넣어서 받아오는 과정을 보겠습니다.

 

Frag1은 int를 가져오게 해 놨습니다.

 

저 아래 onViewCreated에는 위에 onCreateView에서 리턴해준 view객체를 가지고 있습니다!

 

또한 이 메서드(onViewCreated)는 저 뷰가 반환된 직후에 호출이 된답니다.

 

그리고 저장소에서 ViewModelProvider메서드를 호출하고, 파리 미터로

 

액티비티를 호출하고 ViewModel클래스를 가져옵니다.

 

아까 위에서 말한 과정들이 바로 이 코드 한 줄로 이루어지는 거랍니다.

 

ViewModel은 한마디로 실시간으로 데이터가 전달되고 전달받는 저장소라고 볼 수 있습니다.

 

 

 

Frag2.java

 

public class Frag2 extends Fragment {
    TextView textView;
    SharedViewModel sharedViewModel;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_2, container, false );
        textView = view.findViewById(R.id.textView4);


        return view;

    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { //뷰가 제공되는 경우 반환된 뷰를 가져옵니다.
        super.onViewCreated(view, savedInstanceState);

        sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        sharedViewModel.getLiveDataString().observe(getViewLifecycleOwner(), new Observer<String>() {
                    @Override
                    public void onChanged(String s) {
                        textView.setText(s);
                    }
                });
    }

}

 

두 번째 프래그먼트입니다.

 

SharedViewModel(저장소)를 활용해보겠습니다.

 

그 아래 코드 줄을 보면 

 

sharedViewModel.getLiveDataString().observe(getViewLifecycleOwner(), new Observer<String>() {
            @Override
            public void onChanged(String str) {
                textView.setText(str);
            }
        });

이런 식으로 함수를 호출하는 것입니다.

 

SharedViewModel에서 getLiveDataString메서드를 호출해 아까

 

setLiveDataString메서드에 넣어두었던 String값을 가져오게 됩니다.

 

onChanged메서드 안에

 

아까 참조한 textView에다가 setText를 하고 안에 인자로 String를 넣겠습니다.

 

그러하면, 아까 넣어둔 "안녕"이 나올 것입니다.

 

 

Frag3.java

 

public class Frag3 extends Fragment {

    SharedViewModel sharedViewModel;
    TextView textView;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_3, container, false );

        textView = view.findViewById(R.id.textView3);

        return view;

    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        sharedViewModel.getLiveDataLong().observe(getViewLifecycleOwner(), new Observer<Long>() {
            @Override
            public void onChanged(Long aLong) {
                DecimalFormat format = new DecimalFormat("###,###.##"); // 콤마 표시를 해준다(예 123123 => 123,123
                textView.setText(format.format(aLong));
            }
        });

    }
}

3번째 프래그먼트입니다.

 

여기서는 long형으로 값을 넘겨주었습니다.

 

여기서 유용한 정보를 알려드리겠습니다.

 

DecimalFormat이라는 함수입니다.

 

쓰는 법은 위와 같이 인스턴스화를 해주고,

 

파라미터에 패턴을 넣어주시면 됩니다.

 

돈 같은 단위도 콤마를 넣어줘야 하거나 소수점을 붙여서 나올 수 있게 해 줍니다.

 

저렇게만 써놓아도 백만으로 갈 때도 자동으로 콤마가 붙어서 편리하게 쓰실 수 있습니다.

 

format이라는 변수를 선언해주시고, 안에 format함수를 호출하고 그 안에 long타입을 넣으시면 됩니다.

 

이제 결과를 보겠습니다.

 

그전에 xml 파일들입니다.

 

fragment_1~3.xml

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#2196F3">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="나는 자팍이야"
        android:textColor="#000000"
        android:textSize="24sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="136dp"
        android:text="데이터 전달"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FF5722">

    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="헤헿ㅎ"
        android:textColor="#000000"
        android:textSize="24sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FF5722">

    <TextView
        android:id="@+id/textView4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="헤헿ㅎ"
        android:textColor="#000000"
        android:textSize="24sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

 

 

혹시 번거로우시면 그냥 깃 헙에서 다운로드하으세요.!!

 

https://github.com/qjsqjsaos/Tistrory4

 

qjsqjsaos/Tistrory4

ViewModel을 이용한 데이터 전달(뷰페이저 위에서). Contribute to qjsqjsaos/Tistrory4 development by creating an account on GitHub.

github.com

 

728x90
반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading