페이징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의 전역변수는 다시 초기값으로 돌아가는 것을 확인할 수 있다.

이전에 했던 코드(https://relz.tistory.com/64)에서 테스트용으로 넣었던 저장&불러오기를 없애고

사칙연산 버전으로 변경하였음

 

1. Contract

package com.monkey.mvpstudy

interface Contract {
    interface View {
        fun setValue(num1: Int, num2: Int, num3: Int)
        fun showResult(num: Int)
    }

    interface Presenter {
        fun getSum(num1: Int, num2: Int)
        fun getSubtract(num1: Int, num2: Int)
        fun getMultiply(num1: Int, num2: Int)
        fun getDivide(num1: Int, num2: Int)
    }
}

 

 

2. MainModel

class MainModel {
    var num1 = 0
    var num2 = 0

    fun plus() : () -> Int = { num1 + num2 }
    fun subtract() : () -> Int = { num1 - num2 }
    fun multiply() : () -> Int = { num1 * num2 }
    fun divide() : () -> Int = { num1 / num2 }
}

 

3. MainActivity

package com.monkey.mvpstudy

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import com.monkey.mvpstudy.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity(), Contract.View, View.OnClickListener {
    private val binding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }
    private val presenter by lazy {
        MyPresenter(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        binding.btnSum.setOnClickListener(this)
        binding.btnSubtract.setOnClickListener(this)
        binding.btnMultiply.setOnClickListener(this)
        binding.btnDivide.setOnClickListener(this)
    }

    override fun setValue(num1: Int, num2: Int, num3: Int) {
        binding.etNum1.setText(num1.toString())
        binding.etNum2.setText(num2.toString())
        showResult(num3)
    }

    override fun showResult(num: Int) {
        binding.tvResult.text = num.toString()
    }

    override fun onClick(v: View?) {
        val num1 = binding.etNum1.text.toString().toInt()
        val num2 = binding.etNum2.text.toString().toInt()

        when(v) {
            binding.btnSum -> presenter.getSum(num1, num2)
            binding.btnSubtract -> presenter.getSubtract(num1, num2)
            binding.btnMultiply -> presenter.getMultiply(num1, num2)
            binding.btnDivide -> presenter.getDivide(num1, num2)
        }
    }
}

 

4. MyPresent

package com.monkey.mvpstudy

class MyPresenter : Contract.Presenter {

    private var view : Contract.View
    private var model : MainModel

    constructor(view: Contract.View) {
        this.view = view
        this.model = MainModel()
    }

    override fun getSum(num1: Int, num2: Int) {
        val result = calculate(num1, num2, model.plus())
        view.showResult(result)
    }

    override fun getSubtract(num1: Int, num2: Int) {
        val result = calculate(num1, num2, model.subtract())
        view.showResult(result)
    }

    override fun getMultiply(num1: Int, num2: Int) {
        val result = calculate(num1, num2, model.multiply())
        view.showResult(result)
    }

    override fun getDivide(num1: Int, num2: Int) {
        val result = calculate(num1, num2, model.divide())
        view.showResult(result)
    }


    private fun calculate(num1: Int, num2: Int, calc: () -> Int): Int {
        model.num1 = num1
        model.num2 = num2
        return calc()
    }
}

+ Recent posts