본문 바로가기
Project

[프로젝트] 대학시간표(CollegeTimeTable) 레이아웃 커스텀 뷰 제작기 - 1

by Sky Titan 2020. 8. 28.
728x90

 한 동안 안드로이드 관련 포스팅을 안 올렸는데...드디어 올리네요. 사실 이것도 어떻게 보면 중복 주제입니다. 이전에 올렸던 시간표 (TimeTable) 레이아웃 제작했던 것을 custom view로 만들어서 라이브러리화 시켜보기로 했습니다.

 목표는 시간표 레이아웃을 편하게 구성하고 기능을 사용할 수 있는 높은 편의성과 뛰어난 커스터마이징을 지원하는 자율성을 동시에 갖춘 라이브러리 제작입니다.(근데 이건 누구나 다 그렇지 않나..)

이름하여 CollegeTimeTableLayout 입니다.

 

Sky-Titan/CollegeTimeTableLayout

CollegeTimeTableLayout Library Project. Contribute to Sky-Titan/CollegeTimeTableLayout development by creating an account on GitHub.

github.com

 

 

 참 이름 한 번 드릅게 길죠? 원래 TimeTableLayout 이라고 심플하고 무슨 레이아웃인지 바로 알 수 있게 지으려고 했는데 이미 TimeTableLayout이라고 하는 걸출한 오픈소스 레이아웃 라이브러리가 존재했습니다.....

때문에 배포해서 널리 퍼뜨려서 스타 개발자가 될 거라는 소박한 꿈은 바로 나가리 되어 버렸습니다....ㅜㅜ

 어찌됐든 만드는 것에 의의를 두고 제작해보았습니다. 아직 완성단계는 아니고 계속 개발 중이에요. 커스텀 뷰 제작은 처음인지라 서투네요.

1. 라이브러리 프로젝트 만들기

이건 너무 간단하게 안드로이드 개발자 홈페이지에 한글로 친절하게 적혀있습니다.

 

Android 라이브러리 만들기  |  Android 개발자  |  Android Developers

Android 라이브러리를 생성하는 방법을 알아보세요.

developer.android.com

2. collegetimetable_layout.xml 생성

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


</merge>

레이아웃의 모태가 될 xml을 생성합니다.

3. CollegeTimeTableLayout 클래스 생성

 우선 GridLayout을 상속받는 CollegeTimeTableLayout이라는 클래스를 만들어줍니다.

public class CollegeTimeTableLayout extends GridLayout {

    private static String TAG = "CollegeTimeTableLayout";
  
    public CollegeTimeTableLayout(Context context) {
        super(context);

    }

    public CollegeTimeTableLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

    }

    public CollegeTimeTableLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

    }

    public CollegeTimeTableLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

    }
}

그리고 커스텀 뷰이니 xml의 필요한 attribute들도 추가를 해야하니 attrs.xml을 생성합니다.

 

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CollegeTimeTableLayout">
        <attr name="cellTextColor" format="color" />
        <attr name="cellColor" format="color" />
        <attr name="cellMarginRight" format="integer"/>
        <attr name="cellMarginTop" format="integer"/>
        <attr name="cellMarginBottom" format="integer"/>
        <attr name="cellMarginLeft" format="integer"/>

    </declare-styleable>
</resources>

Constructor 4가지를 전부 선언한 상태이고. 그리고 맨 처음 레이아웃이 생성되었을 때 뷰를 초기화 하기 위해 필요한 작업들을 수행할 initView 메소드를 작성합니다.

 

private void initView(Context context, AttributeSet attrs) {

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

        if (attrs != null) {
            //attrs.xml에 정의한 스타일을 가져온다
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CollegeTimeTableLayout);
            cellColor = a.getColor(R.styleable.CollegeTimeTableLayout_cellColor, getResources().getColor(R.color.white,null));
            cellTextColor = a.getColor(R.styleable.CollegeTimeTableLayout_cellTextColor, getResources().getColor(R.color.black,null));
            cellMarginTop = a.getInt(R.styleable.CollegeTimeTableLayout_cellMarginTop, 5);
            cellMarginBottm = a.getInt(R.styleable.CollegeTimeTableLayout_cellMarginBottom, 5);
            cellMarginRight = a.getInt(R.styleable.CollegeTimeTableLayout_cellMarginRight, 5);
            cellMarginLeft = a.getInt(R.styleable.CollegeTimeTableLayout_cellMarginLeft, 5);
            a.recycle(); // 이용이 끝났으면 recycle() 호출
        }

        initRowColumnNames();//행,열 이름 초기화

        addCells();//cell추가
    }

이 곳에선 아까 만들었던 collegetimetable_layout.xml을 inflate 시키고 커스텀 attribute 들의 value들을 가져옵니다.

4. Cell 클래스 생성 (ripple effect, cell 배경커스터마이징)

그리고 시간표에서 cell 역할을 해줄 Cell 클래스를 생성해줍니다.

public class Cell extends FrameLayout {

    private TextView textView;

    public Cell(Context context) {
        super(context);
        initView(context,null);
    }

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

    public Cell(Context context,AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context,attrs);
    }

    private void initView(Context context, AttributeSet attrs) {

        //...초기화 시 할 작업들
       textView = new TextView(context);

        //ripple effect 적용
        TypedValue typedValue = new TypedValue();
        getContext().getTheme().resolveAttribute(R.attr.selectableItemBackground, typedValue,true);
        int resId = typedValue.resourceId;
        textView.setBackgroundResource(resId);

        setClickable(false);
        addView(textView);

        FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) textView.getLayoutParams();
        layoutParams.width = LayoutParams.MATCH_PARENT;
        layoutParams.height = LayoutParams.MATCH_PARENT;
        textView.setLayoutParams(layoutParams);
    }
}

 사실 그냥 textview로 cell 역할을 시킬 수도 있습니다만 cell을 클릭할 때 생기는 ripple effect와 cell 배경색을 동적으로 커스터마이징 할 수 있는 기능을 동시에 넣는 방법을 생각했었습니다.

 만약 그냥 textview로만 사용을 했으면 background에 ripple effect, 혹은 커스텀 배경 지정. 둘 중 하나밖에 할 수가 없습니다.

 물론 drawable 파일을 만들어서 적용할 수 있겠지만 전 무조건 기본적으로 ripple effect는 들어가길 바랬고 drawable로 일일이 파일을 만들어서 적용시키면 사용자 입장에서 불편할 수도 있다고 생각이 됐습니다.

 때문에 FrameLayout 위에 TextView를 width, height 둘 다 Match_parent 시켜서 Cell이라는 새로운 객체를 만들었습니다.

Cell 구조

 

뭐 대충 이런 상태라는 것이죠.

그리고 textview의 background에는 ripple effect를 적용시킴과 동시에

@Override
    public void setOnClickListener(View.OnClickListener l) {
        setClickable(true);
        textView.setOnClickListener(l);
    }

setOnClickListener를 오버라이드해서 FrameLayout과 textview 둘 다 동시에 리스너를 적용시키게 합니다.

그리고 FrameLayout의 background에 drawable이나 color를 설정하게 되면

 에뮬레이터라 그런가 ripple effect가 부자연스럽네요

 

 다음과 같이 cell 배경 커스터마이징에 구애 받지 않고 ripple effect를 동시에 적용시키는 것이 가능하게 됩니다. 사실 이렇게 하면 어플이 무거워 질 것 같아서....걱정이 되긴하는데....요즘 HW 성능 좋으니까....크게 신경 안 쓰기로... 더 좋은 방법이 있으면 바로 적용하려구요...

5. init 메소드들

CollegeTimeTableLayout 의 initView 메소드에 있던 initRowColumnNames(), addCells() 메소드를 살펴 보겠습니다. 이 2개의 메소드는 절대 user가 임의로 접근하게 해선 안되기에 private로 잠궈두었습니다.

//row, column 이름 초기화
    private void initRowColumnNames()
    {
        row_names = new String[getRowCount()];
        column_names = new String[getColumnCount()];

        for(int i=0;i<getRowCount();i++)
            row_names[i] = i+"";
        for(int i=0;i<getColumnCount();i++)
            column_names[i] = i+"";
    }

 

이 메소드는 행과 열의 기본 이름들을 초기화하는 메소드입니다. 마땅히 뭐라고 불러야할 지 기억이 안나서 이름이라고 하긴 했는데

 

 

 빨간색 동그라미 쟤네를 말하는 겁니다. 사실 이것도 그냥 사용자가 알아서 하게 냅둘까 하다가 기본적으로 처리하게 하기로 했습니다.

 처음 생성시엔 둘 다 행열 숫자로 1,2,3,4....이렇게 지정하기로 했습니다.

그 다음 addCells()입니다.

} }

//row, column만큼 cell 추가
    private void addCells()
    {
        for(int i=0;i<getRowCount();i++)
        {
            for(int j=0;j<getColumnCount();j++)
            {
                Cell cell = new Cell(getContext());
                cell.setTag(row_names[i]+"-"+column_names[j]);//행열로 태그 설정

                cell.setRow(i);
                cell.setColumn(j);

                cell.setBackgroundColor(cellColor);//cell 배경색 설정
                cell.setTextColor(cellTextColor);//cell text 색 설정
                cell.setGravity(Gravity.CENTER);
                cell.setText("");

                if(i==0 && j!=0)
                    cell.setText(column_names[j]);
                if(j==0 && i!=0)
                    cell.setText(row_names[i]);

                GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(GridLayout.spec(i,1.0f), GridLayout.spec(j,1.0f));
                layoutParams.setGravity(Gravity.FILL);
                layoutParams.width = 0;//스케줄 추가해도 너비 일정하게 유지
                layoutParams.height = LayoutParams.WRAP_CONTENT;
                layoutParams.setMargins(cellMarginLeft,cellMarginTop,cellMarginRight,cellMarginBottm);
                cell.setLayoutParams(layoutParams);

                addView(cell);
            }
        }
    }

여긴 말그대로 GridLayout에 cell들을 추가 해주는 겁니다. 그 전에 여러 전 처리 과정을 해주고 addView로 GridLayout에 추가해줍니다.

 

 cell을 나중에 임의로 불러올 때를 대비해서 cell의 Tag로 각 cell들을 구분하기로 했습니다.

그래서

cell.setTag(row_names[i]+"-"+column_names[j]);

 이 부분이 존재합니다. 이렇게 tag를 설정해두면 나중에 findViewWithTag로 간단하게 view를 찾아올 수 있습니다.

 

add cell 까지 진행된 기본 화면이 위와 같습니다.

다음은 2편으로 넘어가도록 하겠습니다.

728x90

댓글