
// Tools.kt
// ContentActivity에서 보여줄 프래그먼트들의 이름
enum class ContentFragmentName(var str:String){
A_FRAGMENT("a"),
B_FRAGMENT("b"),
}
replaceFragment메서드와 removeFragment메서드의 매개변수 name의 타입을 ContentFragmentName로 바꿔주기
fragmentTransaction.replace 메서드에 인자값을 받을 프래그먼트컨테이너뷰 id를 containerContent로 꼭 바꿔주기
// ContentActivity.kt
// 프래그먼트 객체를 담을 변수
var oldFragment:Fragment? = null
var newFragment:Fragment? = null
// 지정한 Fragment를 보여주는 메서드
// name : 프래그먼트 이름
// addToBackStack : BackStack에 포함 시킬 것인지
// isAnimate : 애니메이션을 보여줄 것인지
// data : 새로운 프래그먼트에 전달할 값이 담겨져 있는 Bundle 객체
fun replaceFragment(name:ContentFragmentName, addToBackStack:Boolean, isAnimate:Boolean, data:Bundle?){
// Fragment 전환에 딜레이를 조금 준다.
SystemClock.sleep(200)
// Fragment를 교체할 수 있는 객체를 추출
val fragmentTransaction = supportFragmentManager.beginTransaction()
// 새로운 Fragment를 담을 변수
// var newFragment:Fragment? = null 이 코드는 지워주기
// oldFragment에 newFragment가 가지고 있는 Fragment 객체를 담아준다.
if(newFragment != null){
oldFragment = newFragment
}
// 이름으로 분기한다.
// Fragment의 객체를 생성하여 변수에 담아준다.
when(name){
}
// 새로운 Fragment에 전달할 Bundle 객체가 있다면 arguments 프로퍼티에 넣어준다.
if(data != null){
newFragment?.arguments = data
}
if(newFragment != null){
// 애니메이션 설정
if(isAnimate){
// oldFragment -> newFragment
// oldFragment : exit
// newFragment : enter
// newFragment -> oldFragment
// oldFragment : reenter
// newFragment : return
// MaterialSharedAxis : 좌우, 위아래, 공중 바닥 사이로 이동하는 애니메이션 효과
// X - 좌우
// Y - 위아래
// Z - 공중 바닥
// 두 번째 매개변수 : 새로운 화면이 나타나는 것인지 여부를 설정
// true : 새로운 화면이 나타나는 애니메이션이 동작한다.
// false : 이전으로 되돌아가는 애니메이션이 동작한다.
if(oldFragment != null){
// old에서 new가 새롭게 보여질 때 old의 애니메이션
oldFragment?.enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
// new에서 old로 되돌아갈 때 old의 애니메이션
oldFragment?.enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
oldFragment?.enterTransition = null
oldFragment?.returnTransition = null
}
// old에서 new가 새롭게 보여질 때 new의 애니메이션
newFragment?.enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
// new에서 old로 되돌아갈 때의 애니메이션
newFragment?.enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
newFragment?.exitTransition = null
newFragment?.reenterTransition = null
}
// Fragment를 교체한다.(이전 Fragment가 없으면 새롭게 추가하는 역할을 수행한다)
// 첫 번째 매개 변수 : Fragment를 배치할 FragmentContainer의 ID
// 두 번째 매개 변수 : 보여주고자 하는 Fragment 객체
fragmentTransaction.replace(R.id.fragmentContainerView, newFragment!!)
// addToBackStack 변수의 값이 true면 새롭게 보여질 Fragment를 BackStack에 포함시켜 준다.
if(addToBackStack == true){
// BackStack에 포함시킬 때 이름을 지정해주면 원하는 Fragment를 BackStack에서 제거할 수 있다.
fragmentTransaction.addToBackStack(name.str)
}
// Fragment 교체를 확정한다.
fragmentTransaction.commit()
}
}
// BackStack에서 Fragment를 제거한다.
fun removeFragment(name:ContentFragmentName){
// BackStack에 가장 위에 있는 Fragment를 BackStack에서 제거한다
// supportFragmentManager.popBackStack()
// Fragment 전환에 딜레이를 조금 준다.
SystemClock.sleep(200)
// 지정한 이름으로 있는 Fragment를 BackStack에서 제거한다.
supportFragmentManager.popBackStack(name.str, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}

// Tools.kt
// ContentActivity에서 보여줄 프래그먼트들의 이름
enum class ContentFragmentName(var str:String){
MAIN_FRAGMENT("MainFragment"),
B_FRAGMENT("b"),
}
// ContentActivity.kt
when(name){
// 게시글 목록 화면
ContentFragmentName.MAIN_FRAGMENT -> {
newFragment = MainFragment()
}
ContentFragmentName.B_FRAGMENT -> {
}
}
// ContentActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityContentBinding = ActivityContentBinding.inflate(layoutInflater)
setContentView(activityContentBinding.root)
settingNavigationView()
// MainFragment가 나타나도록 한다.
replaceFragment(ContentFragmentName.MAIN_FRAGMENT, false, false, null)
}

CoordinatorLayout이 같이 배치된다.
SearchBar와 SearchView를 같이 사용한다.
SearchBar를 클릭하면 SearchView가 나오게 한다.
SearchView의 layout_anchor 속성에 SerchBar의 아이디를 넣어 연결되게 해야 한다.
SearchBar 다음에 RecyclerView를 배치하면 CoordinatorLayout상에서 위치가 겹치기 때문에 margint_top 값을 넣어서 띄어준다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:transitionGroup="true"
tools:context=".fragment.MainFragment">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbarMain"
style="@style/Theme.AndroidProject4BoardApp.Toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.search.SearchBar
android:id="@+id/searchBarMain"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="90dp" />
<com.google.android.material.search.SearchView
android:id="@+id/searchViewMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_anchor="@id/searchBarMain">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewMainSearch"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.google.android.material.search.SearchView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>
처음에는 SearchBar가 보이고

SearchBar를 클릭하면 SearchView가 나타난다.

// MainFragment.kt
class MainFragment : Fragment() {
lateinit var fragmentMainBinding: FragmentMainBinding
lateinit var contentActivity: ContentActivity
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
fragmentMainBinding = FragmentMainBinding.inflate(inflater)
contentActivity = activity as ContentActivity
settingToolbar()
return fragmentMainBinding.root
}
// 툴바 설정
fun settingToolbar(){
fragmentMainBinding.apply {
toolbarMain.apply {
// 타이틀
title = "전체 게시판"
// 네비게이션
setNavigationIcon(R.drawable.menu_24px)
setNavigationOnClickListener {
// Drawer 메뉴가 나타나게 한다.
contentActivity.activityContentBinding.drawerLayoutContent.open()
}
}
}
}
}



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/textViewRowMainSubject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<TextView
android:id="@+id/textViewRowMainNickName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="TextView" />
</LinearLayout>

두개의 리사이클러뷰 모두 동일한 row_main.xml 사용함
// MainFragment.kt
// 메인 화면의 RecyclerView의 어댑터
inner class MainRecyclerViewAdapter : RecyclerView.Adapter<MainRecyclerViewAdapter.MainViewHolder>(){
inner class MainViewHolder(rowMainBinding: RowMainBinding) : RecyclerView.ViewHolder(rowMainBinding.root){
val rowMainBinding:RowMainBinding
init {
this.rowMainBinding = rowMainBinding
this.rowMainBinding.root.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
val rowMainBinding = RowMainBinding.inflate(layoutInflater)
val mainViewHolder = MainViewHolder(rowMainBinding)
return mainViewHolder
}
override fun getItemCount(): Int {
return 100
}
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
holder.rowMainBinding.textViewRowMainSubject.text = "제목 $position"
holder.rowMainBinding.textViewRowMainNickName.text = "닉네임 $position"
}
}
// MainFragment.kt
// 검색 화면의 RecyclerView의 어댑터
inner class SearchRecyclerViewAdapter : RecyclerView.Adapter<SearchRecyclerViewAdapter.SearchViewHolder>(){
inner class SearchViewHolder(rowMainBinding: RowMainBinding) : RecyclerView.ViewHolder(rowMainBinding.root){
val rowMainBinding:RowMainBinding
init {
this.rowMainBinding = rowMainBinding
this.rowMainBinding.root.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
val rowMainBinding = RowMainBinding.inflate(layoutInflater)
val mainViewHolder = SearchViewHolder(rowMainBinding)
return mainViewHolder
}
override fun getItemCount(): Int {
return 100
}
override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
holder.rowMainBinding.textViewRowMainSubject.text = "제목 $position"
holder.rowMainBinding.textViewRowMainNickName.text = "닉네임 $position"
}
}
// MainFragment.kt
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
fragmentMainBinding = FragmentMainBinding.inflate(inflater)
contentActivity = activity as ContentActivity
settingToolbar()
settingRecyclerViewMain()
settingRecyclerViewMainSearch()
return fragmentMainBinding.root
}
// 메인 화면의 RecyclerView 설정
fun settingRecyclerViewMain(){
fragmentMainBinding.apply {
recyclerViewMain.apply {
// 어댑터
adapter = MainRecyclerViewAdapter()
// 레이아웃 매니저
layoutManager = LinearLayoutManager(contentActivity)
// 데코레이션
val deco = MaterialDividerItemDecoration(contentActivity, MaterialDividerItemDecoration.VERTICAL)
addItemDecoration(deco)
}
}
}
// 검색 화면의 RecyclerView 설정
fun settingRecyclerViewMainSearch(){
fragmentMainBinding.apply {
recyclerViewMainSearch.apply {
// 어댑터
adapter = SearchRecyclerViewAdapter()
// 레이아웃 매니저
layoutManager = LinearLayoutManager(contentActivity)
// 데코레이션
val deco = MaterialDividerItemDecoration(contentActivity, MaterialDividerItemDecoration.VERTICAL)
addItemDecoration(deco)
}
}
}



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menuItemMainSearchAdd"
android:icon="@drawable/add_24px"
android:title="글작성"
app:showAsAction="always" />
</menu>
// MainFragment.kt
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
fragmentMainBinding = FragmentMainBinding.inflate(inflater)
contentActivity = activity as ContentActivity
settingToolbar()
settingRecyclerViewMain()
settingRecyclerViewMainSearch()
settingSearchBar()
return fragmentMainBinding.root
}
// SearchView 설정
fun settingSearchBar(){
fragmentMainBinding.apply {
searchBarMain.apply {
// SearchBar에 보여줄 메시지
hint = "여기를 눌러 검색해주세요"
// 메뉴
inflateMenu(R.menu.menu_main_search_menu)
}
searchViewMain.apply {
// SearchView에 보여줄 메시지
hint = "검색어를 입력해주세요"
}
}
}



// Tools.kt
// ContentActivity에서 보여줄 프래그먼트들의 이름
enum class ContentFragmentName(var str:String){
MAIN_FRAGMENT("MainFragment"),
ADD_CONTENT_FRAGMENT("AddContentFragment"),
}
// ContentActivity.kt
when(name){
// 게시글 목록 화면
ContentFragmentName.MAIN_FRAGMENT -> {
newFragment = MainFragment()
}
// 게시글 작성 화면
ContentFragmentName.B_FRAGMENT -> {
newFragment = AddContentFragment()
}
}
// MainFragment.kt
// SearchView 설정
fun settingSearchBar(){
fragmentMainBinding.apply {
searchBarMain.apply {
// SearchBar에 보여줄 메시지
hint = "여기를 눌러 검색해주세요"
// 메뉴
inflateMenu(R.menu.menu_main_search_menu)
setOnMenuItemClickListener {
// 글 작성 화면이 나타나게 한다.
contentActivity.replaceFragment(ContentFragmentName.ADD_CONTENT_FRAGMENT, true, true, null)
true
}
}
searchViewMain.apply {
// SearchView에 보여줄 메시지
hint = "검색어를 입력해주세요"
}
}
}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:transitionGroup="true"
tools:context=".fragment.AddContentFragment">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbarAddContent"
style="@style/Theme.AndroidProject4BoardApp.Toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="제목"
app:endIconMode="clear_text"
app:startIconDrawable="@drawable/subject_24px">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/textFieldAddContentSubject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/toggleAddContentType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:selectionRequired="true"
app:singleSelection="true">
<Button
android:id="@+id/buttonAddContentType1"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="자유"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<Button
android:id="@+id/buttonAddContentType2"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="유머"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<Button
android:id="@+id/buttonAddContentType3"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="시사"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
<Button
android:id="@+id/buttonAddContentType4"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="스포츠"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
</com.google.android.material.button.MaterialButtonToggleGroup>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:hint="내용"
app:endIconMode="clear_text"
app:startIconDrawable="@drawable/description_24px">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/textFieldAddContentText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text|textMultiLine"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/imageViewAddContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:adjustViewBounds="true"
app:srcCompat="@drawable/panorama_24px" />
</LinearLayout>
</ScrollView>
</LinearLayout>
이미지뷰의 adjustViewBounds 속성은 true로 할 시 이미지가 꽉차게 보이게 해준다.



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menuItemAddContentCamera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/photo_camera_24px"
android:title="카메라"
app:showAsAction="always" />
<item
android:id="@+id/menuItemAddContentAlbum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/photo_album_24px"
android:title="앨범"
app:showAsAction="always" />
<item
android:id="@+id/menuItemAddContentReset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/restart_alt_24px"
android:title="초기화"
app:showAsAction="always" />
<item
android:id="@+id/menuItemAddContentDone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/done_24px"
android:title="완료"
app:showAsAction="always" />
</menu>

// AddContentFragment.kt
class AddContentFragment : Fragment() {
lateinit var fragmentAddContentBinding: FragmentAddContentBinding
lateinit var contentActivity: ContentActivity
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
fragmentAddContentBinding = FragmentAddContentBinding.inflate(inflater)
contentActivity = activity as ContentActivity
settingToolbarAddContent()
return fragmentAddContentBinding.root
}
// 툴바 셋팅
fun settingToolbarAddContent(){
fragmentAddContentBinding.apply {
toolbarAddContent.apply {
// 타이틀
title = "글 작성"
// Back
setNavigationIcon(R.drawable.arrow_back_24px)
setNavigationOnClickListener {
contentActivity.removeFragment(ContentFragmentName.ADD_CONTENT_FRAGMENT)
}
// 메뉴
inflateMenu(R.menu.menu_add_content)
}
}
}
}
