Le_Penseur

[안드로이드 스튜디오] Ch10. 버튼 이펙트 & 핵심 수신기(ButtonEffect & KeyListener) 본문

개인(Personal)/안드로이드 (Andorid Studio)

[안드로이드 스튜디오] Ch10. 버튼 이펙트 & 핵심 수신기(ButtonEffect & KeyListener)

LePenseur 2019. 8. 28. 11:10
반응형

2019년 8월 28일 (수)

 

창업의 길로 가기 위한 두번째 걸음, [안드로이드 스튜디오]로 향해보자!

 

*질적으로 많이 부족한 글입니다. 지적 및 조언 환영합니다.

*이 글에선 "안드로이드 스튜디오"를 줄여서 "안스" 라고 표현합니다. 참고 바랍니다.

 

우선, 본인은 학원에서 'JAVA' 기초 수업을 선수강했다. 안드로이드 스튜디오를 배우기 위해선 피할 수 없는 언어이기 때문이다.

(물론 Kotlin 도 있지만, 대한민국에선 아직 JAVA의 파워가 어마어마 하다고..)

 

이번글에서는 [버튼 클릭전] - [버튼 클릭후] 의 모습을 다르게 하는 방법에 대해 알아보고,

 

 

 


<차례>

1. MyButtonEffect

2. MyKeyListener


 

1. MyButtonEffect

 

1.1 결과

 

사진1

 

설명: 버튼 클릭시 그림 또는 컬러가 바뀌어야 한다.

클릭시 Button1은 놀라는 사람, Button2는 좀더 진한 종, Button3은 배경색이 분홍색, Button4는 안스 제공 기본 이펙트 를 보여준다.

이번 프로젝트를 위해선 총 4개의 xml파일, 1개의 java파일이 필요하다.

으악! 벌써 복잡한 스멜이 느껴진다. 하지만 걱정마시라, 각각의 파일보다 전체적인 프로젝트에 집중하면 크게 부담스럽지 않다.

 

"(어플제작에서 길을 잃지 않으려면) 나무보다는 숲을 보자." - Le Penseur (199* ~)

 

 

1.2 코드 
1.2.1 activity_main.xml

 

<?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"
    tools:context=".MainActivity">

    <!--

    버튼 클릭 효과 주기!
    - 이제는 onClickListener뿐만 아니라, xml파일을 이용하여 효과를 주는 방법을 알아본다.
    - 상태값에 따라 다른 효과를 주기 위해서는 selector를 이용하여 정의하여야 한다.
    - selector 만들때는 drawable파일에서 만든다.

    -->

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button1"
            android:textSize="40dp"
            android:textColor="#ffffff"
            android:background="@drawable/button_selector"
            />

        <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button2"
        android:textSize="40dp"
        android:gravity="center"
        android:background="@drawable/image_select"/>

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button3"
            android:textSize="40dp"
            android:gravity="center"
            android:background="@drawable/selector_view"/>

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button4"
            android:textSize="40dp"
            android:gravity="center"
            android:background="?attr/selectableItemBackground"
            />

        <!--

        따로 색깔,그림 을 지정해주거나 그런게 아닌이상 이 걸 쓰면 된다. 아주 효욜적이다.
         android:background="?attr/selectableItemBackground"
         : 특정 뷰 안에 제한되어 터치시 퍼져나가는 효과
         android:background="?attr/selectableItemBackgroundBorderless"
         : 제한 구역 없이 터치시 퍼져나가는 효과

         API21이상에서는
         클릭효과를 주고싶은 vIEW의 Background 밑에 속성을 지정하면 클릭효과가 생긴다.


        -->




    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

 

설명: 따로 id를 생성해줄 필요없다. 이 프로젝트에서는 기능을 추가할것이 아니기때문이다.

 

1.2.2 button_shape2.xml

 

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle"
    >
    <solid
        android:color="#fd4381"
        />

</shape>

 

설명: 3번째 버튼의 효과다. res.drawable 폴더에 새로운 파일을 만들어주면 되는데, 디폴트값이 shape이 아닌 selector일것이다. 그걸 바꿔주고 위 코드를 작성하면 된다.

android: shape 에서는 [사각형, 타원형, 원형, 선] 이렇게 총 4종류가 있다.

 

1.2.3 selector.xml

 

<?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/button_shape2"

        />
    <item
        android:state_focused="true"
        android:drawable="@drawable/button_shape2"

        />

    <item
        android:drawable="@drawable/button_shape1"

        />



</selector>

설명: 위 코드와 동일한 방법으로 새로운 파일을 만든것이다. 다만, 이번에는 디폴트값을 그대로 사용하고 그안에서 item을 추가할것이다. 그래서 selector는 뭐하는 놈인가? 한마디로 정리하자면, "상태에 따라 다른 이미지를 보여주는 리소스" 정도 되시겠다.

 

그래서 로직을 잘 보면, state pressed, focused 등 눌렀을때, 집중되었을때와 같은 오더가 내려지고 그 밑에 어떤 이미지가 나오게 할지도 지정하는것을 볼 수 있다. 오더의 종류는 총 5가지 이고, 이 안에서 true/false 를 결정하면 된다.

 

1. android:state_pressed :"true" 현재 클릭이 된 경우./ "false" 클릭이 안된 경우

2. android:state_enabled : "true" 현재 사용이 가능한 경우 / "false" 현재 사용이 불가능한 경우

3. android:state_selected :"true" 현재 선택이 된 경우/ "false" 현재 선택이 안된 경우

4. android:state_focused : "true" 현재 포커스를 가진 경우/ "false" 현재 포커스를 안가진 경우

5. android:state_checked : "true" 현재 체크된 경우/ "false" 현재 체크가 안된 경우

 

1.2.4 image_select.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/a_alarm"/>


    <!--버튼 클릭전 -->
    <item
        android:state_pressed="false"
        android:drawable="@drawable/b_alarm"
        />
</selector>

 

설명: 위와 동일하다.

 

1.2.2 MainActivity.java

 

package com.example.mybuttoneffect;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

 

설명: 이번엔 기능을 따로 추가하지않았다.

 

1. MyKeyListener

 

1.1 결과

 

사진2 - 어플 초기 화면

 

사진3 - 위 메뉴 클릭시

 

사진4 - EditText란에 숫자 입력시

 

설명: 이번 프로젝트의 목표는 오른쪽 위에 있는  세로의 [...] 메뉴 버튼을 제작 및 그안에 들어갈 리스트까지 지정하는 것이다.

그런데 메뉴라는 버튼을 독립적으로 만들어서 독립적으로 사용 할 순 없을까? 이에 대한 의문점을 해결할 것이다.

 

InputData 는 EditText란에 숫자또는 글자를 입력했을때 그에 합당하는 숫자를 보여주는데..

이 작업이 정확히 무슨의미가 있나 싶긴하다. (...)

 

결론은 어플 상단 오른쪽위에 메뉴버튼을 생성하고 그안에 들어갈 메뉴 리스트 작성까지가 이 어플의 최종목표다.

 

1.2 코드
1.2.1 activity_main.xml

 

<?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"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <EditText
            android:id="@+id/editText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="25dp"
            android:hint="input"/>

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="25dp"
            android:text="InputData : "
            />

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="menu"/>




    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

 

설명: 이번 디자인란에도 특별한것은 없다. 그저 현 프로젝트는 기능이 '주'이기에, id생성을 꼭 해주자.

 

1.2.2 main_menu.xml

 

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

    <!--
        menu파일의 정식 명칭 main_menu.xml (다른이름으도 상관없지만, 보통 "main_"으로 시작해야함)

    -->
<item android:id="@+id/add"
    android:title="@string/Add"/>

    <item android:id="@+id/edit"
        android:title="@string/Edit"/>

    <item android:id="@+id/email"
        android:title="@string/Email"/>
</menu>

 

설명: 여태까진 res폴더에 있는 drawable폴더를 주로 사용했는데, 이번에는 menu라는 폴더에서 작업을 한다.

new resource file을 만들어주자. 단, 이름은 main_### 으로 시작하도록 하자.

 

기존의 리스트뷰와 동일하게, 아이템을 추가시킨다. 다른점이라면 "android:title=@string/##" 라는 아이디 기반 로직을 이용한다는것이다.

그 이유는 기존의 리스트뷰는 아이템을 '선택'만 하는것이었다면, 메뉴의 리스트는 아이템에 '기능'을 추가해야하기 때문이라고 생각한다.

Add, Edit, Email이라는 메뉴 리스트를 총 3개 추가했다.

 

1.2.3 strings.xml

 

<resources>
    <string name="app_name">MyKeyListener</string>
    <string name="Add">Add</string>
    <string name="Edit">Edit</string>
    <string name="Email">Email</string>
</resources>

 

설명: 또다른 xml파일이 필요하다. 그런데 이 xml파일은 새롭게 생성한건 아니고, 기존에 존재하던 파일이다. 

 

1.2.2 MainActivity.java

 

package com.example.mykeylistener;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private EditText editText;
    private Button button;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //객체 설정
        editText = (EditText) findViewById(R.id.editText);
        textView = (TextView) findViewById(R.id.textView);
        button = (Button) findViewById(R.id.button);

        // OnKeyListener
        // - OnKey메소드를 재정의 하여야 한다. (왜?

        editText.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View view, int i, KeyEvent keyEvent) {

                // - int i: keyCode (키값, 아스키코드와 유니코드랑은 다르다.)
                // - KeyEvent: KeyEvent는 모션 이벤트와 사용방법이 거의 비슷하다.

                //if(keyEvent.getAction() == KeyEvent.ACTION_UP){
                //이 if문은 있어도되고 없어도 된다.
                //keyEvent.ACTION_DOWN
                //예전 코드들을 살펴 보면, DOWN으로 처리하였다.
                //OREO 버전에서는 다운으로 처리하면 제대로 되질 않는다.


                //키가 눌러졌을때 0~9까지의 숫자키 거르기.
                textView.setText("InputData : " + i);
                if (i >= KeyEvent.KEYCODE_0 && i <= KeyEvent.KEYCODE_9) {
                    Toast.makeText(
                            getApplicationContext(), "number" + i, Toast.LENGTH_LONG).show();


                }

                return false;
                // true를 반환하면... event 객체가 내부적으로 항상 이벤트를 발생하고있다고 생각하게 된다.
                // 우린 event를 필요시에만 호출해야하기 때문에, 필요한 경우에만 해당 이벤트를 처리한후에 false를 반환 함으로써
                // 항상 event를 발생하고 있다는 생각을 하지 않도록 해야 한다.
                // 걍 왠만하면 false로 두라.

            }
        });

    }
    // 메뉴 바를 활성화 시키기 위해서는 OnCreateOptionsMenu랑 onOptionsItemSelected 를 @override(재정의) 해야한다.


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.main_menu,menu);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {

        //하나의 메뉴 아이템이 선택되었을때, 현재 메소드 실행!

        switch (item.getItemId()){
            case R.id.add:
                textView.setText("Selected ADD");
                break;

            case R.id.edit:
                textView.setText("Selected EDIT");
                break;

            case R.id.email:
                textView.setText("Selected EMAIL");
                break;
        }

        return super.onOptionsItemSelected(item);
    }
}

 

반응형
Comments