이번에는 MVP 패턴을 구현한 예제를 가지고 왔다.

MVP가 뭐냐!

...는거는 다른 블로그에도 많으니 나는 생략하기로 함.

 

예제는 '버튼을 누르면 서버에서 랜덤한 텍스트를 받아서 화면에 뿌려주는' 간단한 앱이다.

JAVA 언어로 되어있고 DataBinding이나 ViewBinding은 사용하지 않았다!

 

전체 소스는 아래 링크 참조!

 

https://github.com/YelanJeon/PatternTest/tree/mvp

 

GitHub - YelanJeon/PatternTest: 디자인 패턴 공부

디자인 패턴 공부. Contribute to YelanJeon/PatternTest development by creating an account on GitHub.

github.com

 

 

MVP 1번이 있고 2번이 있는데,

1번 방식으로도 할 수 있고 (모델에서 서버 통신)

2번 방식으로도 할 수 있다 (프레젠터에서 서버 통신)

 


MVP를 예를 들자면...

 

뷰 : 야 뭐 해줘

프레젠터 : ㅇㅇ.. 모델아 이거 해

모델 : 했어

프레젠터 : 뷰야 여기

뷰 : ㄳㄳ

 

이런 식이라고 볼 수 있다고...

 

추가로 궁금한 점 있으면 댓글 달아주세요 :)

페이징3 기본 기능을 구현했다.

한 번에 열 개씩 String 리스트를 불러와서 화면에 리스트로 뿌려주는 프로그램이다.

세 번 따라해보니까 이제 뭐가 뭔지 감이 온다.

근데 아직 혼자서는 못하겠음.. ㅜ-ㅜ

 

https://github.com/YelanJeon/Paging3Study.git

 

 

GitHub - YelanJeon/Paging3Study

Contribute to YelanJeon/Paging3Study development by creating an account on GitHub.

github.com


LoadStateAdapter를 구현하고, 맨 처음 데이터를 불러왔을 때 실패하면 Empty View를 보여주도록 코드를 추가하였다.

 

 

 

참고 링크 

https://leveloper.tistory.com/202

https://leveloper.tistory.com/207

 

이번에 RecyclerView를 쓸 때 StaggeredGridLayoutManager를 썼는데

한 쪽으로만 몰리는 이상한 현상이 발생하였다.

알고보니 아이템 뷰로 쓰는 뷰의 크기가 match_parent, match_parent로 설정되어있어서 그런 것이었다.

 

난 카드뷰 안에 이미지 뷰가 들어있는 방식이고, 그 이미지 뷰의 가로 세로 크기만큼 카드뷰 크기가 결정되게 되어있었는데

카드뷰 - wrap_parent, wrap_parent

이미지뷰 - match_parent, match_parent

이렇게 되어있어서 결과적으로는 둘 다 match_parent로 된 거나 다름없이 된 것..

 

이미지를 웹에서 불러온 뒤 이미지뷰에 넣고

이미지 뷰의 layoutParams의 width와 height를 불러온 이미지의 width와 height로 설정해주니 

정상적으로 나왔다.

이번에는 라이브 데이터만 다루는 예제이다

 

1. MainActivity

package com.monkey.viewmodeltest;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;

import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView tvNormal;
    Button btnPlus, btnMinus;

    MutableLiveData<Integer> normalCount = new MutableLiveData<Integer>();

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

        tvNormal = findViewById(R.id.tv_normal);
        btnPlus = findViewById(R.id.btn_plus);
        btnMinus = findViewById(R.id.btn_minus);

        normalCount.observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                setCountText();
            }
        });

        btnPlus.setOnClickListener(v-> {
            normalCount.setValue(normalCount.getValue()+1);
        });

        btnMinus.setOnClickListener(v-> {
            normalCount.setValue(normalCount.getValue()-1);
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        setCountText();
    }

    private void setCountText() {
        tvNormal.setText(normalCount.getValue() +"");
    }
}

 

2. 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">

    <Button
        android:id="@+id/btn_minus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="-"
        app:layout_constraintTop_toBottomOf="@id/tv_normal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/btn_plus" />

    <Button
        android:id="@+id/btn_plus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="+"
        app:layout_constraintLeft_toRightOf="@id/btn_minus"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@id/btn_minus"
        app:layout_constraintBottom_toBottomOf="@id/btn_minus"/>


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="NORMAL"
        android:textColor="@color/purple_500"
        android:textStyle="bold"
        app:layout_constraintLeft_toLeftOf="@id/tv_normal"
        app:layout_constraintRight_toRightOf="@id/tv_normal"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/tv_normal"
        app:layout_constraintVertical_bias="1"/>

    <TextView
        android:id="@+id/tv_normal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="0"
        android:textColor="#000"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

plus, minus 버튼에서 setText를 해 주지 않아도

값이 변하면 텍스트가 변하게 된다!

여기서 말하는 ViewModel은 MVVM의 ViewModel이 아님!

 

ViewModel은 보통 LiveData랑 같이 써서

두개를 따로 다루는 예제는 못봤다 -_- ;;

 

그러다 오늘 예제를 발견해서 따라함ㅋㅋ

 

차근차근 할 거라 우선 ViewModel만 맛보는 예제를 준비했다.

 

코드 언어는 Java고 ViewBinding이나 DataBinding은 사용하지 않았음.

 

 

1. MainActivity.class

public class MainActivity extends AppCompatActivity {

    TextView tvViewModel, tvNormal;
    Button btnPlus, btnMinus;

    CounterViewModel counterViewModel;
    int normalCount = 0;

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

        counterViewModel = new ViewModelProvider(this).get(CounterViewModel.class);

        tvViewModel = findViewById(R.id.tv_viewmodel);
        tvNormal = findViewById(R.id.tv_normal);
        btnPlus = findViewById(R.id.btn_plus);
        btnMinus = findViewById(R.id.btn_minus);

        btnPlus.setOnClickListener(v-> {
            normalCount++;
            counterViewModel.count++;
            setCountText();
        });

        btnMinus.setOnClickListener(v-> {
            normalCount--;
            counterViewModel.count--;
            setCountText();
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        setCountText();
    }

    private void setCountText() {
        tvViewModel.setText(counterViewModel.count+"");
        tvNormal.setText(normalCount +"");
    }
}

 

2. CounterViewModel

* class를 public으로 하지 않으면 'Cannot create an instance class ...' 라는 RuntimeException이 발생한다.

public class CounterViewModel extends ViewModel {
   int count=0;
}

 

3. activity_main

<?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">

    <Button
        android:id="@+id/btn_minus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="-"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/btn_plus"
        app:layout_constraintTop_toBottomOf="@id/tv_viewmodel"
        app:layout_constraintBottom_toBottomOf="parent"/>

    <Button
        android:id="@+id/btn_plus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="+"
        app:layout_constraintLeft_toRightOf="@id/btn_minus"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="@id/btn_minus"
        app:layout_constraintBottom_toBottomOf="@id/btn_minus"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="VIEW_MODEL"
        android:textColor="@color/purple_500"
        android:textStyle="bold"
        app:layout_constraintLeft_toLeftOf="@id/tv_viewmodel"
        app:layout_constraintRight_toRightOf="@id/tv_viewmodel"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/tv_viewmodel"
        app:layout_constraintVertical_bias="1"/>

    <TextView
        android:id="@+id/tv_viewmodel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="0"
        android:textColor="#000"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/tv_normal"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="NORMAL"
        android:textColor="@color/purple_500"
        android:textStyle="bold"
        app:layout_constraintLeft_toLeftOf="@id/tv_normal"
        app:layout_constraintRight_toRightOf="@id/tv_normal"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/tv_normal"
        app:layout_constraintVertical_bias="1"/>

    <TextView
        android:id="@+id/tv_normal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="0"
        android:textColor="#000"
        app:layout_constraintLeft_toRightOf="@id/tv_viewmodel"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>





</androidx.constraintlayout.widget.ConstraintLayout>

하단의 증감 버튼을 누르면 숫자가 표시된다.

하나는 뷰모델을 이용하였고, 하나는 Activity의 전역변수로 선언하여 사용했다.

증감버튼을 누른 다음 화면을 회전시키거나 하면 ViewModel의 카운트는 그대로 유지되지만

Activity의 전역변수는 다시 초기값으로 돌아가는 것을 확인할 수 있다.

+ Recent posts