본문 바로가기
안드로이드 자바

[Java][Android] 별점 만들기 (CustomView 만들기)

by teamnova 2022. 4. 5.
728x90

안녕하세요. 오늘은 나만의 CustomView를 만들겠습니다.

 

클릭 시 별 색깔을 칠해주는 별점 View를 만들려고 합니다.

- 클릭 시 해당 별 위치까지 색깔을 칠해 줌.

- 점수에 따라서 Description Text가 변경 됨.  // Wrost, Bad, Normal, Good, Best

 

 

우선 빈 별 과, 색칠된 별 파일을 준비하여 drawable폴더에 넣습니다.

- empty_star : 빈 별

- filled_star : 색칠 된 별

 

다음은 별 전체를 담을 레이아웃을 준비합니다.

-별 5개 (star0, star1, star2, star3, star4)

- 설명 (description) // on_off할 수 있음.

 

stars_layout.xml

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

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/star0"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="4dp"
            android:src="@drawable/empty_star" />

        <ImageView
            android:id="@+id/star1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="4dp"
            android:src="@drawable/empty_star" />

        <ImageView
            android:id="@+id/star2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="4dp"
            android:src="@drawable/empty_star" />

        <ImageView
            android:id="@+id/star3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="4dp"
            android:src="@drawable/empty_star" />

        <ImageView
            android:id="@+id/star4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="4dp"
            android:src="@drawable/empty_star" />
    </LinearLayout>

    <TextView
        android:id="@+id/description"
        android:textSize="25sp"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Description" />

</LinearLayout>

 

본격적으로 CustomView 클래스를 작성해봅시다.

public class StarsView extends LinearLayout {
    private ImageView[] mStars = new ImageView[5];
    private TextView mDescription;
    private int mCount = 0;
    private boolean isShowed;
    
    private final String[] DESCRIPTION = {
            "Worst",
            "Bad",
            "Normal",
            "Good",
            "Best"
    };

}

- 여러 개의 별을 담기에, LinearLayout을 상속합니다.

- 별 5개를 담을 ImageView 배열을 멤버 변수로 선언해줍니다.

- description이 TextView를 멤버 변수 선언해줍니다.

- 색칠된 별 갯수를 담을 mCount를 멤버 변수로 선언해줍니다.

- description을 표시할 지 안할 지, isShowed를 통해 멤버 변수로 선언해줍니다.

 

- 맨 윗줄에 빨간 줄이 계시 될 텐데, 커서를 올리고 알트 + enter을 눌러 Create constructor matching super class를 작성합니다. View 생성 시 필수적인 생성자를 만듭니다.

    public StarsView(Context context) {
        super(context);
        initial(context, null);
    }

    public StarsView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initial(context, attrs);
    }

    public StarsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initial(context, attrs);
    }

 

각 생성자마다 초기화를 작성시 코드 중복이 발생하므로, 초기화 겸 initial 함수를 만들어서 사용합니다.

- 필요한 전체 layout을 inflate합니다.

    public void initial(Context context, AttributeSet attrs) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.stars_layout, this);

    }

XML에서 색칠된 별점의 수, description을 보여줄 지 보여주지 않을 지, 설정할 수 있게 하기 위해 attrs.xml을 작성합니다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="StarsView">
        <attr name="count" format="integer"/> <!-- 색칠 된 별의 수 -->
        <attr name="showed_description" format="boolean"/> <!-- 설명 보여주기 -->
    </declare-styleable>
</resources>

- initial 함수에서 속성이 있는 경우, 해당 속성 값을 멤버변수에 넣어줍니다.

    public void initial(Context context, AttributeSet attrs) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.stars_layout, this);

        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StarsView);
            mCount = a.getInteger(0, 0);
            isShowed = a.getBoolean(1, true);
            
            a.recycle(); // 이용이 끝났으면 recycle() 호출
        }
    }

 

inflate가 끝나면, 필요한 것에 멤버변수에 mapping하기 위해서 onFinishInflate()를 작성해 줍니다.

클릭 이벤트를 넣기 위해서 setOnClickListener을 만들어 줍니다.

클릭 이벤트 리스너를 처리하기 위하여 StarsView 클래스가 onClickListener의 인터페이스를 상속받게 합니다.

    protected void onFinishInflate() {
        super.onFinishInflate();
        mStars[0] = (ImageView) findViewById(R.id.star0);
        mStars[1] = (ImageView) findViewById(R.id.star1);
        mStars[2] = (ImageView) findViewById(R.id.star2);
        mStars[3] = (ImageView) findViewById(R.id.star3);
        mStars[4] = (ImageView) findViewById(R.id.star4);

        mDescription = (TextView) findViewById(R.id.description);


        for (int i = 0; i < mStars.length; ++i) {
            mStars[i].setOnClickListener(this);
        }


       if (isShowed) {
           mDescription.setVisibility(View.VISIBLE);
       } else {
           mDescription.setVisibility(View.GONE);
       }

        setFilled(mCount, true);
    }

 

count가 있는 만큼 별을 채우기 위하여, setFilled 메소드를 작성합니다.

  public void setFilled(int count, boolean bForce) {
        if (!bForce && mCount == count) {
            return;
        }

        mCount = count;

        for (int i = 0; i < mStars.length; ++i) {
            if (i <= mCount) {
                mStars[i].setImageResource(R.drawable.filled_star);
            } else {
                mStars[i].setImageResource(R.drawable.empty_star);
            }
        }

        mDescription.setText(DESCRIPTION[mCount]);
    }

 

오버라이딩 한 인터페이스 onClickListener 메서드인 onClick 채웁니다.

    @Override
    public void onClick(View view) {
        int count = 0;

        switch (view.getId()) {
            case R.id.star4:
                ++count;
            case R.id.star3:
                ++count;
            case R.id.star2:
                ++count;
            case R.id.star1:
                ++count;
            case R.id.star0:
                break;
            default:
                break;
        }

        setFilled(count, false);

    }

 

 

최종적으로 완성 된 StarsView

 

StarsView.java

public class StarsView extends LinearLayout implements View.OnClickListener {
    private ImageView[] mStars = new ImageView[5];
    private TextView mDescription;
    private int mCount = 0;
    private boolean isShowed;

    private final String[] DESCRIPTION = {
            "Worst",
            "Bad",
            "Normal",
            "Good",
            "Great",
            "Best"
    } ;

    public StarsView(Context context) {
        super(context);
        initial(context, null);
    }

    public StarsView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initial(context, attrs);
    }

    public StarsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initial(context, attrs);
    }

    public void initial(Context context, AttributeSet attrs) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.stars_layout, this);

        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StarsView);
            mCount = a.getInteger(0, 0);
            isShowed = a.getBoolean(1, true);

            a.recycle(); // 이용이 끝났으면 recycle() 호출
        }
    }

    protected void onFinishInflate() {
        super.onFinishInflate();
        mStars[0] = (ImageView) findViewById(R.id.star0);
        mStars[1] = (ImageView) findViewById(R.id.star1);
        mStars[2] = (ImageView) findViewById(R.id.star2);
        mStars[3] = (ImageView) findViewById(R.id.star3);
        mStars[4] = (ImageView) findViewById(R.id.star4);

        mDescription = (TextView) findViewById(R.id.description);


       if (isShowed) {
           mDescription.setVisibility(View.VISIBLE);
       } else {
           mDescription.setVisibility(View.GONE);
       }

        setFilled(mCount, true);
    }

    public void reset() {
        for (int i = 0; i < mStars.length; ++i) {
            mStars[i].setImageResource(R.drawable.empty_star);
        }
    }


    public void setFilled(int count, boolean bForce) {
        if (!bForce && mCount == count) {
            return;
        }

        mCount = count;

        for (int i = 0; i < mStars.length; ++i) {
            mStars[i].setOnClickListener(this);

            if (i <= mCount) {
                mStars[i].setImageResource(R.drawable.filled_star);
            } else {
                mStars[i].setImageResource(R.drawable.empty_star);
            }
        }

        mDescription.setText(DESCRIPTION[mCount]);
    }

    @Override
    public void onClick(View view) {
        int count = 0;

        switch (view.getId()) {
            case R.id.star4:
                ++count;
            case R.id.star3:
                ++count;
            case R.id.star2:
                ++count;
            case R.id.star1:
                ++count;
            case R.id.star0:
                break;
            case R.id.description:
                count = 0;
                break;
            default:
                break;
        }

        setFilled(count, false);

    }
}

 

Main Activity Layout에 내가 만든 뷰를 넣습니다.

attrs.xml안에 설정한 count를 통해 초기 입력된 별 갯수를 설정할 수 있습니다.

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

    </*패키지 이름*/.StarsView
        android:layout_width="match_parent"
        app:count="0"
        app:showed_description="true"
        android:layout_height="wrap_content"/>

</LinearLayout>

 

시연 영상입니다.

 

스틱코드를 사용하시면 훨씬 더 쉽게 코딩 작업을 할 수 있습니다.

https://stickode.com/detail.html?no=2852