본문 바로가기

안드로이드

[안드로이드] Custom View 커스텀 뷰 (1) - 만들기

안드로이드에서 제공하는 뷰에 필요한 기능을 추가하거나, 새로운 뷰를 만들어서 사용해야하는 경우가 생깁니다.

저 같은 경우는 여러 뷰들을 묶어서 하나로 사용하기 위해서 커스텀 뷰를 만들었습니다.

이번 글에서는 하나로 묶어서 쓰는 것을 예제로 커스텀 뷰를 만들어 보겠습니다.

 

커스텀 뷰 하나를 만듭니다.

[ res > layout > my_custom_layout.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"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/white_border"
    android:padding="10dp"
    >

    <ImageView
        android:id="@+id/ivThumbnail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher_foreground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="제목"
        android:textColor="@color/white"
        app:layout_constraintBottom_toTopOf="@+id/tvContents"
        app:layout_constraintLeft_toRightOf="@+id/ivThumbnail"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <TextView
        android:id="@+id/tvContents"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textColor="@color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/ivThumbnail"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvTitle"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

 

요렇게 생겼습니다.

 

이제 이 커스텀 뷰용 클래스를 하나 만듭니다.

[ MyCustomView.java ]

 

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;

public class MyCustomView extends ConstraintLayout {
    View mView;

    ImageView ivThumbnail;
    TextView tvTitle;
    TextView tvContents;

    public MyCustomView(@NonNull Context context) {
        super(context);
        init();
    }

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

    public MyCustomView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public void init(){
        LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mView = layoutInflater.inflate(R.layout.my_custom_layout, this, false);
        addView(mView);

        ivThumbnail = this.findViewById(R.id.ivThumbnail);
        tvTitle = this.findViewById(R.id.tvTitle);
        tvContents = this.findViewById(R.id.tvContents);
    }
}

 

init() 함수에서 LayoutInFlaterxml 파일View 객체반환해주는 역할을 합니다.

 

이제 커스텀뷰를 만들었으니, 제대로 나오나 확인해보겠습니다.

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"
    android:background="@color/black"
    >

    <com.genue.tester.MyCustomView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

아주 잘나오네요!!

 

조금 더 활용해 보겠습니다.

지금 커스텀 뷰에는 이미지, 제목, 내용 이렇게 세 개의 값이 있습니다. 이걸 코드 상에서 수정할 수 있지만, xml 파일에서 바꿀 수 있도록 해보겠습니다. 이미 존재하는 뷰들에 보면 android: 이렇게 시작하는 속성이 있고, app: 으로 시작하는 속성이 있습니다. 여기서 app:이 바로 지금 추가할려는 것입니다.

 

필요한 속성들을 만들겠습니다.

[ res > valus > attrs.xml ]

 

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyCustomView">
        <attr name="title" format="string|reference"/>
        <attr name="content" format="string|reference"/>
        <attr name="thumbnail" format="color|reference"/>
    </declare-styleable>
</resources>

name 은 다른 커스텀 속성과 구문되게 하시면 됩니다.

format 값 중 reference 는 id 형태도 쓰기 위해서 넣었습니다. @sting/... , @drawable/...

 

그리고 아까 만든 MyCustomView.java 를 수정하겠습니다.

 

package com.genue.tester;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;

public class MyCustomView extends ConstraintLayout {
    View mView;

    ImageView ivThumbnail;
    TextView tvTitle;
    TextView tvContents;

    public MyCustomView(@NonNull Context context) {
        super(context);
        init();
    }

    public MyCustomView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
        getAttrs(attrs);
    }

    public MyCustomView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
        getAttrs(attrs, defStyleAttr);
    }

    public void init(){
        LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mView = layoutInflater.inflate(R.layout.my_custom_layout, this, false);
        addView(mView);

        ivThumbnail = this.findViewById(R.id.ivThumbnail);
        tvTitle = this.findViewById(R.id.tvTitle);
        tvContents = this.findViewById(R.id.tvContents);
    }

    private void getAttrs(AttributeSet attrs){
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MyCustomView);

        setTypeArray(typedArray);
    }

    private void getAttrs(AttributeSet attrs, int defStyle){
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0);

        setTypeArray(typedArray);
    }

    private void setTypeArray(TypedArray typedArray){
        String title = typedArray.getString(R.styleable.MyCustomView_title);
        tvTitle.setText(title);

        int thumbnail_resID = typedArray.getResourceId(R.styleable.MyCustomView_thumbnail, R.drawable.ic_launcher_foreground);
        ivThumbnail.setBackgroundResource(thumbnail_resID);

        String content = typedArray.getString(R.styleable.MyCustomView_contents);
        tvContents.setText(content);

        typedArray.recycle();
    }
}

 

이렇게 하고 activity_main.xml에서 커스텀뷰를 수정해보면

 

<com.genue.tester.MyCustomView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"

    app:title="타이틀"
    app:contents="콘텐츠입니다"
    app:thumbnail="@drawable/ic_launcher_background"
/>

 

 

잘 바뀐 것을 확인할 수 있습니다!

 

이렇게 간단한 커스텀뷰를 한번 만들어 봤습니다. 다음번에는 이 커스텀뷰를 활용해서 화면 터치 시 원하는 위치에 찍어보도록 하겠습니다.