Internal Storage

 - Permission 없이 사용 가능

 - 저장된 파일은 자신의 앱에서만 액세스 가능

 - 앱을 지우면 파일도 함께 소멸

 - 캐시파일의 개념과 비슷

 

External Storage

 - Permission 권한 필요

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 - SD카드 등의 외부저장소에 파일을 저장

 - 스마트폰의 저장소 또한 이곳에 포함 (ex. Download 폴더 등)

 - getExternalStorageState() 함수를 호출하여 외부 저장소가 사용 가능한지에 대해 확인

 

//외부 저장소(External Storage)가 마운트(인식) 되었을 때 동작
//getExternalStorageState() 함수를 통해 외부저장장치가 Mount 되어 있는지를 확인 
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
	//다운로드 폴더에 "file.txt" 이름으로 txt 파일 저장
    //Environment.DIRECTORY_DOWNLOADS - 기기의 기본 다운로드 폴더
	File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(), "file" + ".txt");
	try{
		FileWriter fw = new FileWriter(file, false);
		fw.write("TEST MESSAGE");
		fw.close();
	} catch (IOException e){
		e.printStackTrace();
		Toast.makeText(getApplicationContext(),"ERROR",Toast.LENGTH_SHORT).show();
	}
}
else{
	Toast.makeText(getApplicationContext(),"ERROR",Toast.LENGTH_SHORT).show();
}

안드로이드 10, Q 부터는...

 

안드로이드 Target API 29 (안드로이드 10, Q) 부터 보안상의 이유로 

외부 저장소(External Storage)에 접근 불가.

 

   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

   <application
		....
        android:requestLegacyExternalStorage="true">

임시방편으로 AndroidManifest.xml 내에 android:requestLegacyExternalStorage="true" 사용을 해주면 되지만

오직 임시방편일 뿐

 

이전 코드를 사용해 보면 알겠지만 /sdcard  이하의 영역에는 접근이 불가능하며 

open failed: EACCES (Permission denied) 에러가 나옴

 

그렇다보니 최상위 경로를 가지고오도록 했던 Environment.getExternalStorageDirectory()는 deprecated 되었다.

 

따라서 안드로이드 Q 이상부터 외부 저장소의 접근해 파일을 읽고 쓰기 위해서는

MediaStoreSAF(Storage Access Framework)를 이용하는 것을 추천

 

이미지, 동영상, 오디오 파일과 같은 미디어 형식의 파일을 처리할 때는 MediaStore를 사용하고,

Documents(*.txt, *.pdf 등), 기타 파일들을 읽고 쓰려면 SAF를 사용.

 

SAF 사용...

 

 

 

 

 

 

 

 

 

 

 

 

블로그 이미지

jokey12

모든 글은 내가 보려고 작성함 Contact : jsung9912@gmail.com

,

화면(액티비티) 전환

 

AndroidManifest.xml 내에 전환 될 액티비티(SecondActivity) 추가

// <application> 내에 추가
<activity android:name=".SecondActivity" />
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.test">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Test">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <activity android:name=".SecondActivity" />

    </application>

</manifest>

 

//MainActivity 코드

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);

 


값 전달

 

intent로 값 전달은 putExtra( key, value ) 형식으로 전달한다.

//MainActivity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("숫자", 12345);
intent.putExtra("문자", "가나다라");
startActivity(intent);

 

Second_Activity에서는 getIntExtra, getStringExtra, getCharExtra, getBooleanExtra, getByteExtra 등 형식에 맞게

MainActivity에서 지정한 key값인 "숫자" , "문자" 에 맞는 값을 각각 받아올 수 있다.

//Second_Activity
TextView tv = (TextView) findViewById(R.id.tv);

Intent intent = getIntent();
int num = intent.getIntExtra("숫자", 0);
String message = intent.getStringExtra("문자");

tv.setText(num + message);

 

 

 

블로그 이미지

jokey12

모든 글은 내가 보려고 작성함 Contact : jsung9912@gmail.com

,

오버라이드( Ctrl + O )로 onBackPressed 함수 호출

 

뒤로가기 두번 눌러 앱 종료

    long pressedTime = 0; //'뒤로가기' 버튼 클릭했을 때의 시간

    @Override
    public void onBackPressed() {

        //마지막으로 누른 '뒤로가기' 버튼 클릭 시간이 이전의 '뒤로가기' 버튼 클릭 시간과의 차이가 2초보다 크면
        if(System.currentTimeMillis() > pressedTime + 2000){
            //현재 시간을 pressedTime 에 저장
            pressedTime = System.currentTimeMillis();
            Toast.makeText(getApplicationContext(),"한번 더 누르면 종료", Toast.LENGTH_SHORT).show();
        }
        
        //마지막 '뒤로가기' 버튼 클릭시간이 이전의 '뒤로가기' 버튼 클릭 시간과의 차이가 2초보다 작으면
        else{
            Toast.makeText(getApplicationContext(),"종료 완료", Toast.LENGTH_SHORT).show();
            // 앱 종료
            finish();
        }
    }

 

블로그 이미지

jokey12

모든 글은 내가 보려고 작성함 Contact : jsung9912@gmail.com

,

안드로이드 6.0 (마시멜로) 이후 버전부터 유저권한설정 필요

초기 권한 설정

 

1. AndroidManifest.xml 에 권한 부여

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lee.woosuk">

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        ..생략..

권한 목록

CALENDAR
CAMERA
CONTACTS
LOCATION
MICROPHONE
PHONE
SENSORS
SMS
STORAGE

 

앱이 실행되면 권한창을 띄워주고, 권한을 허가하지 않으면 앱 종료 코드.

public class MainActivity extends AppCompatActivity {

    String[] permission_list = {
            Manifest.permission.INTERNET,
            Manifest.permission.RECORD_AUDIO,
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        checkPermission();
    }
 
    
    public void checkPermission(){
        //현재 안드로이드 버전이 6.0미만이면 메서드를 종료한다.
        //안드로이드6.0 (마시멜로) 이후 버전부터 유저 권한설정 필요
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
            return;
 
        for(String permission : permission_list){
            //권한 허용 여부를 확인한다.
            int chk = checkCallingOrSelfPermission(permission);
 
            if(chk == PackageManager.PERMISSION_DENIED){
                //권한 허용을여부를 확인하는 창을 띄운다
                requestPermissions(permission_list,0);
            }
        }
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode==0)
        {
            for(int i=0; i<grantResults.length; i++)
            {
                //허용됬다면
                if(grantResults[i]==PackageManager.PERMISSION_GRANTED){
                }
                else {
                    //권한을 하나라도 허용하지 않는다면 앱 종료
                    Toast.makeText(getApplicationContext(),"앱권한설정하세요",Toast.LENGTH_LONG).show();
                    finish();
                }
            }
        }
    }
 
}

 

블로그 이미지

jokey12

모든 글은 내가 보려고 작성함 Contact : jsung9912@gmail.com

,

app - res - drawable 밑에 xml 리소스 파일 생성

 

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    //버튼 pressed 했을때
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <corners android:radius="50sp"/>
            <solid android:color="#ff0000"/>
        </shape>
    </item>
    
    
    //일반 버튼 상태
    <item>
        <shape android:shape="rectangle">
            <corners android:radius="50sp"/>
            <solid android:color="#00ff00"/>
        </shape>
    </item>
    
    
    
</selector>

 

item 태그 하위 속성

state_pressed 객체를 누를 때 (ex : 버튼을 터치하거나 클릭 할 때)
이 항목을 사용해야하는 경우 "true"

이 항목을 누르지 않은 기본 상태로 사용해야하는 경우 "false"
state_checked 개체를 확인할 때
이 항목을 사용해야하는 경우 "true"
객체를 체크하지 않은 상태에서 사용해야하는 경우 "false"
state_enabled 개체가 활성화되어있을 때
이 항목을 사용해야하는 경우 "true"(터치 / 클릭 이벤트 수신 가능)
개체를 사용할 수 없을 때 사용해야하는 경우 "false"
state_focused 버튼이 강조 표시 될 때와 같이 객체에 포커스가있을 때
이 항목을 사용해야하는 경우"true "
이 항목을 초점이없는 기본 상태로 사용해야하는 경우 "false"
state_selected 개체를 선택할 때 (ex : 탭을 열 때와 같이)
이 항목을 사용해야하는 경우 "true"
 
개체를 선택하지 않은 상태에서이 항목을 사용해야하는 경우 "false"

 

코너 둥글기

<corners android:radius="50sp"/>

 

배경 색상

<solid android:color="#ff0000"/>

 

 

*유의 사항

 

이벤트 효과를 갖는 태그( <item android:state_pressed="true"> )를 먼저 작성

만약 효과가 없는 태그보다 밑에 위치한다면 아무런 변화가 없음


 

적용 방법

    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/button1"/>

background 속성에 @drawable/button1 적용해주면 완료

 

 

결과

버튼 누르기 전

 

 

버튼 누르고 있는 상태


추가) 이미지 버튼

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_pressed="true"
        android:drawable="@drawable/img_clicked">
    </item>

    <item
        android:drawable="@drawable/img">
    </item>

</selector>

itme 태그 내에 drawable 경로 추가

블로그 이미지

jokey12

모든 글은 내가 보려고 작성함 Contact : jsung9912@gmail.com

,

환경

* Android Studio 4.1.1

* SDK API 29 (Android 10)

 

레이아웃 xml 편집 시에 버튼의 백그라운드 색상 변경 및 커스텀 버튼 적용이 안되는 문제

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        
        android:background="#ff0000"
        android:background="@color/black"
        android:background="@drawable/button1"
        
    />

위 코드와 같이 버튼에 대한 3가지의 백그라운드 설정을 각각 해줘도

모두 아래와 같이 모두 보라색 배경으로만 결과가 나온다.

 

4.1.1 부터(?) 앱의 테마를 Theme.MaterialComponents 이녀석을 기본 Default로 사용하면서 발생하는 문제.

 


첫번째 방법

 

아주 간단한 해결방법은 스튜디오 상의 앱 테마를 Theme.AppCompat.Light 등으로 바꿔주면 해결

 

프로젝트의 app - res - values - themes - themes.xml

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.MyApplication" parent="Theme.AppCompat.Light">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>

코드 3번째 줄의 parent="이곳을 수정해주면 된다"

 


두번째 방법

 

themes.xml 을 수정하지 않고 안드로이드 매니패스트(AndroidManifest.xml)에서 테마를 변경.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApplication">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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


    </application>

</manifest>

코드 11번째 줄의 android:theme="@style/이곳을 수정해주면 된다.">

 

기본값으로 설정 되어 있는 Theme.MyApplication은

첫번째 방법의 themes.xml 코드 3번째 줄을 보듯이 themes.xml 의 테마를 정의한 것

즉, 문제의 Theme.MaterialComponents 이녀석인셈

 


세번째 방법

 

첫번째 방법, 두번째 방법 모두 싫고 xml 코드상에서 해결하고 싶다면

두가지 방법이 존재

 

1. backgroundTint 사용

            android:background="#00ff00"
            android:backgroundTint="#00ff00"

코드의 1번줄이 안먹는다면

2번줄의 backgroundTint를 사용하면 바로 적용이 된다.

하지만 xml로 정의한 커스텀 버튼( android:backgroundTint="@drawable/custom_button" ) 사용이 불가능

따라서 다음 2번째 방법을 사용

 

2. androidx.appcompat.widget.AppCompatButton

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        
        android:background="#ff0000"
        android:background="@color/black"
        android:background="@drawable/button1"
        
    />

기존 Button 대신 androidx.appcompat.widget.AppCompatButton 이코드를 써주면 사용 가능

하지만 기존의 Button만 써주면 되던 코드가 말도안되게 길어지는 단점이 존재

 


 

찾다 찾다 하도 모르겠어서 되도않는 영어로 스택오버플로우에 질문해서 얻어낸 결과

 

https://stackoverflow.com/questions/65477334/android-button-background-color-not-changing-on-xml

 

Android Button background color not changing on xml

Android Stidio 4.1.1 SDK 29 (Android 10) I trying to change button background color on xml, but it doesn't changed. here's my code stackoverflow.com

 

블로그 이미지

jokey12

모든 글은 내가 보려고 작성함 Contact : jsung9912@gmail.com

,

기본 토스트 메시지

Toast.makeText(getApplicationContext(), "Toast Message!", Toast.LENGTH_SHORT).show();

변수로 설정

Toast myToast = Toast.makeText(getApplicationContext(), "Toast Message!", Toast.LENGTH_SHORT);
myToast.show();

LENGTH_SHORT / LENGTH_LONG 차이

LENGTH_SHORT 2초 동안 토스트 메시지 출력
LENGTH_LONG 3.5초 동안 토스트 메시지 출력

커스텀 토스트 메시지

 

1. 커스텀 토스트 메시지 사용을 위한 Layout 생성 

//toast_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <TextView
        android:id="@+id/toast"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Custom Toast Message"
        android:textColor="#ff0000"
        android:background="#000000" />
</LinearLayout>

 

2. LayoutInflater를 활용, 커스텀 토스트 메시지 지정 및 출력

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            
            
                View toastLayout = getLayoutInflater().inflate(R.layout.toast_layout, null);
                myToast.setView(toastLayout);
                myToast.show();
                
                
            }
        });

 

3. 출력

 

블로그 이미지

jokey12

모든 글은 내가 보려고 작성함 Contact : jsung9912@gmail.com

,

나볼려고 쓴글

 

Git bash 명렁, 터미널 명령어 외우는 것도 싫고 복잡해서

보기 편한 GUI 방식으로만 사용.


시스템 환경

윈도우 10 / Mac - Big Sur
안드로이드 스튜디오 - 4.1.1
Git- 2.29.2.3 - 64bit

1. Git 설치

 

https://git-scm.com/downloads

 

Git - Downloads

Downloads Mac OS X Windows Linux/Unix Older releases are available and the Git source repository is on GitHub. GUI Clients Git comes with built-in GUI tools (git-gui, gitk), but there are several third-party tools for users looking for a platform-specific

git-scm.com

윈도우 - 위 링크 다운 후에 설치 과정은 전부 default로 설치

Mac OS - 앱스토어에서 Xcode를 설치하면 자동으로 Git 도 함께 설치

           - 또는 터미널 상에서 Homebrew 설치 후 - Git 설치


2. 안드로이드 스튜디오 설정

 

File - Settings - Version Control - Git

└ Path to Git executable - Test 클릭을 통해서 Git 설치 유무와 버전 확인

 

File - Settings - Version Control - GitHub

깃허브에 로그인

 

404 / 401 에러 시에 깃허브 홈페이지에서 토큰 발급 후 토큰으로 로그인

토큰 발급

 깃허브 홈페이지 - 프로필 - Settings - Developer settings - Personal access token

    [ repo / admin:org / gist ] 체크 후 발급 (하위 속성까지 전부 체크)


3. 깃허브(GitHub)에 프로젝트 업로드

 

상단 VCS 탭

1. Create Git Repository

 - 좌측 프로젝트 영역 파일들이 빨갛게 바뀜

2. Share Project on GitHub

 - Repository에 올릴 내용을 담는 작업

 - Repository 이름 설정, private 설정 후

 - commit message 설정 - Add

3. 깃허브에 업로드가 되었다

 


4. 수정한 프로젝트 업데이트

 

프로젝트 수정 후 업데이트 시 방법

3가지 방법

 - 상단 Git - 체크 표시

 - control + k 단축키

 - 상단 메뉴 VCS - Git - Commit Files... 선택

 

 

좌측 Commit 메뉴에서

Show Diff (Ctrl + D)를 통해 변경내용을 아주 보기 좋게 확인 가능

 

이후 Commit Message 입력 후 

Commit 말고 Commit 우측 삼각 메뉴를 눌러 Commit and Push를 선택

 

이후 Push 해주면 깃허브 상에서 변경내용 확인 가능

 


5. 협업 과정

 

깃허브 Repository - Settings - Manage access - Invite a collaborator

협업자 초대를 통한 Collaborator가 프로젝트 Access 가능

 

협업자(Collaborator) 기준 프로젝트 받아오기

 

깃허브 초대 수락 후 Repository 내에서 Clone URL 복사

 

 

이후 마찬가지로 안드로이드 스튜디오에서 Git/GitHub 설정 후

 

첫 시작화면(Close Project로 프로젝트를 닫은 화면)에서

 

우측 Get from Version Control 메뉴 선택

 

복사했던 Repository Clon URL 입력

 

 

또는

 

GitHub 탭에서 repository 선택 가능

 


6. 협업자(Collaborator)가 코드 수정 후 → 업데이트 (Commit and Push)

 

협업자가 수정하던 프로젝트 Master가 수정하던 모두 마찬가지로

 

Commit and Push 로 깃허브에 업데이트

 


7. 수정내용(변경사항)을 협업 프로젝트에 최신으로 업데이트

 

상단 Git - Update Project 를 통해 빠르게 업데이트

 

또는

 

상단 메뉴 VCS - Git - Pull 을 통해 업데이트 가능

 


이상 내가 보려고 쓴 글

 

 

 

 

블로그 이미지

jokey12

모든 글은 내가 보려고 작성함 Contact : jsung9912@gmail.com

,