본문 바로가기
컴퓨터 공학/Android

[Do it 개정6판_안드로이드 스튜디오]둘째마당_07 선택 위젯 만들기

by hahehohoo 2019. 12. 7.
반응형

07-1 나인패치 이미지 알아보기

- 그래픽 프로그램에서 만든 버튼 이미지를 표시하면 단말에 따라 왜곡현상이 일어날 수 있기 때문에 해결 방법을 찾아야했다.

- 나인패치(Nine Patch) 이미지 왜곡 해결 방안이다. 

- 수정한 이미지의 파일 이름은 ooo.9.png 처럼 파일 확장자 앞에 9을 붙이면 안드로에서는 그 파일을 원본 이미지의 가로, 세로 끝부분의 픽셀을 모두 나인패치 이미지의 정보를 담은 것으로 인식한다. 

 

> SampleNinePatch 프로젝트

 

▼ activity_main.xml

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

    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Small"
    android:textColor="#ffffffff"
    android:background="@drawable/button_image_01"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MediumMediumMedium"
        android:textColor="#ffffffff"
        android:background="@drawable/button_image_01"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LongLongLongLongLongLongLong"
        android:textColor="#ffffffff"
        android:background="@drawable/button_image_01"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Small"
        android:textColor="#ffffffff"
        android:background="@drawable/button_image_02"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="MediumMediumMedium"
        android:textColor="#ffffffff"
        android:background="@drawable/button_image_02"/>

    <Button
        android:id="@+id/btn6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="LongLongLongLongLongLongLong"
        android:textColor="#ffffffff"
        android:background="@drawable/button_image_02"/>
a
</LinearLayout>

 


07-2 새로운 뷰 만들기

- 뷰의 영역과 크기는 그 뷰를 포함하고 있는 레이아웃의 영향을 받아 정해진다. 이때 개발자가 뷰의 상태에 따라 추가적인 코드를 넣을 수 콜백 메서드가 호출됨

- 뷰가 스스로의 크기를 정할 때 자동으로 호출되는 메서드는 onMeasure()이 스스로를 레이아웃에 맞게 그릴 때는 onDraw() 메서드가 자동으로 호출

 

■ onDraw() 메서드와 invalidate() 메서드 이해하기

- 새로운 뷰를 클래스로 정의하고 그 안에 onDraw() 메서드를 다시 정의한 후 필요한 코드를 넣어 기능을 구현하면 다른 모양으로 보이는 뷰를 직접 만들 수 있는 것 

 

> SampleView 프로젝트

 

1단계 

▼ Mybutton.java

public class MyButton extends AppCompatButton {
    public MyButton(Context context) {
        super(context);
    }

    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
}

- MainActivit.java 말고 MyButton 클래스를 만들고 androidx.appcompat.widget.AppCompatButton 클래스를 상속받는다. 

- MyButton.java 파일이 만들어지면 클래스 안족에 빨간 줄이 표시된다. Alt + insert 눌러서 생성자를 생성한다

- 세 개 중 두 개가 필수 생성자이다. 하나는 Context 객체만 파라미터로 전달받고, 두 번째 생성자는 Context와 AttributeSet 객체를 파라미터로 전달받는다. 

- 안드로이드는 UI 객체를 만들 때 Context 객체를 전달받도록 되어 있으므로 생성자에는 항상 Context 객체가 전달되어야 한다. 

- AttributSet 객체는 XML레이아웃에서 태그에 추가하는 속성을 전달받기 위한 것

- 생성자가 두 개이므로 이 뷰가 초기화될 때 필요한 코드는 init() 메서드를 만들어 그 안에 정의한다. 그러면 어떤 생성자가 호출되든 상관없이 동일한 초기화 작업이 진행되도록 만들 수 있다. 

 

2단계

▼ Mybutton.java 

public class MyButton extends AppCompatButton {
    public MyButton(Context context) {
        super(context);
        init(context);
    }

    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
    
    private void init(Context context) {
        setBackgroundColor(Color.CYAN);
        setTextColor(Color.BLACK);
        
        float textSize = getResources().getDimension(R.dimen.text_size);
        setTextSize(textSize);
    }
}

- init() 메서드에서는 Context 객체를 전달하도록 했으며 init() 메서드 안에서는 뷰의 배경색과 글자색을 설정하도록 했다. 

- 배경색을 설정할 때는 setBackgroundColor(), 글자색은 setTextColor() 메서드를 호출하면 됩니다. 

- 글자 크기를 조절하려면 setTextSize() 메서드를 이용해야 하는데 픽셀 단위 설정만 할 수 있기 때문에, sp 단위를 사용하는 것이 좋다. 하지만 sp 단위 설정으로 글자 크기를 조절하려면 소스 코드가 아니라 XML 파일을 사용해야 한다. 

 

3단계

app/res/values 폴더 안에 dimens.xml 파일을 하나 만들자. 

 

▼ dimens.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="text_size">16sp</dimen>
</resources>

- dimes.xml 파일은 크기 값 등을 정의할 수 있는 파일

- 소스 코드에서 참조할 때는 Resources 객체의 getDimension() 메서드를 사용한다. 이 메서드에서 변환하는 값은 픽셀 값으로 자동 변환된 값이다. 

 

4단계

MyButton.java 클래스에 onDraw() 메서드와 onTouchEvent() 메서드를 재정의 해보자. 

 

▼ Mybutton.java 

package com.ogrg.techtown.sampleview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

import androidx.appcompat.widget.AppCompatButton;

public class MyButton extends AppCompatButton {
    public MyButton(Context context) {
        super(context);
        init(context);
    }

    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        setBackgroundColor(Color.CYAN);
        setTextColor(Color.BLACK);

        float textSize = getResources().getDimension(R.dimen.text_size);
        setTextSize(textSize);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        Log.d("MyButton", "onDraw() 호출됨.");
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("MyButton", "onTouchEvent() 호출됨.");
        
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                setBackgroundColor(Color.BLUE);
                setTextColor(Color.RED);
                
                break;
                
            case MotionEvent.ACTION_OUTSIDE:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                setBackgroundColor(Color.CYAN);
                setTextColor(Color.BLACK);

                break;
        }
        invalidate();
        
        return true;
    }
}

- MotionEvent 객체에는 getAction() 메서드가 있어서 손가락이 눌렸는지, 눌린 상태로 드래그되는지 또는 손가락이 떼어졌는지 알 수 있게 코드를 작성했다.  getAction() 메서드는 정수형 값으로 이 상태를 반환한다. 

 

5단계 

activity_main.xml 코드를 수정하자.

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">

    <com.ogrg.techtown.sampleview.MyButton
        android:id="@+id/button"
        android:layout_width="200dp"
        android:layout_height="80dp"
        android:layout_centerInParent="true"
        android:text="시작하기"
        />

</RelativeLayout>

- MyButton은 직접 정의한 위젯이므로 XML 레이아웃이기 때문에 패키지 이름까지 함께 넣어야 한다. 

 


07-3 레이아웃 정의하고 카드뷰 넣기

- 레이아웃을 상속해서 새로운 레이아웃을 만들고 그 안에 카드뷰를 넣어보자. 

 

> SampleLayout 프로젝트

 

1단계

레이아웃에 사용될 XML파일과 소스 코드를 만든다. 

 

layout.xml

<?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"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="80dp"
        android:layout_height="80dp"
        app:srcCompat="@mipmap/ic_launcher" />

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

        <TextView
            android:id="@+id/textView3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="이름"
            android:textSize="30sp"/>

        <TextView
            android:id="@+id/textView4"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="전화번호"
            android:textSize="25sp"
            android:textColor="#FF0000FF"/>
    </LinearLayout>

</LinearLayout>

- XML 파일과 매칭될 클래스 파일을 만들어야 한다.  

 

 

▼ Layout1.java

package com.ogrg.techtown.samplelayout;

import android.content.Context;
import android.text.Layout;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;

import androidx.annotation.Nullable;

public class Layout1 extends LinearLayout {


    public Layout1(Context context) {
        super(context);
        init(context);
    }

    public Layout1(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.layout, this, true);
    }
}

 

- init() 메서드 안에서는 LayoutInflater 객체를 참조했다. 이 객체는 시스템 서비스로 제공되므로 getSystemService() 메서드를 호출하면서 파라미터로 Context.LAYOUT_INFLATER_SERVICE 로 전달하면 객체가 반환된다. 

- 이 객체의 inflate() 메서드를 호출하면서 XML 레이아웃 파일을 파라미터로 전달하면 인플레이션(XML 파일의 객체화)이 진행된다. 

- 인플레이션 과정이 끝나면 XML 레이아웃 파일 안에 넣어둔 이미지뷰나 텍스트뷰를 찾아서 참조할 수 있다. 액티비티에서 사용했던 findViewById() 메서드를 동일하게 호출하면 됨

 

2단계 

XML 레이아웃 파일 안에 넣어둔 이미지뷰나 텍스트뷰를 찾아서 참조할 수 있도록 findViewById를 이용해 이미지뷰와 텍스트뷰를 찾아내기

 

▼ Layout1.java

package com.ogrg.techtown.samplelayout;

import android.content.Context;
import android.text.Layout;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.Nullable;

public class Layout1 extends LinearLayout {

    ImageView imageView;
    TextView textView, textView2;

    public Layout1(Context context) {
        super(context);
        init(context);

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.layout, this, true);

        imageView = findViewById(R.id.imageView);
        textView = findViewById(R.id.textView3);
        textView2 = findViewById(R.id.textView4);

    }

    public void setImage(int resId){
        imageView.setImageResource(resId);
    }

    public void setName(String name){
        textView.setText(name);
    }

    public void setMobile(String moblie){
        textView.setText(moblie);
    }

    public Layout1(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.layout, this, true);
    }
}

 

- 이미지뷰에 보이는 이미지를 바꿀 수 있는 메서드 중의 하나가 setImageResource() 메서드인데 이 메서드는 app/res/drawable 폴더 안에 들어있는 이미지 파일을 참조하는 정수 값을 파라미터로 전달받는다. 

 

3단계

레이아웃을 상속한 뷰를 메인 액티비티에 추가하기

 

 

 


07-4 리싸이클러뷰 만들기

- 세로 모양으로 된 화면 컨트롤

 

 


07-5 스피터 사용하기

 

 


Mission 13 리싸이클러뷰에 고객 정보 추가하기

Mission 14 쇼핑 상품 화면 구성하기

 

 

 

소문난 명강의 오준석의 플러터 생존 코딩:Flutter와 Dart 입문부터 안드로이드와 iOS용 3가지 앱 개발까지, 한빛미디어이것이 자바다:신용권의 Java 프로그래밍 정복, 한빛미디어Hello Coding 프로그래밍:개념부터 처음 배우는, 한빛미디어처음 배우는 플러터:예제로 배우는 크로스 플랫폼 애플리케이션 개발, 한빛미디어돈 되는 안드로이드 앱 만들기:안드로이드 앱 개발을 위한 기본적이고 필수적인 내용 수록, 영진닷컴

 

 

반응형


댓글