스택오버플로우에서 찾은 여러 방법 중 이게 제일 깔끔하게 작동했다..

 

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    View view = getCurrentFocus();
    if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText && !view.getClass().getName().startsWith("android.webkit.")) {
        int scrcoords[] = new int[2];
        view.getLocationOnScreen(scrcoords);
        float x = ev.getRawX() + view.getLeft() - scrcoords[0];
        float y = ev.getRawY() + view.getTop() - scrcoords[1];
        if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y > view.getBottom())
        ((InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((this.getWindow().getDecorView().getApplicationWindowToken()), 0);
    }
    return super.dispatchTouchEvent(ev);
}

 

출처:https://stackoverflow.com/questions/8697499/hide-keyboard-when-user-taps-anywhere-else-on-the-screen-in-android/8697635

 

hide keyboard when user taps anywhere else on the screen in android

I need to hide the softkeypad in android when user click on anywhere other than a Edittext. There are many help for iphone but not for android. I tried this code but its not working :( final

stackoverflow.com

 

안드로이드는 여러가지 방법으로 앱 데이터를 저장한다.

저장 방법의 선택은 데이터의 크기와 어떤 종류의 데이터를 저장하는지, 개발중인 앱에서만 사용하는 데이터인지 다른 앱이나 사용자가 접근할 수 있는지에 따라 달라진다.

 

내부 파일 저장소: 기기 파일 시스템에 앱에서만 사용할 수 있도록 저장한다.

외부 파일 저장소: 외부 파일 시스템에 파일을 저장한다. 사용자가 접근할 수 있어야하는 경우 사용한다.

공유 기본 설정: 데이터를 키-값으로 저장한다.

데이터베이스: 데이터베이스에 구조화하여 데이터를 저장한다.

 

외부 파일 저장소를 제외하고 다른 방법은 데이터를 다른 앱에서 접근할 수 없다. 공유하고자하는 경우 FileProvider API를 사용해야한다.

 

앱의 데이터를 다른 앱이 사용할 수 있게 하는 경우, ContentProvider를 사용하면 된다. 콘텐츠 프로바이더는 데이터를 저장 방식과 상관없이 다른 앱에서 읽기/쓰기를 제어할 수 있도록 권한을 준다.

 

내부 저장소

개별 어플리케이션에서 사용하는 전용 파일이다.

내부 앱 데이터를 저장하는데 사용된다.

사용자가 앱을 삭제하면 저장된 파일이 삭제된다.

그렇기에 데이터가 앱을 지워도 남게하려면 MediaStore API를 사용해서 적절한 저장소에 저장해야한다.

 

외부 저장소

모든 안드로이드 기기는 파일을 저장하는데 사용할 수 있는 외부 저장소를 가지고 있다.

파일에 접근하기 전에 외부 저장소 디렉토리를 사용할 수 있는지 확인해야한다.

 

다른 앱에서도 접근해야하고 앱 제거 뒤에도 사용해야하는 데이터라면 외부 저장소를 이용하는 것이 좋다.

 

공유 기본 설정 (SharedPreferences)

데이터를 많이 저장하지 않고, 데이터 구조가 필요하지 않는 경우 사용한다.

boolean, float, int, long, string 유형의 데이터를 키-값으로 쓰고 읽을 수 있다.

이것은 XML파일에 작성되고, 앱이 종료되더라도 유지된다.

파일 이름을 직접 지정하거나 액티비티별로 파일을 사용해 데이터를 저장할 수 있다.

 

 

데이터베이스

안드로이드는 SQLite를 완전히 지원한다. 생성한 모든 데이터 베이스는 앱에의해서만 사용이 가능하다.

SQLite API를 직접사용하는 대신 Room 라이브러리로 데이터베이스를 생성하고 관리하는 것이 좋다.

 

Room라이브러리는 SQLite를 완벽하게 활용하는 객체 매핑 추상화 계층을 제공한다.

 

 

프래그먼트는 어떤 동작이나 UI의 일부입니다.

여러개의 프래그먼트를 하나의 액티비티에 결합하여 창이 여러개인 UI를 만들 수 있습니다.

하나의 프래그먼트를 여러 액티비티에서 재사용할 수 있습니다.

 

프래그먼트는 액티비티의 모듈형 부품이라고 생각하면 됩니다.

 

프래그먼트는 수명주기를 가지고 있고 입력 이벤트를 수신할 수 있고, 액티비티 실행 중에 추가 및 삭제가 가능합니다.

즉, 다른 액티비티에서 다시 사용할 수 있는 하위 액티비티와 같은 개념입니다.

 

프래그먼트는 항상 액티비티 내에 속해야하며, 프래그먼트의 수명 주기는 호스트 액티비티의 수명 주기에 영향을 받습니다.

액티비티가 일시정지된 경우, 거기 속한 모든 프래그먼트도 일시정지 되며 액티비티가 소멸되면 모든 프래그먼트도 소멸됩니다.

액티비티가 실행중인 동안에는 각 프래그먼트를 추가, 제거하는 등 개별적으로 조작할 수 있습니다.

이러한 프래그먼트 조작(트랜잭션)을 수행하는 경우 이 것을 액티비티가 관리하는 백 스택에도 추가할 수 있습니다.

각 백 스택 항목이 발생한 프래그먼트 트랜잭션의 기록이 됩니다. 이 백 스택을 사용해 사용자가 프래그먼트 조작(트랜잭션)을 다시 되돌릴 수 있습니다. 이때 뒤로가기 버튼을 누르면 됩니다.

 

프래그먼트를 액티비티 레이아웃에 추가하면, 해당 프래그먼트는 액티비티의 뷰 계층 내에서 뷰그룹에 들어가고 자체적인 뷰 레이아웃을 정의합니다. (뷰그룹이란 다른 뷰를 포함할 수 있는 뷰입니다. 레이아웃, 컨테이너)

액티비티의 레이아웃 파일에서 <fragment>요소로 프래그먼트를 선언하거나 기존 뷰그룹에 추가하는 방법으로 애플리케이션 코드에서 프래그먼트를 선언하면 액티비티 레이아웃에 프래그먼트를 삽입할 수 있습니다.

 

수명 주기

 

 

뷰모델 개요

뷰모델 클래스는 수명주기를 고려하여 UI관련 데이터를 저장하고 관리하도록 설계되었습니다.

이 클래스를 이용하여 화면 회전과 같이 구성을 변경할 때도 데이터를 유지할 수 있습니다.

뷰 모델 클래스는 수명주기에 상관없이 데이터를 사용하기위해 사용한다.

 

 

안드로이드에서는 액티비티 및 프래그먼트와 같은 UI컨트롤러의 수명주기를 관리합니다.

프레임워크는 특정 사용자 작업이나 완전히 통제할 수 없는 기기 이벤트에 대한 응답으로 UI컨트롤러를 제거하거나 다시 만들도록 결정할 수 있습니다.

안드로이드는 상황에 따라 리프레시가 일어난다.

 

시스템에서 UI컨트롤러를 제거하거나 다시 만들면 컨트롤러에 저장된 일시적인 모든 UI 관련 데이터가 손실됩니다.

예를 들어 앱에 있는 액티비티 중 하나는 사용자 목록 데이터를 가지고 있을 수 있습니다.

Config 변경을 위해 액티비티를 다시 생성하면 새 활동은 사용자 목록을 다시 가져와야 합니다.

데이터가 단순한 경우 활동은 onSaveInstanceState() 메서드를 이용해 onCreate()번들에서 데이터를 복원할 수 있습니다.

하지만 이 접근 방법은 사용자 목록이나 비트맵과 같은 대용량 데이터가 아니라, 직렬화 후 역직렬화할 수 있는 소량의 데이터에만 적합합니다.

리프레시가 일어나 다시 실행되는 경우, UI관련 데이터가 사라진다. 이런 일이 일어나기에 뷰모델을 사용한다.

 

액티비티 및 프래그먼트와 같은 UI 컨트롤러는 주로 UI 데이터를 표시하거나, 사용자 작업에 반응하거나, 권한 요청과 같은 운영체제 커뮤니케이션을 처리하기 위한 것입니다. 또한 UI 컨트롤러에 데이터베이스나 네트워크에서 데이터 로드를 담당하도록 요구하면 클래스가 팽창됩니다. UI 컨트롤러에 과도한 책임을 할당하면 단일 클래스가 다른 클래스에 작업을 위임하지 않고 홀로 모든 앱 작업을 처리하려고 할 수 있습니다. 또한 이런 방법으로 UI 컨트롤러에 과도한 책임을 할당하면 테스트가 훨씬 더 어려워집니다.

액티비티나 프래그먼트는 사용자와의 인터페이스 역할을 하는데, 여기에 비즈니스 로직을 넣으면 관리가 어려워진다.

 

UI 컨트롤러 로직에서 뷰 데이터 소유권을 분리하는 방법이 훨씬 더 쉽고 효율적입니다.

데이터를 따로 관리하는 것이 더 쉽고 효율적이다.

 

뷰모델 구현

아키텍처 구성요소는 UI의 데이터 준비를 담당하는 UI 컨트롤러에 뷰모델 도우미 클래스를 제공합니다.

뷰모델 개체는 구성이 변경되는 동안 자동으로 보관되므로, 이러한 개체가 보유한 데이터는 다음 활동 또는 프래그먼트 인스턴스에서 즉시 사용할 수 있습니다. 예를 들어 앱에서 사용자 목록을 표시해야 한다면 다음 샘플 코드에서와 같이 사용자 목록을 확보하여 활동이나 프래그먼트 대신 뷰모델에 보관하도록 책임을 할당해야 합니다.

public class MyViewModel extends ViewModel {
        private MutableLiveData<List<User>> users;
        public LiveData<List<User>> getUsers() {
            if (users == null) {
                users = new MutableLiveData<List<User>>();
                loadUsers();
            }
            return users;
        }

        private void loadUsers() {
            // Do an asynchronous operation to fetch users.
        }
    }

그 후 다음과 같이 액티비티에서 목록에 접근할 수 있습니다.

    public class MyActivity extends AppCompatActivity {
        public void onCreate(Bundle savedInstanceState) {
            // Create a ViewModel the first time the system calls an activity's onCreate() method.
            // Re-created activities receive the same MyViewModel instance created by the first activity.

            MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
            model.getUsers().observe(this, users -> {
                // update UI
            });
        }
    }
    

액티바티가 다시 생성되면 첫 번째 액티비티에서 생성된 동일한 MyViewModel 인스턴스를 받습니다. 소유자 활동이 완료되면 프레임워크는 리소스를 정리할 수 있도록 뷰모델 개체의 onCleared() 메서드를 호출합니다.

*주의: 뷰모델은 뷰, 라이브사이클 또는 액티비티 컨텍스트에 대한 참조를 포함하는 클래스를 참조하면 안된다.

 

뷰모델 객체는 뷰 또는 LifecycleOwners의 인스턴스보다 오래 남아있도록 설계되었습니다. 이러한 설계로 뷰 및 라이프사이클 객체에 대해 알지 못해도 뷰모델을 다루는 테스트를 쉽게 작성할 수 있습니다.

뷰모델 객체에는 라이브데이터 객체같은 LifecycleObsers가 포함될 수 있습니다. 그러나 뷰모델 객체는 라이브데이트 객체같은 수명주기를 인식하는 Observable의 변경상항을 관찰해서는 안됩니다.

뷰모델은 시스템 서비스를 찾는 데 애플리케이션 컨텍스트가 필요하면 안드로이드 뷰모델 클래스를 확장하고 생성자에 애플리케이션을 받는 생성자를 포함할 수 있습니다.

 

뷰모델의 수명 주기

뷰모델 객체의 범위는 뷰모델을 가져올 때 뷰모델프로바이더에 전달되는 라이프사이클로 지정됩니다.

뷰모델은 범위가 지정된 라이프사이클이 영구적으로 경과될 때 까지, 즉 액티비티가 끝날때까지, 혹은 프래그먼트가 분리될 때까지 메모리에 남아있습니다.

뷰모델은 라이프사이클이라는 값으로 범위가 지정된다. 그리고 범위가 지정된 만큼 메모리에 남아있다.

 

일반적으로 시스템에서 액티비티 객체의 onCreate()메서드를 처음 호출할 때 뷰모델을 요청합니다.

 

1. Manifest파일에 속성을 추가한다.

 

액티비티에 screenOrientation이라는 속성을 추가하여 landscape로 할지, portrait로 할지 설정하면 된다.

 

        <activity android:name=".MapActivity" android:screenOrientation="landscape"/>
        <activity android:name=".StoryActivity" android:screenOrientation="landscape"/>
        <activity android:name=".StartActivity" android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

+ Recent posts