ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 안드로이드 세번째 과제
    Android 2024. 3. 23. 22:15

    EditText에 대한 정리

    비밀번호는 입력내용이 가려져야 하는데.. 구글링이 잘 안나오네 구글 공식 문서도 참고하기가 조금 힘들다

    // 첫번째 시도
    <com.google.android.material.textfield.TextInputLayout
            //android:layout_width="wrap_content"
            //android:layout_height="wrap_content"
            app:endIconMode="password_toggle">
        <EditText
        ...
            />
    </com.google.android.material.textfield.TextInputLayout>

    EditText를 한번더 감싸서 password_toggle 을 사용하면 자동으로 password 를 숨기기가 가능하다는데..

    레이아웃 배치가 이상하게 돼서 실패 ㅠㅠ

     

    안드로이드에서는 InputMethodManager를 제공하는데 해당 클래스의 메소드를 활용하여, SoftInput(입력화면)을 숨기거나 가져올 수 있다.

     

    public boolean hideSoftInputFromWindow(IBinder windowToken, int flags)
    // 입력창을 숨기는 메소드
    
    public boolean showSoftInput(View view, int flags)
    // 입력창을 보여주는 메소드
    
    //메인 kt 코드에 추가
    fun softInputControl(view:View, wantToShow:Boolean) {
            val manager = view.context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
            if(manager!=null) {
                if(wantToShow)
                    manager.showSoftInput(view, 0)
                else
                    manager.hideSoftInputFromWindow(view.windowToken,0)
            }
        }

    뭔가 반응이 없다. 그리고 내용도 어려워서 활용이 어려워보인다.

     

     

    앱을 실행하면서 켜지지 않는 오류가 났는데,

    변수의 위치를 잘못 설정했다. 처음에 onCreate 밖에 변수를 선언했는데, 튜터님이 onCreate 안으로 변수를 가져오라고 하셨다. findViewById 는 다 onCreate 에 설정해야 하는 것 같다. 근데 다른 메소드에서 해당 변수를 참조하기 위해 선언 자체는 onCreate 밖에 해주는 것이다.

    class SignInActivity : AppCompatActivity() {
    
        private lateinit var et_id: EditText // 전역 변수로 설정 → onCreate 외의 메소드에서 해당 변수를 쓸 수 있도록!
    
        override fun onCreate(savedInstanceState: Bundle?) {
            et_id = findViewById(R.id.et_id) // xml 파일로부터 ID 가져오는건 onCreate 함수에 지정
        }
    
        fun btnOnClicked(view: View) { 
            val data_id = et_id.text.toString() // 전역 변수로 설정돼서 et_id 를 쓸 수 있다!
    }

     

    엇..? 다시 실행해서 비밀번호 보니까 가려져서 나오네.. 나 뭐한거지?

    xml 파일에서 EditText에 inputType 을 textPassword로 지정하면 *** 표시와 같이 패스워드 내용이 가려지게 된다.

    <EditText
        android:inputType="textPassword"/>

     

    또 오류가 났는데 이번엔 MainActivity가 아니라 넘겨받는 액티비티에서 문제가 났다.

    findViewByID 속성을 onCreate 밖에 넣어서 문제가 생겼다. 다시 안쪽으로 넣었다.

     

    드디어 lv1 완성 !!!!!

    ㅠㅠ 젤 오류많이 뜬게 class 내 변수의 위치였다.. findViewById는 무조건 onCreate 안에 작성하자!

    그리고 다음에는 질문하기 전에 Logcat을 먼저 해석해보자!

     

    오류 발생: 세개의 데이터를 다 입력했음에도 불구하고 원래 액티비티로 넘어오지 않고 토스트 메세지가 계속 뜬다. 어떻게 해결하지?

     

    또 변수 위치를 잘못 넣어서 오류가 생겼다. 버튼을 누르고 나서야 데이터 값을 가져와야한다. 누르기 전에는 언제 가져올지 모른다(?)..

    튜터님께서 값이 들어오는 시점을 잘 확인해야 하고, 생명주기 확인, onCreate 호출되는 시점을 확인해보라고 말씀하셨다

     

    // 최종 수정 코드
    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_sign_up)
    
            val btnBack = findViewById<Button>(R.id.btn_back)
            val et_name = findViewById<EditText>(R.id.et_name)
            val et_id2 = findViewById<EditText>(R.id.et_id2)
            val et_password2 = findViewById<EditText>(R.id.et_password2)
    
            btnBack.setOnClickListener { 
                val name = et_name.text.toString() // 위치 전에 잘못 넣은 코드
                val id = et_id2.text.toString() // 위치 전에 잘못 넣은 코드
                val password = et_password2.text.toString() // 위치 전에 잘못 넣은 코드
    
                if (name.isNotBlank()&& id.isNotBlank() && password.isNotBlank()) {
                    finish()
                } else {
                    Toast.makeText(this,"입력되지 않은 정보가 있습니다!",Toast.LENGTH_SHORT).show()
                    Log.d("실행", "${name},${id},${password}")
                }
            }

    추가로 name == "" 또는 name.equals("") 도 있지만, name.isNotBlack 로 쓰는 것도 있다!

     

     

     

    null check 를 하지 않으면 0이나 null 등 잘못된 값을 꺼내올 수 있으므로, 해당 key가 전달할 값을 가지고 있는지 hasExtra로 체크하는 것이 좋다. (데이터를 받아오는 액티비티 입장에서)

    // 인텐트 값을 받아오는 액티비티
    
    if(intent.Extra("key")) { // "key" 라는 내용의 키에 저장된 값이 있다면
    	textView.text=intent.getStringExtra("key") //textView의 내용을 해당 키값의 데이터로 바꾼다.
    }

     

    19:00~20:30 선택구현1 완료

    내 생각대로 구현해봤는데 일단 제대로 작동하지 않았다(튜터님께 여쭤봐야겠다)

    일단, 기존 finish()를 빼고 새로운 인텐트를 생성해서 putExtra를 두번써서 데이터를 넣는다.

     

    기존 회원가입 액티비티에서 메인 액티비티로 넘어오면, 생명주기랑 메인 액티비티가 reStart() 가 된다.

    그래서 해당 함수에서 인텐트 값을 받아오고, EditText 에 setText 함수를 이용해서 설정하게 하였다. (그럼 화면에서 보여야 하는데.. 결론적으로 안보인다 ㅠ)

    // 회원가입 액티비티
    if (name.isNotBlank()&& id.isNotBlank() && password.isNotBlank()) {
            val intent = Intent(this,SignInActivity::class.java)
            intent.putExtra("id",id) // 하나의 intent 에 여러 개의 데이터를 putExtra 할 수 있다.
            intent.putExtra("password",password)
            startActivity(intent)
    }
    
    
    // 로그인 화면 액티비티
    override fun onRestart() {
            super.onRestart()
            val id = intent.getStringExtra("id")
            val password = intent.getStringExtra("password")
    //        et_id.text=id // 오류
            et_id.setText(id)
            et_password.setText(password)
    }

     

     

    registerForActvitiyResult 

     

    registerForActivityResult() API를 통해서 결과 콜백을 등록할 수 있다.

    registerForActivityResult()는 ActivityResultContract와 ActivityResultCallback을 가져와서 다른 activity를 실행하는데 사용할 ActivityResultLauncher를 반환한다.

    registerForActivityResult 함수는 데이터를 받는쪽에서 선언해야한다!

    SignInActivity.kt
    
    private lateinit var resultLauncher: ActivityResultLauncher<Intent>
    
    // registerForActivityResult 메소드를 활용해서 ActivityLauncher(resultLauncher) 을 생성한다.
    // 파라미터로는 ActivityResultContract, ActivityResultCallback이 필요하다 
    // ActivityResultContract는 StartActivityForResult 를 비롯한 다양한 계약 객체가 있다.
    // ActivityResultCallback 람다로는 ActivityResult 객체(result)가 파라미터로 생성되고
    // 안쪽 코드에 intent와 result Code 를 이용해 원하는 데이터를 가져온다.
    
    private fun setResultSignUp() {
    	resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> 
        	if(result.resultCode == Activity.RESULT_OK) {
            	val id = result.data?.getStringExtra("id") ?: ""
                val password = result.data?.getStringExtra("password") ?: ""
               	etId.setText(id) // setText 함수를 이용한다.
                etPassword.setText(password)
            }
        }
    }
    
    private fun clickSignUp() {
    	binding.btnSignUp.setOnClickListener() {
        	val intent = Intent(this, SingUpActivity::class.java)
            resultLauncher.launch(intent) //startActivity(intent) 형식을 쓰지 않음
            // 아까 만든 resultLauncher 에 launch 함수를 사용하여 데이터를 받아올 액티비티 실행
            // 너한테서 데이터 받아올거야! (이런 의미)
    	}
    }
    
    
    
    SignUpActivity.kt
    // 아이디, 비밀번호 데이터를 SignInActivity.kt 에 넘겨줄 것임
    
    private fun clickSignUp() { // ★ 함수 선언
    	
        btnSignUp.setOnClickListener {
            if(etId.length()==0 || etName.length==0 || etPassword.length==0) { // ☆
                Toast.makeText(this@SignUpActivity, "입력하지 않은 정보가 있습니다.", Toast.LENGTH_SHORT).show()
            } else {
                // 데이터를 넘겨줌
                val intent = Intent(this@SignUpActivity, SignInActivity::class.java)
                intent.putExtra("id",etId.text.toString())
                intent.putExtra("password",etPassword.text.toString())
                setResult(RESULT_OK,intent) // setResult 함수를 통해 resultCode 와 인텐트 받아옴
                finish() // startActivity(intent) 형식이 아니다!
            }
        }
        
    }

     

    // 기존의 내 코드
     btnBack.setOnClickListener {
                val name = et_name.text.toString()
                val id = et_id2.text.toString()
                val password = et_password2.text.toString()
    
                if (name.isNotBlank()&& id.isNotBlank() && password.isNotBlank()) {
                    val intent = Intent(this,SignInActivity::class.java)
                    intent.putExtra("id",id) // 하나의 intent 에 여러 개의 데이터를 putExtra 할 수 있다.
                    intent.putExtra("password",password)
                    startActivity(intent)
    //                finish()
                } else {
                    Toast.makeText(this,"입력되지 않은 정보가 있습니다!",Toast.LENGTH_SHORT).show()
                }
            }

     

    20:30~21:00

     

    선택2 

    자기소개 페이지가 시작될 때 5장 중 랜덤으로 1장의 사진이 표시되도록 구현합니다.

    // 첫번째 방법
    private val imageArray = arrayOf (
    	R.drawable.image1,
        R.drawable.image2,
        R.drawable.image3,
        R.drawable.image4,
        R.drawable.image5
    )
    
    setRandomImage(imageView)
    
    private fun setRandomImage(imageView : ImageView) {
    	val randomIndex = (imageArray.indices).random() // 0~4 중 랜덤한 인덱스를 꺼내온다.
        val randomImageResourceId = imageArray[randomIndex]
        imageView.setImageResource(randomImageResourceId)
    }
    
    // 두번째 방법 
    when(Random().nextInt(5)) {
    	0 -> {imageView.setImageResource(R.drawalbe.image1)}
        1 -> {..}
        2 -> {..}
        3 -> {..}
        4 -> {..}
    }

    두번째 방법이 더 간결해보이지만, 정적으로만 값을 받아올 수 있다는 점에서 좀 아쉽다. 동적으로

     

    <androidx.constraintlayout..>
        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="none" // 맨 오른쪽에 스크롤바 없애기 (굳이 안써도 됨)
            android:fillViewport="true"> // ScrollView 위젯 사용시 꼭 써줘야 하는 부분
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"> // ScrollView는 단 하나의 자식만 가질 수 있어서 위젯들을 layout으로 묶어줘야함, 스크롤은 수직으로 내려서 vertical 로 설정
                <ImageView
                   />
                <TextView
                    />
                <Button
                    />
            </LinearLayout>
        </ScrollView>
    </androidx.constraintlayout.widget.ConstraintLayout>

    LinearLayout 말고 ConstraintLayout을 써도 된다!

     

    계속 layout 위치가 이상하게 잡혀서 튜터님께 여쭤봤는데 xml 파일에서 위젯에 그냥 margin 속성을 주면 안된다고 하셨다.

    xml 레이아웃이 실제 앱을 실행했을때랑 계속 다르게 나온다 ㅠㅠ 왜그러지..

    (xml 상 코드를 위젯의 순서와 일치시켜야 한다)

    'Android' 카테고리의 다른 글

    Layout, Margin, Padding, Gravity  (0) 2024.03.27
    2번째 키오스크 과제  (0) 2024.03.24
    앱 아키텍처 - 챌린지 과제  (0) 2024.03.22
    MVC, MVP, MVVM, MVI  (0) 2024.03.19
    확장함수  (0) 2024.03.18
Designed by Tistory.