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

다이얼로그 안에서 timePicker 사용시 timePicker가 뜬 뒤 시간 설정을 하면 화면에 값이 넘어가지 않고 튕깁니다

0 추천

activity_main.xml ->  edit_box.xml main에서 버튼 클릭시 edit_box를 불러와서 화면에 다이얼로그를 보여주는 형태입니다.

그런데 다이얼로그 안에서 timePicker 사용시 timePicker가 뜬 뒤 시간 설정을 하면 화면에 값이 넘어가지 않고 그냥 튕겨 버려서 질문남깁니다.

public class MainActivity extends AppCompatActivity {

    private ArrayList<Dictionary> mArrayList;
    private CustomAdapter mAdapter;
    private int count = -1;

    private TextView textView_Date;
    private TimePickerDialog.OnTimeSetListener callbackMethod;


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

        this.InitializeView();
        this.InitializeListener();

        textView_Date = (TextView)findViewById(R.id.textView_date);

        RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview_main_list);
        LinearLayoutManager mLinearLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLinearLayoutManager);

        mArrayList = new ArrayList<>();
        mAdapter = new CustomAdapter( mArrayList);
        mRecyclerView.setAdapter(mAdapter);


        DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(mRecyclerView.getContext(),
                mLinearLayoutManager.getOrientation());
        mRecyclerView.addItemDecoration(dividerItemDecoration);

        Button buttonInsert = (Button)findViewById(R.id.button_main_insert);
        buttonInsert.setOnClickListener(new View.OnClickListener() {


            // 1. 화면 아래쪽에 있는 데이터 추가 버튼을 클릭하면
            @Override
            public void onClick(View v) {

                // 2. 레이아웃 파일 edit_box.xml 을 불러와서 화면에 다이얼로그를 보여줍니다.
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                View view = LayoutInflater.from(MainActivity.this)
                        .inflate(R.layout.edit_box, null, false);
                builder.setView(view);

                final Button ButtonSubmit = (Button) view.findViewById(R.id.button_dialog_submit);
                final EditText editTextID = (EditText) view.findViewById(R.id.edittext_dialog_id);
                final EditText editTextKorean = (EditText) view.findViewById(R.id.edittext_dialog_korean);
                final EditText editTextEnglish = (EditText) view.findViewById(R.id.edittext_dialog_english);

                ButtonSubmit.setText("삽입");

                final AlertDialog dialog = builder.create();
                // 3. 다이얼로그에 있는 삽입 버튼을 클릭하면
                ButtonSubmit.setOnClickListener(new View.OnClickListener() {
                    public void onClick(View v) {
                        // 4. 사용자가 입력한 내용을 가져와서
                        String strID = editTextID.getText().toString();
                        String strKorean = editTextKorean.getText().toString();
                        String strEnglish = editTextEnglish.getText().toString();
                        // 5. ArrayList에 추가하고

                        Dictionary dict = new Dictionary(strID, strEnglish ,strKorean );
                        mArrayList.add(0, dict); //첫번째 줄에 삽입됨
                        //mArrayList.add(dict); //마지막 줄에 삽입됨

                        // 6. 어댑터에서 RecyclerView에 반영하도록 합니다.
                        mAdapter.notifyItemInserted(0);
                        //mAdapter.notifyDataSetChanged();

                        dialog.dismiss();
                    }
                });

                dialog.show();
            }
        });

    }

    private void InitializeView() {
        textView_Date = (TextView)findViewById(R.id.textView_date);
    }
    public void InitializeListener()
    {
        callbackMethod = new TimePickerDialog.OnTimeSetListener()
        {
            @Override
            public void onTimeSet(TimePicker view, int hourOfDay, int minute)
            {
                textView_Date.setText(hourOfDay + "시" + minute + "분");
            }
        };
    }
    public void OnClickHandler(View view)
    {
        TimePickerDialog dialog = new TimePickerDialog(this, callbackMethod, 8, 10, true);

        dialog.show();
    }
}
<?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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/edittext_dialog_id"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:hint="소제목을 입력하세요."
        android:inputType="text"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/button"/>

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="OnClickHandler"
        android:layout_margin="16dp"
        android:gravity="center"
        android:text="시간대 입력"
        app:layout_constraintTop_toBottomOf="@id/button"
        app:layout_constraintBottom_toTopOf="@id/edittext_dialog_korean"/>

    <EditText
        android:id="@+id/textView_date"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:gravity="center"
        android:text="날짜 정보"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/button"
        app:layout_constraintBottom_toTopOf="@id/edittext_dialog_korean"/>

    <EditText
        android:id="@+id/edittext_dialog_korean"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:hint="이동수단."
        android:inputType="text"
        app:layout_constraintBottom_toTopOf="@id/edittext_dialog_english"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/textView_date" />

    <EditText
        android:id="@+id/edittext_dialog_english"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:hint="사용한 비용."
        android:inputType="text"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/edittext_dialog_korean"
        app:layout_constraintBottom_toTopOf="@id/edittext_dialog_img"/>

    <EditText
        android:id="@+id/edittext_dialog_img"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:hint="사진 첨부."
        android:inputType="text"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/edittext_dialog_english"
        app:layout_constraintBottom_toTopOf="@id/button_dialog_submit"/>

    <Button
        android:id="@+id/button_dialog_submit"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:text="수정"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/edittext_dialog_img" />

</androidx.constraintlayout.widget.ConstraintLayout>
ezhoon (130 포인트) 님이 2021년 11월 20일 질문
에러메세지와 에러가 나는 코드를 올려보세요.

1개의 답변

+1 추천

textView_Date 가 문제인 거 같네요. 해당 뷰는 edit_box.xml에 존재하는데 이 뷰를 바인딩하는 건 액티비티에서 아랫처럼 하셨기 때문에, textView_Date가 널인 상태인 걸로 보입니다.

textView_Date = (TextView)findViewById(R.id.textView_date);

buttonInsert의 onClickLisetener 안에서 처럼 Dialog의 레이아웃에서 findViewById를 하셔야 해당 텍스트뷰가 null 이 되지 않을 것 같네요.

안전한 코드를 위해서는 textView_Date는 Dialog 범위 안에서 접근하시는게 좋습니다. 현재는 액티비티 레벨에서 접근하기 때문에 Dialog가 보이지 않는 경우에 textView_Date는 null 이 됩니다. 변수는 적절한 범위에 두고 사용하시는 게 좋아요. 현재와 같은 구조는 우리집에만 있어야 할 물건을 부모님 집에 두고 가져와서 사용하고 돌려놓는 식의 방식이라 좋지 않습니다. textView_Date는 우리집(Dialog) 안에서만 필요하기 때문에 Dialog 안으로 관련 코드를 다 옮기세요.

먼저 아랫처럼 Dialog에 있는 버튼의 클릭이벤트를 제거하세요. 이렇게 하시면 어쩔 수 없이 Dialog 가 액티비티에 접근해야 하게 됩니다. OnClickHandler 는 Dialog 안으로 위치시키세요.

<Button
        android:id="@+id/button"
        ...
        android:onClick="OnClickHandler" // <- 요기 제거
        ... />

 

// private TextView textView_Date; ==> 제거

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        ...
 
         // textView_Date = (TextView)findViewById(R.id.textView_date); => 제거. 
 
        ...
 
        buttonInsert.setOnClickListener(new View.OnClickListener() {
            // 1. 화면 아래쪽에 있는 데이터 추가 버튼을 클릭하면
            @Override
            public void onClick(View v) {
                   showExpenseFormDialog(); // 더 적절한 메소드 이름을 사용하세요.
            }
        });
 
    }


private void showExpenseFormDialog() {
     AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
     View view = LayoutInflater.from(MainActivity.this)
                        .inflate(R.layout.edit_box, null, false);
     builder.setView(view);
 
     final Button buttonSubmit = view.findViewById(R.id.button_dialog_submit);
     final EditText editTextID = view.findViewById(R.id.edittext_dialog_id);
     final EditText editTextKorean = view.findViewById(R.id.edittext_dialog_korean);
     final EditText editTextEnglish = view.findViewById(R.id.edittext_dialog_english);
     final TextView textView_Date = view.findViewById(R.id.textView_date);  // => 추가

     ButtonSubmit.setText("삽입");

     //****** 추가
     TimePickerDialog.OnTimeSetListener callbackMethod = (view1, hourOfDay, minute) -> textView_Date.setText(hourOfDay + "시" + minute + "분");
     view.findViewById(R.id.button).setOnClickListener(button -> {
          TimePickerDialog timePickerDialog = new TimePickerDialog(this, callbackMethod, 8, 10, true);
          timePickerDialog.show();
     });
     //******
 
     final AlertDialog dialog = builder.create();
     buttonSubmit.setOnClickListener(new View.OnClickListener() {
          public void onClick(View v) {
                        // 4. 사용자가 입력한 내용을 가져와서
                        String strID = editTextID.getText().toString();
                        String strKorean = editTextKorean.getText().toString();
                        String strEnglish = editTextEnglish.getText().toString();
                        // 5. ArrayList에 추가하고
 
                        Dictionary dict = new Dictionary(strID, strEnglish ,strKorean );
                        mArrayList.add(0, dict); //첫번째 줄에 삽입됨
                        //mArrayList.add(dict); //마지막 줄에 삽입됨
 
                        // 6. 어댑터에서 RecyclerView에 반영하도록 합니다.
                        mAdapter.notifyItemInserted(0);
                        //mAdapter.notifyDataSetChanged();
 
                        dialog.dismiss();
                    }
          });
 
     dialog.show();
}

 

보시면 아시겠지만, 코드가 Dialog관련코드가 상당히 길어서 한군데에 다 몰아놓으면 코드를 읽기가 상당히 힘들어 집니다. 읽기 쉽게 작은 단위로 쪼개시거나 별도의 클래스로 만들어서 사용하시길 권장드려요. 작은 단위로 쪼개놓은 코드를 예로 들면,

private void showExpenseFormDialog() {
        final AlertDialog dialog = createExpenseDialog();
        dialog.show();

        final EditText editTextID = dialog.findViewById(R.id.edittext_dialog_id);
        final EditText editTextKorean = dialog.findViewById(R.id.edittext_dialog_korean);
        final EditText editTextEnglish = dialog.findViewById(R.id.edittext_dialog_english);
        final TextView textView_Date = dialog.findViewById(R.id.textView_date);

        dialog.findViewById(R.id.button).setOnClickListener(v -> {
            showTimePickerDialog(textView_Date);
        });

        final Button submitButton = dialog.findViewById(R.id.button_dialog_submit);
        submitButton.setText("삽입");
        submitButton.setOnClickListener(v -> {
                submitExpense(editTextID.getText().toString(), editTextKorean.getText().toString(), editTextEnglish.getText().toString());
                dialog.dismiss();
        });
    }

    private void showTimePickerDialog(TextView textViewToDisplay) {
        TimePickerDialog timePickerDialog = new TimePickerDialog(
                this,
                (view1, hourOfDay, minute) -> textViewToDisplay.setText(hourOfDay + "시" + minute + "분"),
                8,
                10,
                true);
        timePickerDialog.show();
    }

    // 화면은 비용입력 화면인데, 왜 사전에 단어 등록을 하고 계신지 이해가 가진 않지만, 일단 화면대로 Expense라는 이름을 붙였습니다.
    private void submitExpense(String id, String korean, String english) {
        Dictionary dict = new Dictionary(id, english, korean);
        mArrayList.add(0, dict); //첫번째 줄에 삽입됨
        mAdapter.notifyItemInserted(0);
    }

    private AlertDialog createExpenseDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        View view = LayoutInflater.from(MainActivity.this)
                .inflate(R.layout.edit_box, null, false);
        builder.setView(view);
        return builder.create();
    }

 

별도의 클래스로 두어서 Dialog 관련 코드를 옮기는 것이 더 좋긴 합니다만, 우선은 위처럼 간단하게 메소드를 기능별로 쪼개서 두시면 코드를 읽기가 더 편해지고 나중에 별도의 클래스로 옮길 때도 도움이 될 거라고 생각합니다.

spark (226,420 포인트) 님이 2021년 11월 21일 답변
spark님이 2021년 11월 21일 수정
...