본문 바로가기

sopt 세미나 정리/과제

3주차 과제중 배운것들

라디오 버튼 커스텀

selector의 state_checked 속성을 이용하기위해 그냥 버튼이아닌 라디오 버튼을 이용해서 버튼을 만들어줬다.

 

 

-  android:button="@null


라디오 버튼에 동그라미 버튼 부분 없애려면 속성에 이렇게 넣어주면된다.


fragment안의 fragment 처리

 

fragment안에서 fragment 처리할때는 activity에서 처리할때와 다르게 supportFragmentManager 를 사용하는것이 아닌

 

childFragmentManager를 사용해야한다.

 

또한 프래그먼트에서 부모의 프래그먼트 매니저를 접근하려면 ex)fragment1에서 activity의 fragment로 접근 

 

이럴경우에는 parentFragmentManager를 사용한다.

 

https://ddangeun.tistory.com/127

 

[안드로이드] Fragment 안의 Fragment 처리(교체, 백스택, Back 버튼 클릭 등) ― parentFragmentManager vs childF

Fragment안에 Fragment를 추가하여 더 관리할 경우가 생기는데요, 예를들어 ViewPager나 TabLayout의 Fragmet중 하나에서 Fragment를 여러개 교체하거나 관리할 수 있습니다. MainActivity에 Fragment1과 Fragment..

ddangeun.tistory.com

자세한 설명은 이 블로그를 참고하자

 


glide 추가 사항

 

dependency를 추가

implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

 

옵션없는 이미지로드

/* Activity에서 사용할 경우 */

Glide.with(this)
    .load(R.drawable.img_file_name)
    .into(imageView)
/* ViewHolder에서 사용할 경우 */

Glide.with(itemView)
    .load(R.drawable.img_file_name)
    .into(itemView.imageView)

뷰홀더에서는 itemview 을 binding.root로 대체하면된다.

 

level 2-2 처럼 그냥 이미지 url넣어주면 알아서 표시됨glide

 

각함수 설명

  • with() : View, Fragment 혹은 Activity로부터 Context를 가져온다.
  • load() : 이미지를 로드한다. 다양한 방법으로 이미지를 불러올 수 있다. (Bitmap, Drawable, String, Uri, File, ResourId(Int), ByteArray)
  • into() : 이미지를 보여줄 View를 지정한다.
  • placeholder() : Glide 로 이미지 로딩을 시작하기 전에 보여줄 이미지를 설정한다.
  • error() : 리소스를 불러오다가 에러가 발생했을 때 보여줄 이미지를 설정한다.
  • fallback() : load할 url이 null인 경우 등 비어있을 때 보여줄 이미지를 설정한다.
  • skipMemoryCache() : 메모리에 캐싱하지 않으려면 true로 준다.
  • diskCacheStrategy() : 디스크에 캐싱하지 않으려면 DiskCacheStrategy.NONE로 준다. 다음과 같은 옵션이 있다. (ALL, AUTOMATIC, DATA, NONE, RESOURCE)

gif로딩기능 있는데 이건 로티쓰는게 더 좋은거아님?

 

 

https://blog.yena.io/studynote/2020/06/10/Android-Glide.html

 

[Android] Glide 로 이미지 Load하기

Glide Glide, 글라이드는 안드로이드에서 이미지를 빠르고 효율적으로 불러올 수 있게 도와주는 라이브러리이다. 사용 방법도 간단하고 확장성도 넓어서 이미 메이저하게 사용되고 있는 라이브러

blog.yena.io

기타 참고사항은 이블로그를 참고하자


ViewPager2 중첩스크롤 문제 해결하기

구글 공식문서에 나와있는 방법대로했다

 

https://developer.android.com/training/animation/vp2-migration?hl=ko 

 

ViewPager에서 ViewPager2로 이전  |  Android 개발자  |  Android Developers

ViewPager에서 ViewPager2로 이전 ViewPager2는 ViewPager 라이브러리의 개선된 버전으로, 향상된 기능을 제공하며 ViewPager 사용 시 발생하는 일반적인 문제를 해결합니다. 앱에서 ViewPager를 이미 사용하고

developer.android.com

구체적인 방법은 

 

1.NestedScrollableHost.kt 파일추가

 

https://github.com/android/views-widgets-samples/blob/master/ViewPager2/app/src/main/java/androidx/viewpager2/integration/testapp/NestedScrollableHost.kt

 

GitHub - android/views-widgets-samples: Multiple samples showing the best practices in views-widgets on Android.

Multiple samples showing the best practices in views-widgets on Android. - GitHub - android/views-widgets-samples: Multiple samples showing the best practices in views-widgets on Android.

github.com

링크의 내용을 긁어서 NestedScrollableHost.kt 를 추가시켜준다.

 

2.xml 에서 중첩되는 즉 내부의 스크롤뷰(viewpager2) 에 NestedScrollableHost 씌워주기

    <androidx.coordinatorlayout.widget.CoordinatorLayout>

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appBarLayout">

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar">

                <com.google.android.material.tabs.TabLayout
                    android:id="@+id/chipsLayout" />

            </androidx.appcompat.widget.Toolbar>

        </com.google.android.material.appbar.AppBarLayout>

        <com.nasrabadiam.widget.widget.NestedScrollableHost
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <androidx.viewpager2.widget.ViewPager2
                android:id="@+id/chipsViewPager"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

        </com.nasrabadiam.widget.NestedScrollableHost>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

이런식으로 

내부에 viewpager2를 NestedScrollableHost로 감싸준다.

 

이러면 끝!!!!

 

참고블로그:

https://medium.com/@nasrabadiam/support-nested-scrollable-elements-inside-viewpager2-59fa34978899

 

Support nested scrollable elements inside ViewPager2

ViewPager2 does not natively support nested scroll views in cases where the scroll view has the same orientation as the ViewPager2 object…

medium.com


dataBinding을 리사이클러 뷰에 적용하기

 

리펙토링을 해보자

 

1. 당연히 gradle추가해주고

android {
    ...
    dataBinding {
        enabled = true
    }
}

 

2.리사이클러의 아이템뷰 의 xml을 <layout>으로 감싼다.

 

 

아이템을 감쌌다.

 

3.data variable 추가 

layout안에 추가해준다

<data>

        <variable
            name="profileRecycler"
            type="changhwan.experiment.sopthomework.FollowerData" />
    </data>

 

 

4.view와 data를 @{}로 bind해준다.

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>

        <variable
            name="profileRecycler"
            type="changhwan.experiment.sopthomework.FollowerData" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/followerLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="49dp"
            android:layout_height="0dp"
            android:layout_marginLeft="21dp"
            android:layout_marginTop="24dp"
            android:layout_marginBottom="24dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintDimensionRatio="1:1"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:recyclerGlide="@{profileRecycler.followerImg}" />

        <TextView
            android:id="@+id/followerName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="22dp"
            android:layout_marginTop="25dp"
            android:fontFamily="@font/noto_sans_kr"
            android:includeFontPadding="false"
            android:text="@{profileRecycler.followerName}"
            android:textFontWeight="700"
            android:textSize="16sp"
            android:textStyle="normal"
            app:layout_constraintStart_toEndOf="@+id/imageView"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="이름" />

        <TextView
            android:id="@+id/followerIntro"
            android:layout_width="150dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:layout_marginBottom="24sp"
            android:ellipsize="end"
            android:fontFamily="@font/noto_sans_kr"
            android:includeFontPadding="false"
            android:maxLines="1"
            android:text="@{profileRecycler.followerIntro}"
            android:textFontWeight="400"
            android:textSize="14sp"
            android:textStyle="normal"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="@+id/followerName"
            app:layout_constraintTop_toBottomOf="@+id/followerName"
            tools:text="자기소개" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

5.viewholder 클래스에서 binding객체에 데이터 담아준다.

 

onbind 함수에서 일일히 다 넣었던거 그냥 itemview에서 설정해놓은 데이터 변수에 매번 들어오는 data를 넣어주는거로 바꿔준다.

 

 inner class FollowerViewHolder(
        private val binding: FollowerItemBinding,
        listener: ItemDragListener
    ) : RecyclerView.ViewHolder(binding.root) {
        fun onBind(data: FollowerData) {
            binding.profileRecycler = data
            // binding.executePendingBindings() -> 없어도 된단다 이거 바인딩할때 작업들 당장당장 수행하라고 강요하는 함수. 
        }
        //중략
 }

 

6.이미지 같은거 처리를위해 bindingadpter 만들어주기

package changhwan.experiment.sopthomework


import android.graphics.drawable.Drawable
import android.widget.ImageView
import androidx.databinding.BindingAdapter
import androidx.lifecycle.MutableLiveData
import com.bumptech.glide.Glide


object BindingAdapters {

    @JvmStatic
    @BindingAdapter("recyclerGlide")
    fun setImage (imageview : ImageView, url : MutableLiveData<String>){
        Glide.with(imageview.context)
            .load(url.value)
            .circleCrop()
            .into(imageview)
    }
}

기존에 bindingadapter 공부했던것처럼 처리 불가능한거 만들어준다. -> 이미지처리 이제 glide 추가했기에 그걸로 처리했다.

 

그래서 이미지뷰에서 이미지 넣는부분이 이렇다

app:recyclerGlide="@{profileRecycler.followerImg}"

 

결론적으로 layout 감싸주고 데이터 만들어주는곳은 item이고

viewholder에서 데이터 집어넣어주는형태이다

 

참고블로그:

https://salix97.tistory.com/244

 

[Android] 안드로이드 - 리사이클러뷰 Data Binding 으로 리팩토링하기

1. app/build.gradle 에 dataBinding 요소를 추가한다 android { ... dataBinding { enabled = true } } 2. 리사이클러뷰 아이템 뷰 xml 레이아웃을 태그로 감싼다 리사이클러뷰 아이템 뷰 xml 레이아웃의 루트태..

salix97.tistory.com

 


갤러리에서 이미지 받아오기

옛날에 프로젝트 할때는 라이브러리 이용해서 크롭기능과 앨범접근까지해서 권한설정까지 다해서 편했는데 그냥간단하게 하려고 라이브러리 이용 안하려니 오히려 더복잡하다. 어쨋든 이미지 끌고오는거 자체는 간편하니 갤러리로 intent를 통해접근해서 1장 uri로 받아오는 것을 다뤄보려한다.

 

 

package changhwan.experiment.sopthomework

import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.database.DatabaseUtils
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.MutableLiveData
import changhwan.experiment.sopthomework.databinding.FragmentCameraBinding


class CameraFragment : Fragment() {

    private var _binding : FragmentCameraBinding? = null
    private val binding get() = _binding!!
    private lateinit var getContent: ActivityResultLauncher<Intent>
    private lateinit var fContext : Context

    override fun onAttach(context: Context) {
        super.onAttach(context)
        fContext = context
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = DataBindingUtil.inflate(inflater,R.layout.fragment_camera, container, false)




        binding.lifecycleOwner = this

        initPicUri()
        initIntent()

        return binding.root
    }

    private fun initPicUri(){


        getContent = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            val cameraData = CameraData(picUri = MutableLiveData<Uri>().apply { value = it.data?.data })
            binding.camera = cameraData
        }
    }

    private fun initIntent(){
        val intent = Intent(Intent.ACTION_PICK).apply {
            type = MediaStore.Images.Media.CONTENT_TYPE
            type = "image/*"
        }




        binding.cameraButton.setOnClickListener{
            var permission = ContextCompat.checkSelfPermission(fContext, Manifest.permission.READ_EXTERNAL_STORAGE)
            if(permission == PackageManager.PERMISSION_DENIED) {
                ActivityCompat.requestPermissions(requireActivity(),arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_CODE)
            } else {
                getContent.launch(intent)
            }
        }
    }


    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    companion object{
        const val REQUEST_CODE = 1
    }
}

 우선 전체 코드는 이렇고 이미지는 uri형태로 받아와서 변수에 넣고 그변수를 그냥 바로 databinding을 통해서 넣어줬다.

 

쪼개서 보자면

 private lateinit var getContent: ActivityResultLauncher<Intent>

1. getContent라는 변수 생성

 

나중에 이렇게 초기화해준다

 

private fun initPicUri(){


        getContent = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            val cameraData = CameraData(picUri = MutableLiveData<Uri>().apply { value = it.data?.data })
            binding.camera = cameraData
        }
    }

 

원래 구글에서는 startActivityForResult가 아닌  기본으로 제공해주는 Contrat 함수인 GetContent()를 쓰라 하지만 이걸 이용하면 불변한 UI IMAGE PICKER 가  뜨게된다 그래서 그냥 ActivityResultContracts.StartActivityForResult() 를 이용해서 앨범에 접근하였다.

 

2.인텐트 변수 만들기

private fun initIntent(){
        val intent = Intent(Intent.ACTION_PICK).apply {
            type = MediaStore.Images.Media.CONTENT_TYPE
            type = "image/*"
        }

 

이렇게 intent를 설정해준다.

 

3.button에 눌리면 launch되도록 설정

 binding.cameraButton.setOnClickListener{
            var permission = ContextCompat.checkSelfPermission(fContext, Manifest.permission.READ_EXTERNAL_STORAGE)
            if(permission == PackageManager.PERMISSION_DENIED) {
                ActivityCompat.requestPermissions(requireActivity(),arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_CODE)
            } else {
                getContent.launch(intent)
            }
        }

 

getContent.Launch(intent)를 통해서 앨범으로 넘어간다.

 

나머지 코드는 권한 관련코드이다 권한에 대한것은 밑에 알아볼것이다.

 

참고블로그:

https://youngest-programming.tistory.com/517

 

[안드로이드] startActivityForResult() onActivityResult() deprecated 해결 방법 !! (갤러리에서 사진불러오기로

최근 안드로이드 스튜디오도 업데이트하고 새로운 프로젝트를 하나 생성해서 개발을 하고 있습니다. 그러던 중 갤러리에서 사진을 불러오기 위해 평소대로 위와 같이 startActivityForResult(), onActivi

youngest-programming.tistory.com

 


갤러리 접근 권한 설정

참고 액티비티와 프래그먼트에서 권한 받아오는게 은근 많이 다르다 이걸 좀 인지하고 가자 프래그먼트에서는 고려해야할것들이있다 -> 컨텍스트, 액티비티

 

갤러리 접근하려면 권한을 얻어야한다.

우선manifest에 storage 읽기쓰기 권한을 추가한다.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

 

일단 자세하게 해야할것 모두 나와있는 블로그글과 간단하게 정리된 블로그글 하나씩 링크를 남겨놓는다.

 

 

복잡:

https://manorgass.tistory.com/74

 

Android :: 권한(Permission) 파헤치기 / 개념과 활용방법 / 실시간 권한얻기

안녕하세요 간만에 포스팅입니다. 오늘은 현재 내 위치를 받아오는 예제 앱을 통해 Permission에 대해 알아보겠습니다. #1> Android Permission 누구니 너? 2007년 스티브잡스가 아이폰을 발표한지 어느덧

manorgass.tistory.com

 

간단:https://superwony.tistory.com/101

 

[안드로이드/코틀린] 카메라와 갤러리에서 이미지 가져오기

해당 포스팅은 제 블로그 조회수에 상당수를 기록했습니다. 그만큼 앱을 개발하는데 있어 이미지는 필수사항이라고 해도 과언이 아닙니다. 많이 부족함에도 불구하고 찾아주셔서 감사합니다.

superwony.tistory.com

 

 

 

적용은 간단한걸로 했다.

 

코드를 봐보자

 binding.cameraButton.setOnClickListener{
            var permission = ContextCompat.checkSelfPermission(fContext, Manifest.permission.READ_EXTERNAL_STORAGE)
            if(permission == PackageManager.PERMISSION_DENIED) {
                ActivityCompat.requestPermissions(requireActivity(),arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_CODE)
            } else {
                getContent.launch(intent)
            }
        }
    }

 

 ContextCompat.checkSelfPermission(fContext, Manifest.permission.READ_EXTERNAL_STORAGE)

 

이부분은 읽기 권한을 사용자에게 받았는지 안받았는지 가져오는 부분인데 첫번째 인자로 context를 넣어줘야한다.

근데 fragment는 context가 없기에 부모 액티비티의 context를 가져와야하는데 2가지방법이있다


 

 

-부제 fragment에서 context 가져오기 

1.requireContext() 함수이용 이함수를 쓰면 getcontext 와는 다르게 notnull한 context를 반환한다.

 

2.onAttach 함수 오버라이딩해서 context 받아오기

onAttach의 인자로 부모의 context가 들어오기에 거기서 전역변수에 담아서 사용해도된다.

 


어쨋든 이렇게 context 가져와서 첫번째 인자에넣고 두번쨰 인자에 무슨 권한인지 넣어주면 권한을 받은지 안받은지 여부를 알려준다.

 

그거를 조건문으로 권한 안받았으면 받는 코드를 

이미 받아져있다면 바로 실행해주는 코드를 짜고

 

권한 받아오는 부분을 봐보자

 ActivityCompat.requestPermissions(requireActivity(),arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_CODE)

이렇게

ActivityCompat.requestPermissions 를 사용하는데 첫번째 인자가 activity를 요구한다.

그래서 프래그 먼트에서 사용하는것을 검색해보니 그냥 requestPermissions를 이용하는 방법이나오는데 실행은 되지만 이건 deprecated 되었기에

 

프래그먼트의 액티비티를 가져올수있는 방법을 봐보자


 

-부제 fragment에서 부모 activity가져오기

 

requireActivity() 함수를 이용한다면 부모 Activity를 가져온다.

getActivity와 다른점은 notNull한 Activity를 반환한다.

 


그래서 이렇게 ActivityCompat.requestPermissions 를 이용해서 권한을 받아온다.

 

 


출처

https://blog.yena.io/studynote/2020/06/10/Android-Glide.html

 

[Android] Glide 로 이미지 Load하기

Glide Glide, 글라이드는 안드로이드에서 이미지를 빠르고 효율적으로 불러올 수 있게 도와주는 라이브러리이다. 사용 방법도 간단하고 확장성도 넓어서 이미 메이저하게 사용되고 있는 라이브러

blog.yena.io

https://medium.com/@nasrabadiam/support-nested-scrollable-elements-inside-viewpager2-59fa34978899

 

Support nested scrollable elements inside ViewPager2

ViewPager2 does not natively support nested scroll views in cases where the scroll view has the same orientation as the ViewPager2 object…

medium.com

https://salix97.tistory.com/244

 

[Android] 안드로이드 - 리사이클러뷰 Data Binding 으로 리팩토링하기

1. app/build.gradle 에 dataBinding 요소를 추가한다 android { ... dataBinding { enabled = true } } 2. 리사이클러뷰 아이템 뷰 xml 레이아웃을 태그로 감싼다 리사이클러뷰 아이템 뷰 xml 레이아웃의 루트태..

salix97.tistory.com

https://youngest-programming.tistory.com/517

 

[안드로이드] startActivityForResult() onActivityResult() deprecated 해결 방법 !! (갤러리에서 사진불러오기로

최근 안드로이드 스튜디오도 업데이트하고 새로운 프로젝트를 하나 생성해서 개발을 하고 있습니다. 그러던 중 갤러리에서 사진을 불러오기 위해 평소대로 위와 같이 startActivityForResult(), onActivi

youngest-programming.tistory.com

 

'sopt 세미나 정리 > 과제' 카테고리의 다른 글

4주차 과제중 배운것들  (0) 2021.11.12
2주차 과제중 배운것  (0) 2021.10.22
1주차 과제중 배운것들  (0) 2021.10.10