오늘은 Android 개발 혹은 공부하시는 분들이라면 모두들 기본적으로 하시는 아이콘 변경에 대해서 메모해보도록 하겠습니다.
어떻게보면 대표 아이콘은 스토어에서 직관적으로 보여져서 App을 대표하는 마스코트 느낌이기 때문에 많이들 신경쓰실 것 같습니다.
하지만 저는 메모장이기 때문에....... 허접하게 IT김군 이름이 들어간 아이콘을 하나 준비하였습니다.
우선 아이콘 적용을 위해 적용할 아이콘 이미지를 준비합니다.
스토어에 적용되어 보여질 사이즈는 512x512 입니다.
저도 메모를 위해 512x512 사이즈의 png 파일을 하나 준비했습니다.
이미지가 준비되었다면 다음은 준비된 이미지를 Android Studio에서 프로젝트에 적용해보겠습니다.
방법은 굉장히 간단하기 때문에 아래 사진들을 보시면서 차근차근 하시면 쉽게 하실 수 있습니다.
res에서 마우스 우클릭을 하신 후 New -> Image Asset를 클릭합니다.
Image Asset를 클릭하시면 아래와 같은 창이 생성됩니다.
저는 icon 이름을 ic_main으로 변경했습니다.
정확하게 정해진 것이 없기때문에 구분하기 편하신 이름으로 지정하시면 됩니다.
아래 Resize의 경우 크기가 조금 안맞는 것 같으면 Resize를 통하여 조절할 수 있습니다.
여기까지 완료되었다면 Next를 눌러줍니다.
아이콘 사이즈가 512x512로 잘 지정되었죠?
Finish를 눌러서 적용해줍니다.
여기까지 완료하셨다면 res\mipmap 여러 폴더들에 아래와 같이 제가 적용한 아이콘이 들어가 있을 것입니다.
제가 적용한 ic_main이 매우 잘 들어있습니다.
그럼 이제 이미지는 넣었고, 이 이미지를 적용해봐야겠죠?
그럼 이제 AndroidManifest.xml 파일로 가보도록 하겠습니다.
AndroidManifest.xml에서는 아래 두 부분만 수정해주시면 됩니다.
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
▼ 아래 처럼 변경 ▼
android:icon="@mipmap/ic_main"
android:roundIcon="@mipmap/ic_main_round"
위와 같이 변경해주시면 됩니다.
못찾으시는 분들을 위해 AndroidManifest.xml 풀 소스를 보여드리겠습니다.
#### AndroidManifest.xml ####
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.itkim.exam.permissionsexam">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_main" ---------------------------------- 이 부분 수정
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_main_round" ----------------------- 이 부분 수정
android:supportsRtl="true"
android:theme="@style/AppTheme">
<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>
이렇게 두 부분을 수정해주신 후 빌드하시고 적용해보시면 아이콘이 정상적으로 바뀌어 있는 것을 확인하실 수 있습니다.
혹시나 저의 메모에 잘못된 부분이 있거나 이해가 잘 안되시는 부분이 있으시다면 댓글 부탁드리겠습니다.
오늘은 Android를 개발하신다면 모두 사용하실만한 Multi Permissions에 대해 메모해보도록 하겠습니다.
각각 개별로 Permission 요청하는 방법에 대해서는 설명이 잘 나와있는 곳이 많아서 여러 개를 사용하는 것만 메모하겠습니다.
우선 오늘도 파일을 나누어서 시작하겠습니다.
오늘은 패키지 안에 support라는 폴더를 생성했고
그 안에 PermissionSupport.java 파일을 만들었습니다.
추후 관리의 편의성을 위해 제 자신만의 방법으로 폴더를 구분하고 파일을 나눈 것이라서
이 방법이 불편하신 분들은 그냥 Activity에 코딩하셔도 상관 없습니다!
그럼 PermissionSupport.java의 소스를 먼저 보도록 하겠습니다.
// #### PermissionSupport.java ####
package com.itkim.exam.permissionsexam.support;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.List;
public class PermissionSupport {
private Context context;
private Activity activity;
// 요청할 권한을 배열로 저장해주었습니다.
private String[] permissions = {
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_CONTACTS,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private List permissionList;
// 이 부분은 권한 요청을 할 때 발생하는 창에 대한 결과값을 받기 위해 지정해주는 int 형입니다.
// 본인에 맞게 숫자를 지정하시면 될 것 같습니다.
private final int MULTIPLE_PERMISSIONS = 1023;
// 생성자에서 Activity와 Context를 파라미터로 받았습니다.
public PermissionSupport(Activity _activity, Context _context){
this.activity = _activity;
this.context = _context;
}
// 허용 받아야할 권한이 남았는지 체크
public boolean checkPermission(){
int result;
permissionList = new ArrayList<>();
// 위에서 배열로 선언한 권한 중 허용되지 않은 권한이 있는지 체크
for(String pm : permissions){
result = ContextCompat.checkSelfPermission(context, pm);
if(result != PackageManager.PERMISSION_GRANTED){
permissionList.add(pm);
}
}
if(!permissionList.isEmpty()){
return false;
}
return true;
}
// 권한 허용 요청
public void requestPermission(){
ActivityCompat.requestPermissions(activity, permissionList.toArray(new String[permissionList.size()]), MULTIPLE_PERMISSIONS);
}
// 권한 요청에 대한 결과 처리
public boolean permissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
// 우선 requestCode가 아까 위에 final로 선언하였던 숫자와 맞는지, 결과값의 길이가 0보다는 큰지 먼저 체크했습니다.
if(requestCode == MULTIPLE_PERMISSIONS && (grantResults.length > 0)){
for(int i=0; i < grantResults.length ; i++){
//grantResults 가 0이면 사용자가 허용한 것이고 / -1이면 거부한 것입니다.
// -1이 있는지 체크하여 하나라도 -1이 나온다면 false를 리턴해주었습니다.
if(grantResults[i] == -1){
return false;
}
}
}
return true;
}
}
해당 Class의 큰 흐름을 보면 이렇습니다.
1. 우선 요청할 권한들을 String 배열을 선언하여 넣어주었습니다.
2. 요청에 대한 결과값을 확인하기 위해 RequestCode를 Final로 정의해주었습니다. (MULTIPLE_PERMISSIONS)
3. checkPermission() 함수를 통해 위에서 배열로 선언한 권한 중 허용되지 않은 권한이 있는지 체크하였습니다.
4. requestPermission() 함수에서는 위에서 배열로 선언한 권한에 대해 사용자에게 허용 요청을 하였습니다.
5. permissionResult() 함수에서는 요청한 권한에 대한 결과값 판단하도록 하였습니다.
주석도 제 나름대로 상세하게 달아놓아서 이해가 잘 안되시는 분들은 주석을 참고하셔도 될 것 같습니다.
그럼 이제 이 클래스를 Activity에서 활용하는 방법에 대해서 보도록 하겠습니다.
// #### MainActivity.java ####
package com.itkim.exam.permissionsexam;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Build;
import android.os.Bundle;
import com.itkim.exam.permissionsexam.support.PermissionSupport;
public class MainActivity extends AppCompatActivity {
// 방금 전 만들었던 클래스를 선언해줍니다.
private PermissionSupport permission;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
permissionCheck();
}
// 권한 체크
private void permissionCheck(){
// SDK 23버전 이하 버전에서는 Permission이 필요하지 않습니다.
if(Build.VERSION.SDK_INT >= 23){
// 방금 전 만들었던 클래스 객체 생성
permission = new PermissionSupport(this, this);
// 권한 체크한 후에 리턴이 false로 들어온다면
if (!permission.checkPermission()){
// 권한 요청을 해줍니다.
permission.requestPermission();
}
}
}
// Request Permission에 대한 결과 값을 받아올 수 있습니다.
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
// 여기서도 리턴이 false로 들어온다면 (사용자가 권한 허용을 거부하였다면)
if(!permission.permissionResult(requestCode, permissions, grantResults)){
// 저의 경우는 여기서 다시 Permission 요청을 걸었습니다.
permission.requestPermission();
}
}
}
MainActivity에서 활용법을 보시면 굉장히 간단합니다.
우선 위에서 만들었던 Class를 활용하기 위해 객체를 생성해준 후 check / request를 통해 권한을 요청했습니다.
그리고 아래 보시면 onRequestPermissionResult 가 있는데 이 부분은
Ctrl + O 단축키를 통해 오버라이드 하실 수 있습니다.
요청한 권한에 대한 결과 값을 얻어올 수 있는 함수입니다.
위 MainActivity까지 완료하셨다면 여러 개의 권한을 한 번에 요청할 수 있는 Multi Permissions 완성입니다!
혹시나 해당 메모에서 잘못된 부분이나 이해하시기 어려운 부분이 있으시다면 댓글로 달아주세요.
그래서 2개의 Java Class를 생성 후 Activity에서 사용하는 방법으로 진행해보도록 하겠습니다.
우선 제가 만들 테이블은 간단하게
(이름, 성, 핸드폰 번호)를 칼럼으로 갖는 테이블을 만들고 사용해보도록 하겠습니다.
저는 아래 사진과 같이 패키지 내부에 SQLite라는 폴더를 생성한 후
SQLiteControl / SQLiteHelper 두 개의 Java 파일을 생성하였습니다.
위와 같이 생성하셨다면 SQLiteHelper.java 파일을 먼저 코딩해보도록 하겠습니다.
우선 SQLiteHelper에 아래 사진과 같이 android.database.sqlite.SQLiteOpenHelper를 상속해줍니다.
그럼 위와 같이 빨간 밑줄이 쫘~~악! 그어지는데요.
몇 가지 Function들을 오버라이드 해주어야 합니다.
Ctrl + O 키를 누르면 Override 할 수 있는 함수들이 쭉~ 나올 것입니다.
여기서 아래 사진과 같이 생성자, onCreate, onUpgrade를 포함해줍니다.
해당 함수들을 모두 포함하고 OK를 누르면 자동으로 입력되며,
아래와 같이 빨간 줄이 사라집니다!
그럼 이제 소스를 살펴보겠습니다.
먼저 SQLiteHelper.java를 모두 코딩한 소스입니다.
SQLiteHelper에서는 DB를 생성, Open 및 버전 Update에 대한 내용을 편리하게 도와주는 Class 입니다.
#### SQLiteHelper.java ####
package com.itkim.exam.sqliteexam.SQLite;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import androidx.annotation.Nullable;
public class SQLiteHelper extends android.database.sqlite.SQLiteOpenHelper{
// 저는 나중에 수정할 때를 대비하여 final 선언을 하였지만 굳이 이렇게 안하셔도 괜찮습니다.
public final String TABLE_NAME = "itkimtable";
public final String FIRST_NAME = "f_name";
public final String LAST_NAME = "l_name";
public final String PHONENUM = "phoneNum";
public SQLiteHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
// Table 생성하는 Query
// ( if not exists ) 만약 존재하지 않으면 생성
String create_query = "create table if not exists " + TABLE_NAME + "("
+ FIRST_NAME + " text not null , "
+ LAST_NAME + " text , " // not null을 쓰시지 않으실 때는 이렇게 선언합니다.
+ PHONENUM + " text primary key);"; // primary key는 이렇게 선언합니다.
// 위 Create Query로 Table을 생성해줍니다.
sqLiteDatabase.execSQL(create_query);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
// SQLite에 대해 설정한 버전을 올렸을 때
// 기존 테이블 Drop 해준 후
String drop_query = "drop table " + TABLE_NAME + ";";
sqLiteDatabase.execSQL(drop_query);
// onCreate를 호출해서 Table 다시 생성
onCreate(sqLiteDatabase);
}
}
그럼 다음으로 SQLiteHelper.java 소스를 살펴보도록 하겠습니다.
#### SQLiteControl.java ####
package com.itkim.exam.sqliteexam.SQLite;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
public class SQLiteControl {
SQLiteHelper helper;
SQLiteDatabase sqlite;
// 생성자
public SQLiteControl(SQLiteHelper _helper){
this.helper = _helper;
}
// DB Insert
public void insert(String _firstName, String _lastName, String _phoneNum){
sqlite = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(helper.FIRST_NAME, _firstName);
values.put(helper.LAST_NAME, _lastName);
values.put(helper.PHONENUM, _phoneNum);
sqlite.insert(helper.TABLE_NAME, null, values);
}
// DB Select
public String[] select(){
sqlite = helper.getReadableDatabase();
// 커서 사용
Cursor c = sqlite.query(helper.TABLE_NAME, null, null, null,null,null,null);
// 칼럼 정보를 배열에 넣고
String[] columnName = {helper.FIRST_NAME, helper.LAST_NAME, helper.PHONENUM};
// 칼럼 정보와 길이가 같은 배열을 생성 후
String[] returnValue = new String[columnName.length];
// 생성한 배열에 데이터를 받아줍니다.
while(c.moveToNext()){
for(int i=0 ; i<returnValue.length; i++){
returnValue[i] = c.getString(c.getColumnIndex(columnName[i]));
Log.e("DB Select : ",i + " - "+returnValue[i]);
}
}
// 커서를 사용 후에 꼭 닫아줍시다!
c.close();
return returnValue;
}
// DB Update
public void update(String _key, String _value, String _phoneNum){
sqlite = helper.getWritableDatabase();
ContentValues value = new ContentValues();
value.put(_key, _value);
// 제가 phoneNum를 사용한 이유는 포스팅하는 예제 Table의 Primary Key가 phoneNum 이기 때문입니다.
sqlite.update(helper.TABLE_NAME, value, "phoneNum=?", new String[]{_phoneNum});
}
// DB Delete
public void delete(String _phoneNum){
sqlite = helper.getWritableDatabase();
// 제가 phoneNum를 사용한 이유는 포스팅하는 예제 Table의 Primary Key가 phoneNum 이기 때문입니다.
sqlite.delete(helper.TABLE_NAME, "phoneNum=?", new String[]{_phoneNum});
}
// SQLite Close
public void db_close(){
sqlite.close();
helper.close();
}
}
위와 같습니다.
주석을 상세하게 달아놓는다고 노력하긴 했는데 부족한지 모르겠습니다.
그럼 이제 위 두 Class를 활용하여 DB를 사용해보도록 하겠습니다.
DB를 실질적으로 사용하는 MainActivity의 소스입니다.
#### MainActivity.java ####
package com.itkim.exam.sqliteexam;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.itkim.exam.sqliteexam.SQLite.SQLiteControl;
import com.itkim.exam.sqliteexam.SQLite.SQLiteHelper;
public class MainActivity extends AppCompatActivity {
SQLiteHelper helper; // 헬퍼 선언
SQLiteControl sqlite; // 실제로 SQLite를 활용할 Class를 선언합니다.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// DB를 생성 및 Open합니다.
helper = new SQLiteHelper(
MainActivity.this, // context
"dbFileName.db", // DB 파일 이름을 적어주시면 됩니다.
null, // Factory
1 // 현재 생성하는 DB의 버전을 설정합니다.
);
/*
제가 SQLiteControl에 helper를 파라미터로 넣어준 이유는 파일을 나누어서 관리하기 위함입니다.
그냥 Activity에서 모든 작업을 하고 싶으시면 이렇게 나누시지 않아도 괜찮습니다.
*/
sqlite = new SQLiteControl(helper);
dbTest();
}
private void dbTest(){
// DB Insert를 하고 Select로 확인합니다.
dbInsert();
dbSelect();
// DB Update를 하고 Select로 확인합니다.
dbUpdate();
dbSelect();
// DB Delete를 하고 닫아줍니다. (DB를 사용 후에는 꼭 닫는 습관을 들이시는게 좋습니다!)
dbDelete();
sqlite.db_close();
}
private void dbInsert(){
// Insert 하려는 정보를 파라미터로 넘겨주기만 하면 Insert 됩니다.
sqlite.insert("군", "김", "010-0000-0000");
}
private void dbSelect(){
// 아까 SQLiteControl에서 배열로 넘겨주었기 때문에 sqlite.select()를 배열로 받아줍니다.
String[] selectData = sqlite.select();
// 배열로 받은 정보를 로그로 확인합니다.
for(int i=0; i>selectData.length; i++){
Log.i("@@ Select DB : ", selectData[i]);
}
}
private void dbUpdate(){
// update 해야 할 column명, 변경할 값, where조건 이지만 간단 예제이기때문에 primary Key를 사용했습니다.
sqlite.update(helper.FIRST_NAME, "김군", "010-0000-0000");
sqlite.update(helper.LAST_NAME, "IT", "010-0000-0000");
}
private void dbDelete(){
// delete 해야 할 정보 primary Key를 파라미터로 전달
sqlite.delete("010-0000-0000");
}
}
위와 같이 진행했을 시 결과 값은 아래와 같습니다.
처음에 군, 김, 010-0000-0000 이라는 정보를 Insert 후 Select한 정보가 위 3줄입니다.
그리고 "군 -> 김군" 으로 "김 -> IT"로 Update 후 Select한 정보가 아래 3줄입니다.
혹시나 보시고 어려운 부분이나 보시기 불편한 부분 있으시면 꼭 댓글로 저에게 가르침을 부탁드립니다.
#### CustomActionBar.java ####
package com.itkim.exam.customactionbar.customView;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import com.itkim.exam.customactionbar.R;
public class Custom_ActionBar extends AppCompatActivity {
private Activity activity;
private ActionBar actionBar;
public Custom_ActionBar(Activity _activity, ActionBar _actionBar){
this.activity = _activity;
this.actionBar = _actionBar;
}
public void setActionBar(){
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
View mCustomView = LayoutInflater.from(activity).inflate(R.layout.custom_actionbar, null);
actionBar.setCustomView(mCustomView);
// Custom ActionBar 생성하면 양쪽에 공백이 생기는데 이 공백을 채우기 위해 아래 4줄 적용
Toolbar parent = (Toolbar)mCustomView.getParent();
parent.setContentInsetsAbsolute(0,0);
ActionBar.LayoutParams params = new ActionBar.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.MATCH_PARENT);
actionBar.setCustomView(mCustomView, params);
}
}
위 주석에 있는 것처럼 아래 4줄만 추가해주시면 정상적으로 작동합니다.
// Custom ActionBar 생성하면 양쪽에 공백이 생기는데 이 공백을 채우기 위해 아래 4줄 적용
Toolbar parent = (Toolbar)mCustomView.getParent();
parent.setContentInsetsAbsolute(0,0);
ActionBar.LayoutParams params = new ActionBar.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.MATCH_PARENT);
actionBar.setCustomView(mCustomView, params);
여기까지 끝!
도움이 되셨다면 공감버튼 한 번만 부탁드리겠습니다.
저도 Android / Java에 대한 지식이 많이 부족하기에 잘못된 부분이 있다면 댓글로 꼭 알려주세요.