마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

(안드로이드 자바) 여러개의 texview를 클릭이벤트 처리 하려고합니다

0 추천

119개의 textview가 xml에 만들어져있고 id는 text1, text2 이런식입니다.

특정 textview를 클릭하면 빨간색으로 backgroundcolor를 바꿔주도록했습니다. 

각각의 텍스트뷰에 클릭이벤트 처리를 위해 배열에 저장하고 for문을 사용했는데 

실행시 자꾸 튕기네요

색깔 바꿔주는 부분은 문제가 없는데 배열이나 클릭이벤트쪽에서 문제가 있는것같습니다. 

혹시 아시는분 있다면 피드백 부탁드립니다.

public class Writing_Schedule extends AppCompatActivity {
    private boolean State = false;
    TextView[] tv = new TextView[119];

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

        for (int i = 1; i <= tv.length; i++) {
            int getID = getResources().getIdentifier("text" + i, "id", "com.example.promise");
            tv[i] = (TextView) findViewById(getID);
        }

        for (int i = 1; i <= tv.length; i++) {
            int finalI = i;
            tv[i].setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if(State)
                    {
                        State = false;
                        tv[finalI].setBackgroundResource(R.drawable.table_touch_again);
                    }
                    else
                    {
                        State = true;
                        tv[finalI].setBackgroundResource(R.drawable.table_touch);
                    }
                }
            });
        }
    }
}

 

큐큐큐 (150 포인트) 님이 2022년 4월 27일 질문

1개의 답변

0 추천
 
채택된 답변

코드는자체 이상은 없어 보이는데요, 아래처럼 좀 더 읽기 쉽게 작성할 수 있을 것 같구요.

private static final NUMBER_OF_VIEWS = 119;
private boolean viewClicked = false;

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

  
    bindViews();
}

private View.OnClickListener viewClickListener =  new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            viewClicked != viewClicked;
            toggleBackgroundForView(view);
        }
};

private void toggleBackgroundForView(View view) {
    int backgroundResId = viewClicked ? R.drawable.table_touch : R.drawable.table_touch_again;
    view.setBackgroundResource(backgroundResId);
}

private void bindViews() {
     for (int i = 1; i <= NUMBER_OF_VIEWS; i++) {
        String idAsString = "text" + i;;
        int viewId = getResources().getIdentifier(idAsString, "id", "com.example.promise");
        View view = Objects.requireNotNull(findViewById(viewId), idAsString + " must not be null");
        view.setOnClickListener(viewClickListener);
    }
}

 

에러가 있다면, Logcat을 통해서 확인할 수도 있고 브레이크 포인트를 걸어서 디버깅을 할 수도 있습니다. 의심스러운 부분은,
tv[finalI]부분이 null인 것 같습니다.

 

spark (226,720 포인트) 님이 2022년 4월 27일 답변
큐큐큐님이 2022년 4월 28일 채택됨
방금 확인해봤는데 tv[i]가 null 값으로 나오네요. 근데 null값이라는게 이해가 잘 가지 않습니다. tv배열에 텍스트뷰에 대한 정보를 넣었는데 왜 null인것인지, 그렇다면 어떤값을 제가 임의로 넣어줘야하는건지 모르겠습니다
getIdentifier부분에서 문제가 생건 것은 아닌지 확인해 보세요.
개발자 문서를 보시면 아래 처럼 나옵니다.

Note: use of this function is discouraged. It is much more efficient to retrieve resources by identifier than by name.

int The associated resource identifier. Returns 0 if no such resource was found. (0 is not a valid resource ID.)
가능하면 사용하지 말라는 것과 못찾을 경우 0을 리턴하므로 아래처럼 디버깅이 쉬운코드가 좋을 것 같구요.
패키지 이름은 하드코드 대신 동적으로 구해오시구요.

String resIdAsString = "text" + i, "id";
int getID = getResources().getIdentifier(resIdAsString, "id", Writing_Schedule.this.getPackageName());
if (getIID == 0) throw NoSuchElementNotFound(resIdAsString+ "리소스 ID가 존재하지 않습니다.")
tv[i] = Objects.requireNotNull(((TextView) findViewById(getID));

그리고 혹 뷰를 동적으로 생성하신 다면, 생성시에 리스트에 넣고 이걸 사용하시거나 생성시에 OnClickListener를 할당하시면 더 안전할 것 같습니다.

View view = createView(...);
view.setOnClickListener(...)
감사합니다 왜인지 모르겠지만 배열 120까지 하고 for문에서 tv.length-1 해주니까 되더라구요 ㅠ
아마 에러메세지를 보셨다면 IndexOutOfBoundException이 있었을 겁니다. 루프가 배열의 길이를 벗어났기 때문에 생긴 에러입니다. 에러가 나면 에러메시지를 잘 보는 것이 중요합니다. 많은 경우는 에러의 종류와 메세지를 가지고 원인을 추적하고 해결방법을 찾기 때문입니다.

그리고 텍스트뷰를 119개씩이나 XML에 배치하는 건 좋지 않아 보입니다. 디자인도 어렵거니와 수정시에 많은 부작용이 우려됩니다.
다른 질문을 보니 달력관련 앱을 만드시는 것 같은데, 하루에 해당하는 레이아웃과 이걸 다섯개 배치한 레이아웃을 커스텀 뷰로 만드셔서 사용하세요. 그렇게 하면 일일이 지금처럼 뷰를 맵핑할 필요가 없어집니다.
...