안드로이드 With Java #30 Preference (환경 설정)

Jake Seo·2020년 9월 9일
0

안드로이드 With Java

목록 보기
30/31

안드로이드 With Java #30 Preference (환경 설정)

개발 환경

  • sdk 버전 29

개요

안드로이드에서는 Preference라는 개념을 이용하여 앱의 환경 설정을 조정할 수 있다. 공식문서는 여기에 있는데 한글 번역을 보면 내가 난독증인지 발번역인지 이해하기가 조금 어렵다.

기본적인 개념은 이전에 배웠던 Fragment를 이용하는 것이다. preference를 사용하기 위한 xml을 작성하고, FragmentActivity를 덮어씌워주면 된다. 기본적으로는 Fragment를 위한 xml을 작성하고 로직을 담고 있는 Java 파일을 작성하면 된다.

xml파일은 다음과 같은 양식으로 작성해주면 된다.

<PreferenceScreen
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <SwitchPreferenceCompat
        app:key="notifications"
        app:title="Enable message notifications"/>

    <Preference
        app:key="feedback"
        app:title="Send feedback"
        app:summary="Report technical issues or suggest new features"/>

</PreferenceScreen>

그러면 대략 아래와 같은 생김새의 화면이 생성된다.

(그림이 너무 크긴 한데) 우리가 앱에서 흔히 보는 환경설정 화면이다.

JAVA 소스는 대략 위와 같은 방식으로 구현하는 것을 권하고 있다.

특징

Preference라는 이름과 같이 설정사항들은 SharedPreference에 저장한다고 한다.

환경설정 Fragment 기본 세팅

일단은, Fragment로 구현한다는 사실에서 알 수 있듯 기존 Fragment를 이용한 화면 구현과 매우 비슷하다.

Fragment를 보여줄 Activity하나를 생성하고, onCreate()Fragment를 보여주는 코드를 작성하면 된다.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.some_activity);
    fragmentManager = getSupportFragmentManager();

    fragmentManager
            .beginTransaction()
            .replace(R.id.container_in_some_activity, new SettingsFragment())
            .commit();

    ActionBar actionBar = getSupportActionBar();
    if (actionBar != null) {
        actionBar.setDisplayHomeAsUpEnabled(true);
    }
}

위와 같이 작성하면 된다.

기본적으로 Activity를 위한 Layout을 하나 만들고 그 안에 Fragment가 들어갈 컨테이너를 하나 만드는 개념이다.

화면 보기

위와 같은 절차만 진행해도 이렇게 화면은 보인다.

다른 Fragment로 이동하며 설정화면 옮기는 방법

다른 Fragment로 이동하며 설정 화면을 옮긴다는 것은 위의 스크린샷의 내용과 같다. 세부 설정을 하기 위해 다른 Fragment로 옮겨야 하는 상황일 때 구현한다.

방법은 여기 공식문서에 잘 나와있다.

상황

나의 경우에는 본인인증을 3가지 방식 중 하나를 택하는 방식으로 구현했어야 했다.

  1. 패스워드
  2. PIN 번호
  3. 지문인식

onPreferenceStartFragment 콜백함수 설정하기

@Override
public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) {
    // Instantiate the new Fragment
    final Bundle args = pref.getExtras();
    final Fragment fragment = getSupportFragmentManager().getFragmentFactory().instantiate(
            getClassLoader(),
            pref.getFragment());
    fragment.setArguments(args);
    fragment.setTargetFragment(caller, 0);
    // Replace the existing Fragment with the new Fragment

    getSupportFragmentManager().beginTransaction()
            .replace(R.id.container_in_some_activity, fragment)
            .addToBackStack(null)
            .commit();
    return true;
}

위와 같이 설정 FragmentonPreferenceStartFragment() 콜백함수를 작성해준다.

연결할 Fragment를 설정하기

연결할 Fragment도 미리 xml을 이용하여 PreferenceScreen 형식으로 만들어둔다. 그리고 환경설정 Fragment를 처음 설정할 때 처럼 Activity에 연결해두면 된다.

@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.auth_preferences, rootKey);
}

그리고 Fragment가 잘 인식되도록 빈 생성자 하나를 만들어두는 것이 좋다고 한다.

나의 경우에는

public AuthSettingsFragment() {
}

위와 같은 생성자 하나를 만들어두었다.

참고로 Fragment는 절대 Activity 클래스 내부나 다른 자바파일에 내장되는 방식으로 만들어선 안된다. 연결될 Fragment는 반드시 다른 파일로 나뉘어 있어야 한다.

연결할 xml은 아래와 같은 방식으로 설정하면 된다. com.패키지명.Fragment명 이렇게 넣어놓으면 잘 연결된다.

<Preference
            app:fragment="com.....AuthSettingsFragment"
            />

Fragment에서 뒤로가기 버튼 동작하게 만들기

여기서 말하는 뒤로가기 버튼이란

위 버튼을 말한다.

사용자는 세부 설정을 한 뒤에 기존 설정창으로 나가고 싶을 것이다. 안드로이드 기본 UI에도 뒤로가기 버튼이 있지만 저 버튼을 이용하여 뒤로 가려고 하는 사용자도 분명 있을 것이다.

저 버튼에 대한 콜백함수는 onSupportNavigateUp()이다.

여기에 아래와 같이 코딩한다.

    @Override
    public boolean onSupportNavigateUp() {

        if(fragmentManager.getBackStackEntryCount() == 0) {
            finish();
        }else {
            fragmentManager.popBackStack();
        }

        return super.onSupportNavigateUp();
    }

위와 같이 코딩하면, Fragment 스택이 있을 때는 나를 pop()하여 이전 스택으로 가고, Fragment 스택이 없을 때는 Activity를 종료한다.

전체 java 소스

SettingsActivity.java (환경설정 메인)

import android.os.Bundle;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;

public class SettingsActivity extends AppCompatActivity implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback{
    FragmentManager fragmentManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.settings_activity);
        fragmentManager = getSupportFragmentManager();

        fragmentManager
                .beginTransaction()
                .replace(R.id.settings, new SettingsFragment())
                .commit();

        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
        }
    }

    @Override
    public boolean onSupportNavigateUp() {

        if(fragmentManager.getBackStackEntryCount() == 0) {
            finish();
        }else {
            fragmentManager.popBackStack();
        }

        return super.onSupportNavigateUp();
    }

    @Override
    public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) {
        // Instantiate the new Fragment
        final Bundle args = pref.getExtras();
        final Fragment fragment = getSupportFragmentManager().getFragmentFactory().instantiate(
                getClassLoader(),
                pref.getFragment());
        fragment.setArguments(args);
        fragment.setTargetFragment(caller, 0);
        // Replace the existing Fragment with the new Fragment

        getSupportFragmentManager().beginTransaction()
                .replace(R.id.settings, fragment)
                .addToBackStack(null)
                .commit();
        return true;
    }

    public static class SettingsFragment extends PreferenceFragmentCompat {
        @Override
        public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
            setPreferencesFromResource(R.xml.root_preferences, rootKey);
        }
    }

}

AuthSettingsFragment.java (본인인증 선택)

여기엔 3개의 체크상자중 하나의 체크상자를 클릭했을 때, 다이얼로그 메세지와 함께 단 하나의 체크상자만 체크하는 로직도 들어있다.

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.widget.Toast;

import androidx.preference.CheckBoxPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;

public class AuthSettingsFragment extends PreferenceFragmentCompat{

    AlertDialog.Builder adb;
    boolean dialogResult;
    CheckBoxPreference authPasswordCheckBoxPreference;
    CheckBoxPreference authFingerprintCheckBoxPreference;
    CheckBoxPreference authPinCheckBoxPreference;
    CheckBoxPreference selectedCheckBoxPreference;

    public AuthSettingsFragment() {
    }

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.auth_preferences, rootKey);
        adb = new AlertDialog.Builder(getActivity());
        adb.setTitle("정말로 변경하시겠습니까? 변경하면 이전 인증수단을 초기화합니다.");
        adb.setView(getView());
        adb.setPositiveButton("변경하기", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                authPasswordCheckBoxPreference.setChecked(false);
                authPinCheckBoxPreference.setChecked(false);
                authFingerprintCheckBoxPreference.setChecked(false);

                selectedCheckBoxPreference.setChecked(true);
            }
        });

        adb.setNegativeButton("취소", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                dialogResult = false;
            }
        });

        Preference.OnPreferenceChangeListener onPreferenceChangeListener = new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                selectedCheckBoxPreference = (CheckBoxPreference) preference;

                if(((CheckBoxPreference) preference).isChecked()){
                    Toast.makeText(getContext(), "현재 사용중인 인증수단입니다.", Toast.LENGTH_SHORT).show();
                    return false;
                }

                adb.show();
                return false;
            }
        };

        authPasswordCheckBoxPreference = findPreference("pref_auth_password");
        authPasswordCheckBoxPreference.setOnPreferenceChangeListener(onPreferenceChangeListener);

        authPinCheckBoxPreference = findPreference("pref_auth_pin");
        authPinCheckBoxPreference.setOnPreferenceChangeListener(onPreferenceChangeListener);

        authFingerprintCheckBoxPreference = findPreference("pref_auth_fingerprint");
        authFingerprintCheckBoxPreference.setOnPreferenceChangeListener(onPreferenceChangeListener);
    }
}

결과

클릭 시

화면 표출

변경하기 클릭 시에

체크된 것이 바뀜.

끝.

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글