Windows에 Git을 먼저 설치하고 버전관리를 시작하려는 안드로이드 스튜디오 프로젝트에서 VCS-Enable Version Control Integration..을 누른다.

Git을 선택하고 확인

app을 우클릭 후 Git-Add 선택

Bitbucket에서 저장소를 만든다.

 

Readme를 포함하면 나중에 push할 때 오류가 나더라. 원인이 뭔지 몰라 해결하지 못했지만 강제로 push하려면 터미널에 아래 코드를 입력하면 된다.

$ git push -u origin +main

만들어진 저장소 주소(HTTPS)를 복사한다.

Git-Manage Remotes

 

아까 복사한 주소를 넣는다.

확인을 누른 후 커밋

안드로이드 스튜디오는 기본 브랜치 이름이 master로 되어 있다. 브랜치 명을 main으로 바꾸고 싶으면 push하기 전에 Git-Branches에 들어가 로컬 저장소 브랜치 명을 바꾼다.

이제 push해 보자.

Bitbucket에 push 완료!

FloatingActionButton의 색상을 변경하고 싶다!

 

app:backgroundTint : 버튼 색상

app:tint : 버튼 안의 아이콘 색상

app:rippleColor : 버튼을 눌렀을 때 퍼지는 이펙트 색상

 

<com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id="@+id/floating_action_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end"
    android:layout_margin="16dp"
    android:contentDescription="@string/fab_content_desc"
    android:src="@drawable/ic_baseline_add_24"
    app:rippleColor="@color/purple_200"
    app:tint="@color/white"
    app:backgroundTint="@color/purple_500" />

 

layout 안의 뷰 여러 개를 percent에 따라 화면의 각 비율만큼 차지하게 하고 싶을 때 부모 layout에 따라 값을 지정하는 방법이 다르다.

Linearlayout과 Constraintlayout의 두 가지 경우를 살펴보자.

두 경우 모두 weight을 지정하려는 자식 view의 width나 height 값을 0dp로 설정해야 한다. 아래 코드에서는 layout_height 값을 0dp로 하고 비율을 9 : 1로 설정했다.

 

1. 부모가 LinearLayout일 때는 부모에 weightSum을 설정하고 자식에 layout_weight 값을 지정한다.

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="10"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/frame_layout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="9" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_gravity="bottom"
        android:layout_weight="1"
        app:labelVisibilityMode="unlabeled"
        app:menu="@menu/bottom_navigation_menu" />
</LinearLayout>

 

2. 부모가 Constraintlayout일 때는 자식의 layout_constraintHeight_percent값을 지정한다. width일 때도 마찬가지. percent 값은 소수점으로 입력한다. (10% = 0.1로 입력)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/frame_layout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHeight_percent="0.9"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_gravity="bottom"
        app:labelVisibilityMode="unlabeled"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHeight_percent="0.1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/frame_layout"
        app:menu="@menu/bottom_navigation_menu" />

</androidx.constraintlayout.widget.ConstraintLayout>

firebase로 구글 로그인 기능을 구현하는데 아래 코드에서 R.string.default_web_client_id 부분에서 오류가 났다.

분명 빨갛게 표시가 되어 있는데 앱을 실행하는 데에는 전혀 문제가 없었다.

그래도 거슬리니까 고쳐보자.

// Configure Google Sign In
val gso = GoogleSignInOptions
    .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
    .requestIdToken(getString(R.string.default_web_client_id))
    .requestEmail()
    .build()
googleSignInClient = GoogleSignIn.getClient(this, gso)

 

build.gradle(project) 파일의 dependencies에 classpath 'com.google.gms:google-services:4.3.10' 부분을 수정하면 된다.

buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:7.0.3"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31"
        classpath 'com.google.gms:google-services:4.2.0' // 4.3.10에서 4.3.8로 수정
    }
}

The minCompileSdk (31) specified in a
     dependency's AAR metadata (META-INF/com/android/build/gradle/aar-metadata.properties)
     is greater than this module's compileSdkVersion (android-30).
     Dependency: androidx.core:core:1.7.0.

 

새 프로젝트를 만들어 빌드를 하는데 위와 같은 오류가 났다. 며칠 전까지 작성하던 프로젝트와 비교해보니 build.gradle(:app)의 implementation 'androidx.core:core-ktx:1.7.0' 부분이 1.6.0에서 1.7.0으로 바뀌어 있었다.

 

해결 방법은 compileSdk를 31로 수정하는 것.

android {
    compileSdk 31 //30에서 31로 수정

    defaultConfig {
        applicationId "com.electroskunk.myproject"
        minSdk 26
        targetSdk 30
        versionCode 1
        versionName "1.0"
        ...
    }
    ....
}

 

 

compileSdk 와 minSdk, targetSdk에 관한 설명

 

Picking your compileSdkVersion, minSdkVersion, targetSdkVersion

Choose wisely and follow minSdkVersion <= targetSdkVersion == compileSdkVersion

medium.com

 

 

[Android] compileSdkVersion, targetSdkVersion 등 SDK 버전 설정 이해하기

compileSdkVersion, targetSdkVersion 등 SDK 버전 설정 알아보기 개념은 어설프게 알아와서 이번 포스팅으로 정확하게 이해하기 위해 정리했습니다. 안드로이드는 상위호환성을 중요시하기때문에 compileSd

duzi077.tistory.com

 

버튼을 <Button>으로 했더니 foreground 수정은 반영이 되는데 background에 이미지를 넣던 색을 넣던 수정이 안되는 현상이 있었다. 간단하게 태그를 <androidx.appcompat.widget.AppCompatButton>로 수정해서 해결함.

 

    //<Button />
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btn_signin_google"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_margin="16dp"
        android:background="@drawable/btn_google_signin_light_normal_hdpi"
        android:text="@string/signin_google"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_appTitle" />

MySQL 루트 패스워드를 잘못 변경했다.

그래서 패스워드를 리셋하려고 구글링을 해서 이런 답변을 얻었다.

 

Reset MySQL 8.0 root Password in Windows

Reset MySQL 8.0 root Password in Windows. GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

일단

 

1. mysql 서비스를 모두 종료한다.

2. 명령 프롬프트를 관리자 권한으로 실행하고

3. C:\Program Files\MySQL\MySQL Server 8.0\bin 경로로 들어가서

4. mysqld --console --skip-grant-tables --shared-memory

 

를 하면 된다는데 안된다.

ERROR

No such file or directory란다.

 


*** 실선 안에 있는 건 따라하지 마세요. 삽질의 기록입니다. ***

 

다시 구글링해서 mysqld --initialize 라는 명령어를 얻었다.

했더니 C:\Program Files\MySQL\MySQL Server 8.0에 data라는 폴더가 새로 생겼다.

이후 위에서 시도한 mysqld --console --skip-grant-tables --shared-memory를 다시 입력한다.

그리고 새로 명령 프롬프트를 띄워서 패스워드 없이 mysql에 접속을 시도한다.

  1. mysql -u root
  2. select authentication_string,host from mysql.user where user='root'; // 패스워드가 있는지 확인
  3. UPDATE mysql.user SET authentication_string='' WHERE user='root'; // 패스워드를 삭제함

여기까지 했으면 터미널 두 개를 다 닫고 다시 작업관리자-서비스에서 MYSQL80을 실행한다. 

 

 

그러면 이제 될 것 같지? 노노 천만의 말씀 만만의 콩떡(천부당만부당)이다!

 

다른 데이터의 패스워드를 바꾼 거라서 원래 있던 데이터는 여전히 로그인이 안 되고 먹통이다.


결국 

 

1. 아까 생긴 C:\Program Files\MySQL\MySQL Server 8.0\data 폴더를 삭제하고

2. 작업관리자-서비스에서 MYSQL80을 다시 중지한다.

3. 명령 프롬프트를 관리자 권한으로 다시 켜서

4. mysqld --console --skip-grant-tables --shared-memory 뒤에 --defaults-file="C:\ProgramData\MySQL\MySQL Server 8.0\my.ini" 를 붙여서 입력.

5. 그리고 새로 명령 프롬프트를 띄워서 패스워드 없이 mysql에 접속을 시도한다.

    5-1. mysql -u root

    5-2. select authentication_string,host from mysql.user where user='root'; // 패스워드가 있는지 확인

    5-3. UPDATE mysql.user SET authentication_string='' WHERE user='root'; // 패스워드를 삭제함

 

여기까지 했으면

 

6. 터미널 두 개를 다 닫고

7. 다시 작업관리자-서비스에서 MYSQL80을 실행한다. 

 

그리고 대망의 마지막

alter user 'root'@'localhost' identified with mysql_native_password by 'password';

password 부분을 원하는 새 비밀번호로 입력하고 엔터를 땅 하면!

 

 

 

 

할렐루야!

완성 이미지

전체화면에서의 기본 정렬 모습
창의 크기가 좁을 때 자동 정렬

HTML 코드

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link href="https://fonts.googleapis.com/css2?family=Black+Han+Sans&family=Nanum+Gothic&display=swap" rel="stylesheet">

    <link rel="stylesheet/less" type="text/css" href="grid-flex.less" />
    <script src="//cdn.jsdelivr.net/npm/less" ></script>
    <!--
    <link rel="stylesheet" href="grid-flex.css" />
    -->
    <script src="prefixfree.min.js"></script>
    <title>Electro Skunk</title>
</head>
<body>
    <div class="container">
        <header>
            <h1>Electro Skunk</h1>
        </header>

        <div class="row">
            <section class="grid_12">
                <div class="box">12</div>
            </section>
        </div>

        <div class="row">
            <section class="grid_11">
                <div class="box">11</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
        </div>

        <div class="row">
            <section class="grid_10">
                <div class="box">10</div>
            </section>
            <section class="grid_2">
                <div class="box">2</div>
            </section>
        </div>

        <div class="row">
            <section class="grid_9">
                <div class="box">9</div>
            </section>
            <section class="grid_3">
                <div class="box">3</div>
            </section>
        </div>

        <div class="row">
            <section class="grid_8">
                <div class="box">8</div>
            </section>
            <section class="grid_4">
                <div class="box">4</div>
            </section>
        </div>

        <div class="row">
            <section class="grid_7">
                <div class="box">7</div>
            </section>
            <section class="grid_5">
                <div class="box">5</div>
            </section>
        </div>

        <div class="row">
            <section class="grid_6">
                <div class="box">6</div>
            </section>
            <section class="grid_6">
                <div class="box">6</div>
            </section>
        </div>

        <div class="row">
            <section class="grid_4">
                <div class="box">4</div>
            </section>
            <section class="grid_4">
                <div class="box">4</div>
            </section>
            <section class="grid_4">
                <div class="box">4</div>
            </section>
        </div>

        <div class="row">
            <section class="grid_3">
                <div class="box">3</div>
            </section>
            <section class="grid_3">
                <div class="box">3</div>
            </section>
            <section class="grid_3">
                <div class="box">3</div>
            </section>
            <section class="grid_3">
                <div class="box">3</div>
            </section>
        </div>

        <div class="row">
            <section class="grid_2">
                <div class="box">2</div>
            </section>
            <section class="grid_2">
                <div class="box">2</div>
            </section>
            <section class="grid_2">
                <div class="box">2</div>
            </section>
            <section class="grid_2">
                <div class="box">2</div>
            </section>
        </div>

        <div class="row">
            <section class="grid_2">
                <div class="box">2</div>
            </section>
            <section class="grid_2">
                <div class="box">2</div>
            </section>
            <section class="grid_2">
                <div class="box">2</div>
            </section>
            <section class="grid_2">
                <div class="box">2</div>
            </section>
            <section class="grid_2">
                <div class="box">2</div>
            </section>
            <section class="grid_2">
                <div class="box">2</div>
            </section>
        </div>

        <div class="row">
            <section class="grid_1">
                <div class="box">1</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
            <section class="grid_1">
                <div class="box">1</div>
            </section>
        </div>

        <footer>
            <address>and Hope</address>
        </footer>
    </div>
</body>
</html>

LESS 코드

//reset
* { margin: 0; padding: 0; }
body { font-family: 'Nanum Gothic', sans-serif; }
li { list-style: none; }
a { text-decoration: none; }
img { border: 0; }


//style
header, footer {
    width: 100%;
    line-height: 100px;
    background: green;
    font-family: 'Black Han Sans', sans-serif;
    text-align: center;
}

.box {
    background: pink;
    text-align: center;
}

//responsive size
@MaxWidth: 1200px;
@ColumnWidth: 80px;
@GutterWidth: 20px;

.container {
    max-width: @MaxWidth;
    min-height: 100vh;
    margin: 0 auto;
    display: flex;
    flex-flow: column nowrap;
    justify-content: space-around;
    align-content: space-between;
}

.row {
    display: flex;
    flex-flow: row wrap;
    justify-content: space-around;
    align-content: space-between;
}

.grid(@column) {
    width: (@ColumnWidth * @column) + (@GutterWidth * (@column - 1));
    margin: ((@GutterWidth / 4) - 1);
    padding: (@GutterWidth / 4);
    border: 1px solid black;
}

.grid_1 { .grid(1); }
.grid_2 { .grid(2); }
.grid_3 { .grid(3); }
.grid_4 { .grid(4); }
.grid_5 { .grid(5); }
.grid_6 { .grid(6); }
.grid_7 { .grid(7); }
.grid_8 { .grid(8); }
.grid_9 { .grid(9); }
.grid_10 { .grid(10); }
.grid_11 { .grid(11); }
.grid_12 { .grid(12); }

인터넷에서 많이 쓴다던 CSS Reset code(CSS 초기화 코드)를 복붙 할 때, 리스트 스타일을 없애는 코드를 빼도 리스트 스타일이 사라진다.

 

왜 그럴까?

 

일단 리스트 스타일이 제대로 적용된 화면을 보자.

 

 

그리고 이건 li { list-style: none; }을 써서 리스트 스타일을 없앤 모습이다. ol, ul { list-style: none; }을 써도 된다.

 

 

그리고 많이들 쓰는 Eric Meyer's Reset CSS에서 ol, ul { list-style: none; }를 지우고 붙여넣기를 하면 이렇게 나온다.

 

 

결과를 놓고 비교하니 답이 나온다.

 

'공백'이 문제다.

 

<ol>과 <ul>은 기본적으로 리스트의 위아래로 16px의 margin을 갖고, 왼쪽으로는 40px의 패딩을 갖는다. 그리고 이 padding값 안에 리스트 스타일이 포함된다.

그런데 CSS 초기화 코드를 보면 보통 전체 태그의 margin과 padding을 0으로 설정한다.

* { margin: 0; padding: 0;} 을 쓰는 경우, 또는 Eric Meyer's Reset CSS에서처럼 ol, ul { margin: 0; padding: 0; }을 적용하면 <ol>과 <ul> 이 갖는 숫자와 점 같은 리스트 스타일뿐만 아니라 여백까지 사라진다. margin과 padding을 없애니 리스트 스타일이 함께 사라지는 것이다.

 

크롬의 검사 기능으로 쉽게 확인할 수 있다.

 

참고로 리스트 스타일은 padding에 포함되므로 margin만 0으로 초기화하면 리스트 스타일은 적용되고 위아래 여백만 사라진다.

 

 

반대로 margin은 그대로 두고 padding만 초기화하면 <ol> 리스트 앞에는 .dummy 와 같이 점이 붙어있는 이상한 모양이 된다.

 

 

별 쓸데 없는 내용으로 한 시간 넘게 버렸다. 하지만 원인을 알고 나니 뿌듯하다. 이런 게 개발의 재미인가?

+ Recent posts