[Android] Android configChanges option (안드로이드 화면 회전시 view 유지방법)

How to maintain view when screen direction change using configChanges option


안드로이드의 경우 화면이 회전되었을 경우 화면을 종료시키고 새로운 layout으로 재 시작한다.

즉 세로화면 -> 가로화면으로 전환 시 onDestroy() 함수가 호출되고 가로모드에서 다시 onCreate() 함수가 호출된다.

즉 환경변화가 일어날 경우 기본적인 동작은 activity 의 재시작이다.

이럴때 AndroidManifest.xml 파일에 ` android:configChanges`을 설정함으로써 activity가 reset되는것을 막을 수 있다.

이런경우 activity의 onDestroy()onCreate() 함수 대신에 onConfigurationChanged()함수가 호출된다.

이러한 옵션이 왜 필요한지 예를 들어보자.

사용자의 이름을 입력하는 Edittext를 1개 포함하는 화면이 있다고 하자.

화면이 처음 구성되는 onCreate()함수에서 사용자의 이름변수를 null로 초기화 하고, 화면이 구성된 이후에 사용자가 이름을 입력하였다.

만약 이때 화면이 가로모드에서 세로모드로 변경되면 화면이 종료되고 다시 시작되기 때문에 사용자의 이름변수는 다시 null이 입력되어 있게 된다.

이런 상황에서 원하는 동작은 사용자가 이름을 입력하였기 때문에 Edittext는 사용자가 입력된 내용을 그대로 유지하고 있어야 한다. 이럴때 configChanges 옵션중에서 orientation 옵션을 주면 화면의 방향이 변경되어도 초기화 되지 않는다.

적용시키는 방법은 다음과 같다.

1. AndroidManifest.xml 수정

AndroidManifest.xml 파일에 android:configChanges="orientation" 을 추가해준다. activity안에 넣어주어야 한다. 다른 설정과 함께 작성하면 다음과 같다.

 <activity
     android:name=".MainActivity"
     android:label="@string/app_name"
     android:windowSoftInputMode="stateAlwaysHidden"
     android:configChanges="orientation"
     android:theme="@style/AppTheme.NoActionBar">
 </activity>

위의 옵션은 화면의 방향이 변해도 화면이 초기화 되지 않도록 하며 (android:configChanges="orientation"), fragment가 변경되었을 때 항상 soft 키보드를 숨기는 옵션 (android:windowSoftInputMode="stateAlwaysHidden") 을 적용하였다. 키보드 관련된 내용은 다음 글을 참고하면 된다.

2. onConfigurationChanged() 함수 작성

1번과 같이 옵션을 추가하고 화면의 방향이 변경 시, onConfigurationChanged() 함수가 호출된다. 만약 화면의 방향이 전환되었을 때 수행해야 하는 작업이 있는 경우 이 함수에 추가하면 된다.

3. configChanges의 여러가지 옵션들

참고사이트: https://aroundck.tistory.com/36

위와 같이 다양한 옵션을 사용할 수 있다. 위의 옵션은 or 구분자(|)를 통해 다음과 같이 중복 입력 가능하다

android:configChanges="orientation|screenLayout"



[Android] Android fragment transition with animation (안드로이드 프레그먼트 애니메이션 화면전환)

Fragment transition with animation


이 글에서는 fragment의 화면전환 할 때 애니메이션을 넣는 방법에 대해서 설명한다.

1. 애니메이션 정의

화면전환에 대한 애니메이션을 각각 정의해 주어야 한다.

애니메이션을 정의하는 경로는 res/anim/파일명.xml이다 .

다양한 동작을 하는 애니메이션들을 정의해보자

enter_from_left.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"  >
    <objectAnimator
    android:propertyName="x"
    android:duration="600"
    android:valueFrom="-400dp"
    android:valueTo="0"
    android:valueType="floatType" />
</set>

enter_from_right.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <objectAnimator
    android:duration="600"
    android:propertyName="x"
    android:valueFrom="400dp"
    android:valueTo="0"
    android:valueType="floatType" />
</set>

exit_to_left.xml

<?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android" >
    <objectAnimator
    android:duration="600"
    android:propertyName="x"
    android:valueFrom="0"
    android:valueTo="-400dp"
    android:valueType="floatType" />
</set>

exit_to_left.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
    android:duration="600"
    android:propertyName="x"
    android:valueFrom="0"
    android:valueTo="400dp"
    android:valueType="floatType" />
</set>

모든 동작은 좌우로 fragment를 이동하는 애니메이션이며 property의 이름은 x 이다.

이동하는 애니메이션 뿐만 아니라 fade_in, fade_out 애니메이션도 다음과 같이 정의할 수 있다.

fade_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"  >
    <objectAnimator
    android:duration="600"
    android:propertyName="alpha"
    android:valueFrom="0"
    android:valueTo="1"
    android:valueType="floatType"
    />
</set>

fade_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"  >
    <objectAnimator
    android:duration="600"
    android:propertyName="alpha"
    android:valueFrom="1"
    android:valueTo="0"
    android:valueType="floatType"
    />
</set>

2. 애니메이션을 통한 Fragment 전환

애니메이션을 적용한 fragment의 전환을 위해서는 기본적은 fragment transition에 setCustomAnimations 옵션을 추가해주면 된다. 다음은 이전 화면은 왼쪽으로 나가면서 오른쪽에서 새로운 fragment가 들어오는 애니메이션이 적용되는 예이다.

FragmentManager manager = getFragmentManager();
manager.beginTransaction()
    .setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left,R.anim.exit_to_right)
    .replace(R.id.content_main, fragment)
    .commit();

위의 예제는 뒤로가기를 눌렀을 때 이전 fragment로 가지 않도록 stack에 누적하지 않는 코드이다. 만약 뒤로가기를 눌렀을 때 다시 이전 화면으로 돌아가는 코드로 만들고 싶다면 transaction.addToBackStack(null) 를 추가해주면 된다.

setCustomAnimations의 인자는 총 4개이다.

첫번째 인자는 fragment의 transition이 수행되었을 때 새로운 fragment의 애니메이션이며 두번째 인자는 기존에 화면에 있던 fragment의 애니메이션이다.

즉 새로운 fragment는 오른쪽에서 들어오면서 기존에 있던 fragment는 왼쪽으로 나간다.

세번째 인자는 뒤로가기를 눌렀을 때, 즉 이전화면으로 이동 시의 애니메이션이다.

즉 stack에 쌓여있던 fragment는 왼쪽에서 들어오며, 기존에 있던 fragment는 오른쪽으로 사라진다.


[Android] Android data path and method (안드로이드 저장소 경로 및 경로 획득 함수)

Data path and method (저장소 경로 및 경로 획득 함수)


이 글은 이 글을 참고하여 작성하였다.

안드로이드 시스템에서 데이터 저장 장소는 크게 2가지로 나뉘어진다.

첫번째는 내부 저장소로 이 공간은 해당 앱에서만 접근 가능하다. 유저도 이 공간에는 직접 접근할 수 없다.

두번째는 외부 저장소로 외부저장소는 공용영역과 고유영역으로 나뉘어진다.

외부저장소의 공용영역은 다양한 어플이 접근할 수 있는 영역이며 어플이 삭제되어도 이 경로에 저장된 데이터는 유지된다.

반면 외부저장소의 고유영역은 다른 어플도 접근 가능하며, 어플이 삭제시 이 공간의 데이터도 삭제된다.

보안을 위해서는 외부 어플이 접근할 수 없도록 내부 저장소에 저장하는 것이 안전하며,

만약 내부 저장소에 저장된 데이터를 외부 어플로 전달하고 싶은경우 (email 첨부, 다른어플로 열기 등)에는 fileprovider를 사용해야 한다.

fileprovider를 이용해서 데이터를 전달하는 방법에 대해서는 다음 글을 참고하기 바란다.

내부저장소

각 애플리케이션에서만 데이터를 읽고 쓸 수 있다.

1. 캐시(Cache)

임시 파일들이 저장된다.

File Context.getCacheDir()

내부 저장소의 캐시 디렉터리 경로를 반환한다.

경로: /data/data/패키지 이름/cache

2. 데이터베이스(Database)

데이터베이스 파일들이 저장된다.

File Context.getDatabasePath(String name)

데이터베이스 파일의 경로를 반환. 인자로 데이터베이스 파일의 이름을 넘겨준다.

경로: /data/data/패키지 이름/databases

3. 일반 파일

일반 파일이 저장되는 영역이다. 

이 경로는 Context.openFileOutput(String, int)를 사용하여 생성되는 파일이 저장되는 경로와 동일하다.

File Context.getFilesDir()

일반 파일들의 저장 경로를 반환한다.

경로: /data/data/패키지 이름/files

각 일반 파일들의 경로를 가져오기

File Context.getFileStreamPath(String name)

일반 파일이 저장된 공간에서 특정 이름을 가지는 파일의 경로를 반환한다.

인자로 확장자를 포함한 파일 이름을 넘겨준다.

경로: /data/data/패키지 이름/files/파일이름

외부저장소 (공용영역)

애플리케이션을 삭제해도 데이터는 남아있다.

1. 최상위 경로

외부 저장소(SD카드)의 최상위 경로를 반환한다.

static File Environment.getExternalStorageDirectory()

경로: /mnt/sdcard 또는 /storage/emulated/0 등 기종마다 다르다.   

2. 특정 데이터를 저장하는 영역

여러 애플리케이션에서 공용으로 사용할 수 있는 데이터들을 저장하며 데이터의 유형에 따라 별도의 디렉터리를 사용한다.

이 영역에 데이터를 저장하기 전에, 해당 디렉터리가 존재하는지 확인해야 한다. 존재하지 않으면 FileNotFoundException이 발생하기 때문에 File.mkdirs()를 사용하여 없을 경우 새 디렉터리를 생성해 준다. 

다음의 함수를 이용해서 external 디렉터리가 존재하는지, 그리고 write가 가능한지 확인할 수 있다.

public static boolean checkAvailable() {

    // Retrieving the external storage state
    String state = Environment.getExternalStorageState();

    // Check if available
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

public static boolean checkWritable() {
    // Retrieving the external storage state
    String state = Environment.getExternalStorageState();

    // Check if writable
    if (Environment.MEDIA_MOUNTED.equals(state){
        if(Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            return false;
        }else{
            return true;
        }
    }
    return false;
}

외부 저장소의 해당 경로를 얻기위해서는 다음 함수를 사용한다

static File Environment.getExternalStoragePublicDirectory(String type)

인자로 디렉터리의 유형을 넘겨준다.

[각 인자 및 경로]

외부저장소 (고유영역)

외부 저장소 - 애플리케이션 고유 영역

이 영역에 저장된 데이터는 애플리케이션이 삭제될 때 같이 삭제되며 다른 애플리케이션에서 해당 데이터에 접근하는 것이 가능하다.

1. 특정 데이터를 저장하는 영역

애플리케이션 고유 영역에도 공용 영역과 마찬가지로 각 데이터 유형별로 데이터를 저장하는 표준 디렉터리를 제공한다.

File Context.getExternalFilesDir(String type)

인자로 디렉터리의 유형을 넘겨준다.

[각 인자 및 경로]

2. 캐시 데이터를 저장하는 영역

애플리케이션에서 사용하는 임시 데이터를 외부 저장소에 저장한다.

File Context.getExternalCacheDir()

경로: /mnt/sdcard/Android/data/패키지 이름/cache




[Android] Android export file in internal storage (안드로이드 내부 저장소의 데이터 출력, fileprovider)

export file in internal storage (내부 저장소의 데이터 출력, fileprovider)


안드로이드에서는 보안을 위해서 데이터를 내부에 저장하는 것이 가장 안전한 방법이다.

안드로이드의 데이터 저장위치에 대한 내용을 검색해 보면 크게

내부저장소, 외부저장소 (공유영역, 어플고유영역)으로 나뉘어 진다.

내부저장소는 어플 자체에서만 접근 가능하며, 사용자도 접근하지 못한다.

외부저장소의 공유영역은 모든 어플에서 접근 가능하며, 어플을 삭제해도 데이터가 남는 영역이다.

외부저장소의 고유영역은 모든 어플에서 접근 가능하지만, 어플을 삭제할때 데이터도 같이 삭제되는 영역이다.

이러한 각 데이터 저장 영역에 대해서는 많은 포스팅이 있으니 참고하자.

http://jinyongjeong.github.io/2018/09/30/android_path/

http://ismydream.tistory.com/124

http://bitsoul.tistory.com/117

https://developer88.tistory.com/28

만약 보안을 위해서 내부영역에 데이터를 저장했는데 이메일을 통해서 데이터를 출력하고 싶다면 어떻게 해야할까.

가장 쉽게 생각할 수 있는것이 외부영역으로 데이터를 복사한 후에 데이터를 출력하고 삭제하는 방법이다.

하지만 이럴때 사용할 수 있는 것이 fileprovider이다.

fileprovider는 내부 영역에 특정 파일을 외부 어플에서 접근 가능하도록 제공해 주는 역할을 하며

특정 파일을 지정해서 공유할 수 있기 때문에 안전하다.

fileprovider를 사용하기 위해서는 다음과 같은 절차가 필요하다.

1. Manifest 설정

AndroidManifest.xmlapplication 안쪽에 선언

<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>

2. Provider의 경로 설정

res/xml/file_path.xml 을 생성하고 다음을 입력

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <paths>
        <files-path path="/" name="default" />
    </paths>
</paths>

경로가 xml 폴더가 아닐경우 오류가 난다

3. Email을 통해 데이터 출력

File file = new File(activity.getFilesDir(), "/exported_data.zip");
Uri uri = FileProvider.getUriForFile(context, activity.getPackageName()+".fileprovider", file);
Intent i = new Intent(Intent.ACTION_SEND);
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
i.setType("message/rfc822");
i.putExtra(Intent.EXTRA_SUBJECT, "Gaitspeedometer data");
i.putExtra(Intent.EXTRA_STREAM, uri);

try {
    startActivity(Intent.createChooser(i, "Send file..."));
} catch (Exception e) {
    Toast.makeText(activity, "No activity found for export", Toast.LENGTH_SHORT).show();
}

위와 같이 코드를 실행하면 내부 영역에 있는 exported_data.zip파일이 이메일에 첨부되고 출력할 수 있게 된다.


[Ubuntu] Ubuntu16.04 Wifi problem in Macbook(맥북 ubuntu16.04 Wifi 문제)

Ubuntu 16.04 Wifi problem in Macbook


Macbook에 듀얼부팅으로 ubuntu16.04를 설치하였을 때 Wifi가 정상적으로 잡히지 않는 문제가 발생한다.

구글링을 해보면 다음과 같은 답변이 많이 나오는데 나의경우에는 해결되지 않았다.

sudo apt-get install firmware-b43-installer
sudo modprobe -r b43
sudo modprobe b43

위와 같은 방법 말고 bcmwl 커널소스를 재 설치 하는 방법으로 해결되었다.

혹시 이글을 보고 있다면 위에 코드 실행하기 전에 아래 코드를 먼저 실행해 보기 바란다.

sudo apt-get update
sudo apt-get install --reinstall bcmwl-kernel-source

기타 Mac 에 우분투를 설치시 다양한 문제들에 대한 정보는

다음 링크에서 찾아볼수 있다.


Pagination