버그 잡이

(실전 적용)ViewModel + LiveData로 화면 전환시 data 초기화 문제 해결하기 #configuration change 본문

안드로이드

(실전 적용)ViewModel + LiveData로 화면 전환시 data 초기화 문제 해결하기 #configuration change

버그잡이 2020. 4. 10. 21:49

센서로 흔들림을 감지 -> 카운트up

 

내가 만들었던 앱 중에서 핸드폰 센서를 이용해서 벌레를 잡는 게임이 있다.

벌레는 4가지 종류가 랜덤으로 선택 되어 나타나고 열심히 흔들어서 카운트가 10까지 세지면 벌레가 잡히는 게임이다.

 

여기에는 문제가 있는데 화면이 가로/세로 전환이 될때 벌레 이미지와 카운트가 초기화 된다는 것이다.

이번에 배운 ViewModelLiveData를 활용해서 이 문제를 해결해보자.

 

 

GetBugActivity에서 사용되는 메소드는 다음과 같다.

 

- selectBug() : bugImg 랜덤 선택

- getBugImg() : bugImg 받아서 업데이트

- showAlert() : 카운트가 10이 될 경우 다이얼로그 띄워주기

- countUp() : 흔들림 감지될때마다 카운트 올리기

 

+SenSonEventListener에 따른 override 메서드들...

 

 

UI / ViewModel 구분하기

 

1. selectBug() : bugImg 데이터를 결정하는 decision-making 메서드로 -> ViewModel

2. getbugImg() : UI를 참조함으로 -> UI

3. showAlert() : 다이얼로그는 UI와 연결됨으로 -> UI

4. showAlert() : count 데이터를 결정하는 decisiom-making 메서드로 -> ViewModel

 

5. SensorEventListener : 생명주기에 따라 리스너를 해제해줘야하는데 viewmodel에 넣을 경우 onStop시 리스너 해제가 안 됨으로 UI의 생명주기 메서드를 통해서 통제.

 

이를 바탕으로 아래의 코드와 같이 작성했다.

 

 

*GetBugActivity.class

public class GetBugActivity extends AppCompatActivity implements SensorEventListener {

    ImageView iv_getBug;
    TextView tv_count;
    ConstraintLayout layout_getBug;
    private SensorManager sensorManager;
    private Sensor countSensor;
    Animation animRotate;

    int count = 0;

    private GetBugViewModel viewModel;

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

        iv_getBug = findViewById(R.id.iv_getBug);
        tv_count = findViewById(R.id.tv_count);
        layout_getBug = findViewById(R.id.layout_getBug);

        //뷰모델 설정
        viewModel = ViewModelProviders.of(this).get(GetBugViewModel.class);

        //버그 이미지 받아서 넣어주기
        getBugImg();

        viewModel.getCount().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer count) {
                //카운트 세주기
                tv_count.setText("카운트 : " + count);

                //카운트에 따라 배경색 바꿔주기
                int colorPercent = count*10;
                layout_getBug.setBackgroundColor(Color.argb(colorPercent, 255, 175, 175));

                //카운트가 10이 되면 알람창 띄워주고 센서 종료
                showAlert(count);
            }
        });



        //벌레 애니메이션
        animRotate = AnimationUtils.loadAnimation(this, R.anim.rotate);


        //흔들기 센서
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);

    }

    private void getBugImg(){
        //뷰모델에서 버그 이미지 받아서 넣어주기
        iv_getBug.setImageResource(viewModel.getBugImg());
    }

    @Override
    protected void onStart() {
        super.onStart();
        sensorManager.registerListener(this, countSensor, SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    protected void onResume() {
        super.onResume();
        iv_getBug.startAnimation(animRotate);
    }

    @Override
    protected void onPause() {
        super.onPause();
        iv_getBug.clearAnimation(); //Todo. 이미지가 사라진다. 수정하자.
    }

    @Override
    protected void onStop() {
        super.onStop();
        sensorManager.unregisterListener(this);
    }

    //카운트가 10이 되면 알람창 띄워주고 센서 종료
    private void showAlert(int count){
        //카운트가 10이 되면 센서 종료 및 알람창 띄워주기
        if(count == 10){
            sensorManager.unregisterListener(this);
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("## 벌레를 잡았습니다 ##");
            builder.setMessage("잡은 벌레를 캐릭터에게 먹이겠습니까?");
            builder.setPositiveButton("예",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {

                            //벌레 등록 액티비티로 이동
                            Intent intent = new Intent(GetBugActivity.this, EatBugActivity.class);
                            intent.putExtra("bugImg", viewModel.getBugImg());
                            startActivity(intent);

                            finish();
                        }
                    });
            builder.setNegativeButton("아니오",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            //메인메뉴로 돌아가기
                            Intent intent = new Intent(GetBugActivity.this, MainActivity.class);
                            startActivity(intent);

                            finish();
                        }
                    });
            builder.show();
        }
    }


    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_STEP_DETECTOR) {

            //카운트 올리기
            viewModel.countUp();

        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }
}

 

*GetBugViewModel.class

public class GetBugViewModel extends ViewModel {

    private int bugImg;
    private MutableLiveData<Integer> count;

    public GetBugViewModel() {
        super();
        selectBug();
        count = new MutableLiveData<Integer>();
        count.setValue(0);
    }

    public MutableLiveData<Integer> getCount(){
        if(count == null){
            count = new MutableLiveData<Integer>();
        }
        return count;
    }

    public int getBugImg(){
        return bugImg;
    }

    //게임 시작시 랜덤으로 벌레 선택
    private int selectBug(){
        //랜덤 값에 따라 벌레 이미지 결정
        int bugRandom= (int)(Math.random()*10);
        if(bugRandom<2){
            bugImg = R.drawable.img_bug1;
        }
        else if(bugRandom >= 2 && bugRandom<5){
            bugImg = R.drawable.img_bug2;
        }
        else if(bugRandom >= 5 && bugRandom <8){
            bugImg = R.drawable.img_bug3;
        }
        else{
            bugImg = R.drawable.img_bug4;
        }

        return bugImg;
    }


    //걸음수가 인식되면 count 올리기
    public void countUp(){
        count.setValue(count.getValue() + 1);
    }


}

 

 

느낀점

1. 부끄럽지만 의식적으로 private를 활용하여 데이터를 보호한 것은 이번이 처음이다.

 - 앞으로도 객체 내의 데이터를 잘 보호하자.

 

2. 코드가 깔끔해지는 것을 느꼈다. 

 - 지저분했던 나의 방을 정리하는 기분이었다.

 - 정리가 되니 구조가 확실히 더 잘 보인다. 

 - 지금 프로젝트는 코드가 적으니 큰 차이 없지만 프로젝트의 규모가 커졌을때 큰 힘을 발휘할 것 같다.

반응형
Comments