-
private fun loginEvent() { binding.btnLogin.setOnClickListener { if (!binding.etId.text.isNullOrBlank() && !binding.etPw.text.isNullOrBlank()) { showToast("로그인 성공") goHome() } else showToast("아이디/비밀번호를 확인해주세요") } }
-
private fun signupEvent() { with(binding) { btnComplete.setOnClickListener { if (!binding.etId.text.isNullOrBlank() && !binding.etName.text.isNullOrBlank() && !binding.etPw.text.isNullOrBlank()) { val intent = Intent(this@SignUpActivity, SignInActivity::class.java) intent.putExtra("id", etId.text.toString()) intent.putExtra("password", etPw.text.toString()) setResult(RESULT_OK, intent) finish() } else { showToast("입력하지 않은 정보가 있습니다.") } } } }
-
private fun setSignUp() { resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode == Activity.RESULT_OK) { val id = result.data?.getStringExtra("id") ?: "" val password = result.data?.getStringExtra("password") ?: "" binding.etId.setText(id) binding.etPw.setText(password) } } } private fun signupEvent() { binding.btnSignup.setOnClickListener { val intent = Intent(this, SignUpActivity::class.java) resultLauncher.launch(intent) } }
-
<data> <variable name="user" type="org.sopt.seminar.User" /> </data> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".HomeActivity"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/img_profile" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginHorizontal="120dp" android:layout_marginTop="50dp" android:src="@drawable/profile" app:layout_constraintDimensionRatio="1:1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView . . android:text="@{user.age}" . ./>
-
[DataBinding] -데이터 바인딩의 주목적은 UI 레이아웃의 뷰를 앱 코드에 저장된 데이터와 연결하는 간단한 방법을 제공 -데이터와 뷰를 연결하는 작업을 레이아웃에서 -버튼과 같은 UI 컨트롤을 UI컨트롤러 또는 ViewModel 인스턴스와 같은 다른 객체의 이벤트나 리스너 함수에 연결 시키는 편리한 방법도 존재 -특히 LiveData 컴포넌트와 같이 사용 될 때 이점이 배가 됨 -xml단에서 <layout> 태그를 사용하여 만든 것만 처리 [ViewBinding] -findViewById()사용보다 Null 안전에 있어 아래와 같은 장점이 존재 -뷰 결합은 뷰의 직접 참조를 생성하므로 유효하지 않은 뷰 ID로 인해 null 포인터 예외가 발생할 위험이 없음 -레이아웃의 일부 구성에만 뷰가 있는 경우 결합 클래스에서 참조를 포함하는 필드가 @Nullable로 표시 -뷰바인딩은 데이터바인딩에 비해 주석처리가 필요하지 않으므로 더 빠른 컴파일 속도를 가짐 -하지만 뷰바인딩은 레이아웃 변수 또는 레이아웃 표현식을 지원하지 않으므로 XML 레이아웃 파일에서 직접 동적 UI 콘텐츠를 선언하는 데 사용 불가능 -양방향 데이터 결합 지원 불가
로그인 | 회원가입 |
---|---|
-
supportFragmentManager.beginTransaction().add(R.id.fragment_home, followerFragment).commit() binding.btnFollower.setOnClickListener { val transaction = supportFragmentManager.beginTransaction() if (position == REPO_POSITION) { transaction.replace(R.id.fragment_home, followerFragment).commit() position = FOLLOWER_POSITION } } binding.btnRepo.setOnClickListener { val transaction = supportFragmentManager.beginTransaction() if (position == FOLLOWER_POSITION) { transaction.replace(R.id.fragment_home, repoFragment).commit() position = REPO_POSITION } }
-
<TextView ... android:ellipsize="end" android:ems="8" android:maxLines="1" android:text="@{repo.repo}" android:textColor="@color/black" android:textSize="17sp" android:textStyle="bold" ... /> <TextView ... android:ellipsize="end" android:ems="7" android:maxLines="1" android:text="@{repo.introduction}" android:textColor="@color/black" android:textSize="15sp" ... />
-
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
-
private fun itemClickEvent() { followerAdapter.setItemClickListener(object : FollowerAdapter.OnItemClickListener { override fun onClick(view: View, position: Int) { val name = followerAdapter.currentList[position].name val introduce = followerAdapter.currentList[position].introduction val intent = Intent(context, DetailActivity::class.java) .putExtra("name", name) .putExtra("introduction", introduce) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) startActivity(intent) } }) }
-
private fun recyclerViewDecoration() { with(binding) { rvFollower.addItemDecoration(VerticalItemDecorator(10)) rvFollower.addItemDecoration(HorizontalItemDecorator(10)) } }
-
val callback = MyTouchHelperCallback(followerAdapter) val touchHelper = ItemTouchHelper(callback) touchHelper.attachToRecyclerView(binding.rvFollower) binding.rvFollower.adapter = followerAdapter followerAdapter.startDrag(object : FollowerAdapter.OnStartDragListener { override fun onStartDrag(viewHolder: FollowerAdapter.FollowerViewHolder) { touchHelper.startDrag(viewHolder) } })
-
abstract class BaseFragment<B : ViewDataBinding>(@LayoutRes private val layoutRes: Int) : Fragment() { private var _binding: B? = null val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = DataBindingUtil.inflate(inflater, layoutRes, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.lifecycleOwner = this initAdapter() } abstract fun initAdapter() override fun onDestroyView() { _binding = null super.onDestroyView() } }
-
class RepoViewHolder( private val binding: ItemRepoListBinding ) : RecyclerView.ViewHolder(binding.root) { fun onBind(repoData: RepoData) { binding.repo = repoData } } companion object { val DIFFUTIL = object : DiffUtil.ItemCallback<RepoData>() { override fun areItemsTheSame( oldItem: RepoData, newItem: RepoData ): Boolean { return oldItem.repo == newItem.repo } override fun areContentsTheSame( oldItem: RepoData, newItem: RepoData ): Boolean { return oldItem == newItem } } }
-
변경된 데이터의 position을 인자로 넘겨주어 해당 데이터만 변경하는 notifyItemChanged가 있지만 역시나 position을 찾아 넘겨주며 하나하나 값을 변경하는 번거로운 일이 발생한다.
-
-
ListAdapter는 DiffUtil을 활용하여 리스트를 업데이트할 수 있는 기능을 추가한 Adapter, 기존 어댑터와 비교해서 추가로 DiffUtil 기능에 대한 콜백 기능 클래스만 구현하면 되므로 생산성, 효율성을 높일 수 있다.
-
https://velog.io/@l2hyunwoo/Android-RecyclerView-DiffUtil-ListAdapter
-
https://velog.io/@deepblue/RecyclerView%EC%9D%98-notifyDataSetChanged
-
DetailActivity | 아이템 이동 |
---|---|
-
private fun initTransactionEvent() { val followerFragment = FollowerFragment() val repositoryFragment = RepoFragment() childFragmentManager.beginTransaction().add(R.id.fragment_profile, followerFragment) .commit() binding.btnFollower.isSelected = true //처음 화면 보여질 시에 binding.btnFollower.setTextColor(Color.BLACK) binding.btnFollower.setOnClickListener { childFragmentManager.beginTransaction() .replace(R.id.fragment_profile, followerFragment) .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) .commit() binding.btnFollower.isSelected = true; binding.btnRepo.isSelected = false; binding.btnRepo.setTextColor(Color.GRAY) binding.btnFollower.setTextColor(Color.BLACK) } binding.btnRepo.setOnClickListener { childFragmentManager.beginTransaction() .replace(R.id.fragment_profile, repositoryFragment) .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) .commit() binding.btnRepo.isSelected = true binding.btnFollower.isSelected = false binding.btnFollower.setTextColor(Color.GRAY) binding.btnRepo.setTextColor(Color.BLACK) } }
-
private fun initTabLayout() { val tabLable = listOf("팔로잉", "팔로워") TabLayoutMediator(binding.homeTablayout, binding.vpHome) { tab, position -> tab.text = tabLable[position] }.attach() }
<com.google.android.material.tabs.TabLayout android:id="@+id/home_tablayout" ... app:tabIndicatorColor="@color/sopt_main_purple" app:tabMode="fixed" app:tabSelectedTextColor="@color/sopt_main_purple" />
-
class ViewPagerAdapter(fragment: FragmentActivity) : FragmentStateAdapter(fragment) { override fun getItemCount() = 3 override fun createFragment(position: Int): Fragment { return when (position) { 0 -> ProfileFragment() 1 -> HomeFragment() else -> CameraFragment() } } }
-
<org.sopt.seminar.NestedScrollableHost android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/home_tablayout"> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/vp_home" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FAFAFA" /> </org.sopt.seminar.NestedScrollableHost>
-
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
private val activityLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == RESULT_OK && it.data != null) { var currentImageUri = it.data?.data Glide.with(requireActivity()).load(currentImageUri).into(binding.ivGalleryImage) } else if (it.resultCode == RESULT_CANCELED) { requireActivity().showToast("사진 선택 취소") } else { requireActivity().showToast("사진 첨부 실패") } }
Profile,Home | Camera |
---|---|