본문 바로가기

Android Project

[Android/Kotlin] 커뮤니티앱(7) - 게시글 만들기

이제 자취톡 부분에 게시판 기능을 만들어볼것이다

 

일단 fragment_talk.xml로 가서 게시물작성버튼을 추가로 디자인해준다

<ImageView
    android:id="@+id/writeBtn"
    android:layout_width="80dp"
    android:layout_height="80dp"
    android:src="@drawable/btnwrite"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    android:layout_marginBottom="90dp"
    android:layout_marginRight="20dp"/>

fragment_talk.xml

fragment_talk.xml

 

 

그리고 게시물작성 버튼을 누르면 이동할 엑티비티인 BoardWriteActivity와 xml파일을 만들어주고,

TalkFragment로 가서 게시물 작성버튼을 눌렀을때 BoardWriteActivity로 이동하는 코드를 짜준다

// 게시물 작성버튼 눌렀을때
binding.writeBtn.setOnClickListener { 
    val intent = Intent(context, BoardWriteActivity::class.java)
    startActivity(intent)
}

TalkFragment.kt

 

그런다음  activity_board_write.xml 레이아웃파일을 디자인해주겠다

<?xml version="1.0" encoding="utf-8"?>

<layout>

    <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"
        tools:context=".board.BoardWriteActivity"
        android:orientation="vertical">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="60dp">

                    <TextView
                        android:text="글쓰기 페이지"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:textColor="@color/black"
                        android:gravity="center"/>

                </LinearLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="0.5dp"
                    android:background="#000000"/>

                <EditText
                    android:id="@+id/titleArea"
                    android:layout_margin="20dp"
                    android:layout_width="match_parent"
                    android:background="@android:color/transparent"
                    android:hint="제목을 입력해주세요"
                    android:layout_height="60dp"/>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="0.5dp"
                    android:layout_marginLeft="20dp"
                    android:layout_marginRight="20dp"
                    android:background="#000000"/>

                <EditText
                    android:id="@+id/contentArea"
                    android:layout_margin="20dp"
                    android:layout_width="match_parent"
                    android:hint="내용을 입력해주세요"
                    android:background="@android:color/transparent"
                    android:layout_height="60dp"/>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="0.5dp"
                    android:layout_marginLeft="20dp"
                    android:layout_marginRight="20dp"
                    android:background="#000000"/>

                <ImageView
                    android:id="@+id/imageArea"
                    android:src="@drawable/plusbtn"
                    android:layout_marginTop="20dp"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"/>

                <Button
                    android:id="@+id/writeBtn"
                    android:text="입력"
                    android:layout_width="match_parent"
                    android:background="@drawable/background_radius_yellow"
                    android:layout_marginTop="50dp"
                    android:layout_marginLeft="20dp"
                    android:layout_marginRight="20dp"
                    android:layout_height="wrap_content"/>

            </LinearLayout>

        </ScrollView>

    </LinearLayout>

</layout>

activity_board_write.xml

 

activity_board_write.xml

 

여기까지하면 게시물작성버튼을 클릭하면 BoardWriteActivity화면으로 이동하는것을 확인할수있다

이제 입력버튼을 눌렀을때 파이어베이스 데이터베이스에 데이터를 올리는 작업을 해야될것이다 

 

파이어베이스 리얼타임 데이터베이스에 board를 새로 추가시켜서 게시글이 저장되도록 만들어보고싶기때문에 

FBRef.kt파일로가서 getReference를 사용해서 board를 추가해준다

// 파이어베이스에 board 추가
val boardRef = database.getReference("board")

FBRef.kt

 

그리고 BoardWriteActivity로 가서 데이터바인딩을 해준뒤, 입력버튼을 눌렀을때 titleArea(제목)과 contentArea(내용)에 해당하는 부분을 받아온다. 제목과 내용이 잘 받아왔는지 로그를 찍어서 확인해보면 내가 입력한 제목,내용값이 로그에 잘 찍히는것을 확인할수있다 

 

이제 데이터를 집어넣는 작업을 해줄것이다

파이어베이스에 들어갈 데이터구조를 아래와같이 짜줄것이다. boardModel은 title, content, uid, time을 넣어서 직접 데이터모델을 생성해줄것이다

<데이터구조>

-board

  - key (임의의값)

    - boardModel (title, content, uid, time)

 

근데 아직 boardModel이 없기때문에 먼저 생성해준다

package com.example.mysololife.board

data class BoardModel(

    val title : String = "",	// 제목
    val content : String = "",	// 내용 
    val uid : String = "",      // uid
    val time : String = ""      // 현재시간
    
)

boardModel.kt

 

package com.example.mysololife.board

class BoardWriteActivity : AppCompatActivity() {

    private lateinit var binding : ActivityBoardWriteBinding

    private val TAG = BoardWriteActivity::class.java.simpleName

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_board_write)


        binding.writeBtn.setOnClickListener {

            // titleArea,contentArea 값을 받아옴
            val title = binding.titleArea.text.toString()
            val content = binding.contentArea.text.toString()

            Log.d(TAG, title)
            Log.d(TAG, content)

            // 데이터 집어넣기
            FBRef.boardRef
                .push()
                .setValue(BoardModel(title,content,"uid","time"))
        }

    }
}

BoardWriteActivity.kt

 

이렇게하고 글을작성하고, 파이어베이스를 보면 board안에 내가 쓴 글이 잘 등록이 되어있는걸 볼수있다 

 

근데 아직 uid값과 시간에 해당하는 값은 들어가지 않았기 때문에 알맞은값이 들어갈수있도록 수정해준다

uid값은 이미 FBAuth.kt에 만들어놨기때문에 그걸 가져와서 사용해주고, 시간에 해당하는값은 아래와같이 추가를 해준다

// 시간가져오는 함수
fun getTime() : String{

    val currentDateTime = Calendar.getInstance().time
    val dateFormat = SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.KOREA).format(currentDateTime)

    return dateFormat
}

FBAuth.kt

package com.example.mysololife.board

class BoardWriteActivity : AppCompatActivity() {

    private lateinit var binding : ActivityBoardWriteBinding

    private val TAG = BoardWriteActivity::class.java.simpleName

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_board_write)

        binding.writeBtn.setOnClickListener {

            // titleArea,contentArea 값을 받아옴
            val title = binding.titleArea.text.toString()
            val content = binding.contentArea.text.toString()
            // uid값 가져오기
            val uid = FBAuth.getUid()
            // tiem값 가져오기
            val time = FBAuth.getTime()

            Log.d(TAG, title)
            Log.d(TAG, content)

            // 데이터 집어넣기
            FBRef.boardRef
                .push()
                .setValue(BoardModel(title,content,uid,time))

            Toast.makeText(this, "게시글 입력완료", Toast.LENGTH_SHORT).show()

            // 엑티비티 사라짐
            finish()
        }

    }
}

BoardWriteActivity.kt

 

BoardWriteActivity도 이렇게 수정해주고 파이어베이스를 확인해보면 uid값과 시간값도 잘 들어간걸 확인할수있다

 

 

 

 

이제 파이어베이스에 등록되어있는 데이터들을 불러오는 작업을 해볼것이다

 

게시글을 올리면 보이는부분을 리스트뷰를 이용해서 나타내줄것이기때문에 

fragment_talk.xml로가서 리스트뷰를 추가해준다. (리스트뷰 만들기- 리스트뷰추가,아이템레이아웃 생성,어뎁터와 연결)

그리고 board_list_item.xml을 생성해서 아이템 레이아웃을 만들어준다 

<?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="120dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/titleArea"
        android:text="제목"
        android:textSize="20sp"
        android:layout_margin="15dp"
        android:textStyle="bold"
        android:textColor="@color/black"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView
        android:id="@+id/contentArea"
        android:text="내용"
        android:textSize="15sp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:textColor="@color/black"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView
        android:id="@+id/timeArea"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="5dp"
        android:text="시간"
        android:textColor="#999999"
        android:textSize="10sp" />

</LinearLayout>

board_list_item.xml

board_list_item.xml

 

제목,내용,시간에 관련된 내용만 리스트뷰에 보여지게 할것이기때문에 이렇게 디자인해줬다

 

 

이제 어뎁터도 생성해준다

package com.example.mysololife.board

// BoardModel 데이터모델을 받아옴
class BoadListLVAdapter(val boardList: MutableList<BoardModel>) : BaseAdapter() {
    override fun getCount(): Int {
        // boardList의 사이즈만큼 리턴
        return boardList
        .size
    }

    override fun getItem(position: Int): Any {
        // boardList를 클릭한값 리턴
        return boardList[position]
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {

        // 아이템뷰를 가져와서 연결
        var view = convertView

        if (view == null){
            view = LayoutInflater.from(parent?.context).inflate(R.layout.board_list_item, parent,false)
        }
        return view!!
    }
}

BoardListLVAdapter.kt

 

이제 방금만든 이 어뎁터를 TalkFragment에 연결해주면 된다

그리고 BoardModle의 리스트를 만들어주고, 값을 임의로 추가해준다 

package com.example.mysololife.fragments

class TalkFragment : Fragment() {

    private lateinit var binding : FragmentTalkBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = DataBindingUtil.inflate(inflater,R.layout.fragment_talk,container,false)
        
        val boardList = mutableListOf<BoardModel>()
        boardList.add(BoardModel("a","b","c","d"))
        
        // 어뎁터와 ListView 연결
        val BoardRVadapter = BoadListLVAdapter(boardList)
        binding.boardListView.adapter = BoardRVadapter
    
        ...
}

TalkFragment.kt

 

여기까지 작성해주고 앱을 실행시켜주면 리스트뷰 한개가 잘 나오고 있는것을 확인할수있다

 

 

지금은 a,b,c,d로 샘플데이터를 넣었지만, 실제로 파이어베이스에 등록되어있는 데이터들을 가져와서 넣어줘야한다 

 

TalkFramgment에서 게시글 데이터를 가져오고, 데이터를 받아와서 하나하나씩 넣어주는 작업을 해준다 

그리고 이 데이터들을 어뎁터와 연결해준다

package com.example.mysololife.fragments


class TalkFragment : Fragment() {

    private lateinit var binding : FragmentTalkBinding

    private val TAG = TalkFragment::class.java.simpleName

    private val boardDataList = mutableListOf<BoardModel>()

    private lateinit var boardRVAdapter : BoadListLVAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }

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

//        val boardList = mutableListOf<BoardModel>()
//        boardList.add(BoardModel("a","b","c","d"))


        // 어뎁터와 ListView 연결
        boardRVAdapter = BoadListLVAdapter(boardDataList)
        binding.boardListView.adapter = boardRVAdapter


        ...




    // 게시글 데이터 가져오는 함수
    private fun getFBBoardData(){

        // 데이터 가져오기
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {

                for (dataModel in dataSnapshot.children){

                    Log.d(TAG, dataModel.toString())

                    // 데이터 받아오기
                    val item = dataModel.getValue(BoardModel::class.java)
                    boardDataList.add(item!!)   // BoardModel에 데이터 하나씩 넣어줌
                }

                //동기화
                boardRVAdapter.notifyDataSetChanged()

                Log.d(TAG, boardDataList.toString())
            }



            override fun onCancelled(databaseError: DatabaseError) {
                // Getting Post failed, log a message
                Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
            }
        }
        FBRef.boardRef.addValueEventListener(postListener)
    }


}

TalkFramgment.kt

 

여기까지하면 이런식으로 결과가 나오는것을 볼수있다 

 

등록은 되는데 내가 쓴 글의 내용들은 반영이 안되기때문에 반영을 해주도록 하겠다

그리고 처음에 게시글내용이 2배로 등록되는 오류도 수정해보도록 하겠다

 

BoardListLVAdapter에서 아래와같이 코드를 써주면 내가 작성한 값들이 리스트뷰에 잘 보여지는것을 확인할수있다

override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {

     ...
    
    // 내가 작성한 title값이 titleArea1에 적용되게
    val title = view?.findViewById<TextView>(R.id.titleArea1)
    title!!.text = boardList[position].title

    // 내가 작성한 content값이 contentArea1에 적용되게
    val content = view?.findViewById<TextView>(R.id.contentArea1)
    content!!.text = boardList[position].content

    // 내가 작성한 time값이 timeArea1에 적용되게
    val time = view?.findViewById<TextView>(R.id.timeArea1)
    time!!.text = boardList[position].time


    return view!!
}

BoardListLVAdapter.kt

 

보통은 순서를 아래와같이 써주는것같지만 나는 위에처럼 쓰는게 편해서 저렇게 써주었다

override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {

     ...
    
    val title = view?.findViewById<TextView>(R.id.titleArea1)
    val content = view?.findViewById<TextView>(R.id.contentArea1)
    val time = view?.findViewById<TextView>(R.id.timeArea1)
    
    title!!.text = boardList[position].title 
    content!!.text = boardList[position].content 
    time!!.text = boardList[position].time

    return view!!
}

BoardListLVAdapter.kt

 

이제 데이터가 겹쳐서 출력되는현상을 해결해줄것이다. 

데이터가 변경이 되면 한번 더 실행이 되는게 파이어베이스의 특징이라고 할수있다

그래서 데이터를 초기화시켜줘서 겹쳐서 출력되는 현상을 방지시켜준다 

// 게시글 데이터 가져오는 함수
private fun getFBBoardData(){

    // 데이터 가져오기
    val postListener = object : ValueEventListener {
        override fun onDataChange(dataSnapshot: DataSnapshot) {

            // 겹쳐서 출력되는 현상 방지 (데이터 초기화)
            boardDataList.clear()

            for (dataModel in dataSnapshot.children){

                Log.d(TAG, dataModel.toString())

                // 데이터 받아오기
                val item = dataModel.getValue(BoardModel::class.java)
                boardDataList.add(item!!)   // BoardModel에 데이터 하나씩 넣어줌
            }

            //동기화
            boardRVAdapter.notifyDataSetChanged()

            Log.d(TAG, boardDataList.toString())
        }

TalkFramgment.kt

 

 

 

여기까지하면 내가 입력한 데이터가 리스트뷰에 잘 반영이되는것을 볼수있다