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

setOnClickListener() 안에 들어가는 객체가 null 값이라는 걸 알았는데 수정을 어떻게 해야할지 모르겠습니다ㅠ

0 추천

안드로이드 시작한지 얼마안된 초보인데,, 어떤 걸 수정해야할지 도저히 감이 안 잡힙니다 ㅠㅠ 



public class MainActivity extends AppCompatActivity {
    Button btn;
    int vCount[] =new int[6];
    ImageView[] imgV = new ImageView[6];
    int[] imgID = {R.id.img1, R.id.img2, R.id.img3, R.id.img4, R.id.img5, R.id.img6};
    String Name[] = {"크리스", "브라운", "아랍","철수","은수","강민"};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        android:setTitle("리더선거 투표");

        btn = (Button)findViewById(R.id.btn);

        for (int i=0; i<imgID.length; i++) {
            final int index;
            index = i;
            imgV[index] = (ImageView) findViewById(imgID[index]);
            imgV[index].setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    vCount[index]++;
                    Toast.makeText(getApplicationContext(), Name[index] + "에게 투표하였습니다.",
                            Toast.LENGTH_SHORT).show();
                }
            });
        }
        btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getApplicationContext(), resultvote.class);
                intent.putExtra("vCount", vCount);
                intent.putExtra("Name", Name);
                startActivity(intent);
            }
        });
    }
아이스는아아 (140 포인트) 님이 2021년 12월 18일 질문

1개의 답변

0 추천

테스트를 해보면 올리신 코드에는 에러가 없는데요, 정확하게 어디에서 어떤 에러가 나는 건가요?

그리고 위의 코드에서 "투표"는 비지니스 로직에 해당하기 때문에 항상 뷰와 분리해서 독립적인 클래스로 만드는 것이 좋은 접근방법입니다. 이렇게 비지니스 로직을 담는 클래스를 Model이라고 합니다. Model를 발견하고 만드는 것이 OOP에서는 상당히 중요합니다. 제가 간단하게 아랫처럼 구현을 해봤습니다. 코드를 참조하셔서 더 좋은 코드를 만드셨으면 좋겠네요.

먼저 후보자 클래스입니다. String만 사용하는 것 보다는 제대로된 클래스로 분리하는 것이 좋습니다. 투표에는 "후보자"가 존재한다는 것을 이미 알고 있으니까요. 필요에 따라 필드를 더 추가하시면 됩니다. 

public class Candidate {
    private final String id;
    private final String name;

    public Candidate(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

투표를 관리할 Model 클래스입니다.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

public class Vote {

    // Vote를 싱글톤으로 사용하기 위한 팩토리 메소드.
    public static Vote getInstance() {
        return Impl.INSTANCE;
    }

    // 싱글톤을 지원하기 위한 private static class. 클래스가 생성되자 마자 INSTANCE가 싱글톤이 되는 것을 보장한다.
    private static class Impl {
        public static Vote INSTANCE = new Vote();
    }

    // private 생성자. Vote를 싱글톤으로 사용할 것이기 때문에 생성자를 외부에 공개하지 않는다. 대신 외부에서는
    // Vote.getInstance()로 접근해서 사용해야 한다.
    private Vote(){}

    private List<Candidate> candidates = new ArrayList<>();
    private final Map<Candidate,Integer> voteCounts = new HashMap<>();

    public List<Candidate> getCandidates() {
        return candidates;
    }

    public void setCandidates(List<Candidate> candidates) {
        this.candidates = candidates;
    }

    public Map<Candidate, Integer> getVoteCounts() {
        return voteCounts;
    }
    
    //후보자에게 투표한다. 후보자를 찾아서 득표수를 1증가 시킨다.
    public void castVoteTo(Candidate candidate) {
        String candidateId = candidate.getId();
        Candidate found = findCandidateById(candidateId);
        if (found == null) {
            throw new NoSuchElementException("Cannot find candidate");
        }

        voteCounts.put(candidate,  getVoteCountOf(candidate) + 1);
    }

    // 후보자가 존재하는지 찾기.
    @Nullable
    private Candidate findCandidateById(String candidateId) {
        for (Candidate person : candidates) {
            if (person.getId().equals(candidateId)) {
                return person;
            }
        }
        return null;
    }

    // 후보자의 득표수 가져오기.
    private int getVoteCountOf(Candidate candidate) {
        Integer voteCount= voteCounts.get(candidate);
        return voteCount == null ? 0 : voteCount;
    }
}

 모델 클래스는 일단 Singleton을 적용했습니다. 모델 클래스 자체보다는 후보자, 득표수를 저장해서 사용하고 이 저장 클래스를 Sington으로 만드는 것이 더 좋은 접근방법이지만, 코드가 너무 복잡해 지므로 간단하게 Vote클래스를 Singleton으로 만들었습니다. Singleton을 사용하기 위해서 생성자를 private으로 만들고 static inner class와 getInstance라는 public 팩토리 메소드를 만들었습니다.

 

액티비티에서 Model class를 사용합니다. 변수명등은 더 읽기 좋다고 생각이 드는 대로 바꿨습니다.

public class MainActivity extends AppCompatActivity {

    private Button showResultBtn;
    private final List<ImageView> imageViews = new ArrayList<>();
    private final int[] imgIDs = {R.id.img1, R.id.img2, R.id.img3, R.id.img4, R.id.img5, R.id.img6};

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

        initCandidates();
        bindCandidates();
        showResultBtn = findViewById(R.id.btn);
        showResultBtn.setOnClickListener(view -> showVoteResult());
    }

    private void initCandidates() {
        Vote.getInstance().setCandidates(Arrays.asList(
                new Candidate("1", "크리스"),
                new Candidate("2", "브라운"),
                new Candidate("3", "아랍"),
                new Candidate("4", "철수"),
                new Candidate("5", "은수"),
                new Candidate("6", "강민")
        ));
    }

    private void bindCandidates() {
        int i = 0;
        for (Candidate candidate : Vote.getInstance().getCandidates()) {
            final int index = i++;
            ImageView imageView = findViewById(imgIDs[index]);
            imageViews.add(imageView);
            imageView.setOnClickListener(view -> voteClicked(candidate));
        }
    }
    
    private void voteClicked(Candidate candidate) {
        Vote.getInstance().castVoteTo(candidate);
        MessageUtil.showShortToast(this, getString(R.string.vote_message, candidate.getName()));
    }

    private void showVoteResult() {
        Intent intent = new Intent(this, VoteResultActivity.class);
        startActivity(intent);
    }
}


public class MessageUtil {
    public static void showShortToast(Context context, String message) {
        Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
    }
}

 

strings.xml

<resources>
    ...
    <string name="vote_message">%1$s에게 투표하였습니다.</string>
</resources>

 

Vote가 singleton이므로 VoteResultActivity에는 데이터를 넘겨줄 필요가 없습니다. VoteResultActivity에서는  Vote.getInstance().getVoteCounts()를 호출해서 사용하면 되겠죠.

spark (227,530 포인트) 님이 2021년 12월 18일 답변
spark님이 2021년 12월 18일 수정
...