프로그램에서 함수는 스택구조이다.
0->1->2->3->3->2->1->0

MVP 패턴 
모델, 뷰, 프레젠터


Model : 데이터 처리
View : xml을 가지는 액티비티 또는 프래그먼트가 됨.
Presenter : 뷰와 모델을 연결해주는 인터페이스


view    <->  Presenter <-> Model 
뷰와 프레젠터는 화면을 그리기 위한 처리만한다.

프레젠터와 모델은 데이터 처리만 한다.(추가, 갱신, 삭제)

프레젠터가 모델에 데이터를 처리 요청을 하고 값을 돌려 받으면
프레젠터는 뷰에게 값만 전달하여 화면 변경을 한다.

액션(서버통신) 또는 데이터 가져오는 클래스가 모델이 된다.


소스예제.


1. 데이터 클래스 생성.
/**데이터를 생성하는 클래스.*/
class MakeData {

/**어레이리스트 형식으로 객체생성*/
    fun makeList(): MutableList {

        var data = mutableListOf()
        for (i in 0 until 50) {
            data.add("$i element row")
        }
        return data
    }

}

mvp 패턴에서 사용할 Base interface 등록

2. 인터페이스 생성.
/**뷰에서 구현할 인터페이스.*/
interface BaseView {
    /**제네릭으로 상속받을 프레젠터 클래스.*/
    val presenter: T
}

3. 인터페이스 생성.
/**프레젠터에서 구현할 인터페이스*/
interface BasePresent {
    /**
     * 제네릭으로 상속받을 뷰 클래스.
     * 최초 객체 생성이 null로 생성되므로 null type 선언.
     */
    val view: T?
}

4. 계약자 인터페이스 생성.
/**아답터에서사용할 계약자 인터페이스 */
interface MainActivityAdapterContract {

/**아답터뷰*/
    interface View {
     /**리스트 갱신을 위한 데이터 갱신*/
        fun changeData(data: MutableList)
    }

}

5. 계약자 인터페이스 생성.
/**
 * mvp 패턴에서 사용할 계약자 인터페이스 생성.
 * 계약자의 프레젠터를 상속 받음.
 * 등록한 뷰에는 UI처리만 하도록 한다. 화면에 그릴 데이터의 결과값만 던저준다. 
 */
/**mvp 패턴에서 사용할 계약자 인터페이스 생성.*/
interface MainActivityContract {

    /**BaseView 를 상속받은 인터페이스 제네릭으로 인터페이스(Present)를 넘겨줌*/
    interface View : BaseView {
        /**
         * 뷰는 데이터 처리를 하지 않으며 데이터의 결과만 전달한다.
         * 뷰는 프레젠터에서 넘겨받은 결과로 UI의 변경만 한다.
         */
    }

    /**BasePresent를 상속받은 인터페이스 제네릭으로 인터페이스(View)를 넘겨줌*/
    interface Present : BasePresent {
        /**
         * 프레젠터는 데이터를 처리한다.
         * 
         */

        fun attachView(view: View)
        /**뷰 해제.*/
        fun detachView()
        
        /**데이터 갱신위한 아답터뷰 등록*/
        var adapterView: MainActivityAdapterContract.View?
/**데이터 클래스에서 값 가져오기.*/
        fun getData()
        
    }
}

6. 리사이클러뷰 홀더 생성.
/**리사이클러뷰 각 항목 그리기 위한 홀더 생성.*/
class MainActivityViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView),
    LayoutContainer {

    companion object {
        fun newInstance(parent: ViewGroup): MainActivityViewHolder {
            val view = LayoutInflater.from(parent.context).inflate(R.layout.datalayout_row, parent, false)
            return MainActivityViewHolder(view)
        }
    }

    fun onBindView(str: String) {
        textView_Row.text = str
    }

}

7. 리사이클러뷰 아답터 
/** 아답터에 대한 MVP */
class MainActivityAdapter : RecyclerView.Adapter(), MainActivityAdapterContract.View {

    private var data: MutableList = mutableListOf()
        private set(value) {
            field = value
            notifyDataSetChanged()
        }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainActivityViewHolder {
        return MainActivityViewHolder.newInstance(parent)
    }

    override fun getItemCount(): Int {
        return data.size
    }

    override fun onBindViewHolder(holder: MainActivityViewHolder, position: Int) {
        holder.onBindView(data[position])
    }

    override fun changeData(data: MutableList) {
        this.data=data
    }
}

8. 액티비티 구현을 위한 상속 액티비티
/**상속 액티비티*/
abstract class BaseActivity : AppCompatActivity() {

    protected abstract val layoutId: Int

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

9. 구현 액티비티 
/**
 * 액티비티가 mvp 패턴에서 View 해당한다. View엔 데이터 처리를 하지않고
 * 데이터 처리를 위한 작업은 presenter에 요청한다.
 */
class MainActivity : BaseActivity(), MainActivityContract.View {


    override val layoutId = R.layout.activity_main

    override val presenter: MainActivityContract.Present = MainActivityPresenter()


    private lateinit var mainActivityAdapter: MainActivityAdapter

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

        setupUI()

        presenter.adapterView = mainActivityAdapter

        //프레젠터에 데이터를 요청.
        presenter.getData()
    }

    override fun onDestroy() {
        presenter.detachView()
        super.onDestroy()
    }

    private fun setupUI() {
        setupRecyclerView()
    }

    private fun setupRecyclerView() {
        mainActivityAdapter = MainActivityAdapter()
        recyclerView.adapter = mainActivityAdapter
    }

}

'Android > study' 카테고리의 다른 글

안드로이드 코틀린 프로젝트 디펜던시  (0) 2019.01.25
안드로이드 액티비티 및 프래그먼트.  (0) 2018.09.27
레트로핏, okhttp  (0) 2018.08.27
RxJava  (0) 2018.07.30

MVP 패턴.





MODEL : 데이터 처리.


VIEW : 뷰 


PRESENT : MODEL과 VIEW 사이의 매개체



MVP 패턴의 동작


VIEW 에서 사용자 이벤트 수신

VIEW 에서 PRESENTER 이벤트 호출

PRESENTER 에서 MODEL 데이터 요청

MODEL 에서 PRESENTER 로 데이터 전달

PRESENTER 에서 전달받은 데이털 처리 후 VIEW 업데이트 요청

VIEW 에서 화면 업데이트



구현 방법


interface Contact


inner interface View

inner interface Present 


View 에서 처리할 이벤트 선언

Present 에서 처리할 이벤트 선언



View를 상속받는 Activiry 구현

Present를 상속받는 Presenter 구현


Presenter 에서 Model로 데이터 요청.(통신을 통한 서버 데이터 요청)


Model 에서 Presenter 가 요청한 데이터를 전달 후 Presenter에서 데이터 가공 후 View에 업데이트 요청


View 로 가공된 데이터를 가지고 화면 업데이트. 




MainActivityContract


MainActivity  MainActivityContract.View 상속


MainActivityPresenter MainActivityContract.Present 상속


MainActivityDataAction Present에서 데이터 요청




코틀린으로 코드 구현.




/**

 * 액티비티 상속 클래스

 */

abstract class BaseActivity : AppCompatActivity() {


    /**레이아웃 뷰 추상화 등록*/

    protected abstract val layoutId: Int


    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(layoutId)

    }

}




/**

 * mvp 계약자 정의

 */

interface MainActivityContract {

    /**

     * 단순 화면 처리

     * 액티비이에서 뷰 구현

     */

    interface View {


        /** 뷰에 프레젠터 등록.*/

        val presenter: Present


        fun buttonClickChangeUI(count: Int)


    }


    /**

     * 프레젠터 뷰와 모델의 데이터 처리

     * 프레젠터 클래스를 생성하여 인터페이스 구현.

     */

    interface Present {


        /**

         * 프레젠터에 뷰 등록

         *

         */

        var view: View?


        /** 전달받을 뷰를 등록함.*/

        fun attachView(view: View)


        /** 뷰 해제*/

        fun detachView()



        fun buttonClickCountUp(count: Int)

    }

}


/**

 * MainActivity

 * 

 */

class MainActivity : BaseActivity(), MainActivityContract.View {


    /**BaseActivity에서 상속받은 변수. */

    override val layoutId = R.layout.activity_main

    /**뷰에 지정한 프레젠터 등록.*/

    override val presenter: MainActivityContract.Present = MainActivityPresenter()


    override fun onCreate(savedInstanceState: Bundle?) {


        super.onCreate(savedInstanceState)

        presenter.attachView(this)

        buttonEvent()

    }



    fun buttonEvent() {


        button_Click.setOnClickListener(View.OnClickListener {

            Log.w("bear", "buttonclick")

            presenter.buttonClickCountUp(count)

        }

        )

    }


    var count = 0

    override fun buttonClickChangeUI(count: Int) {

        this.count = count

        textview.text = "${this.count}"

    }

}




/**

 * 데이터를 처리하는 프레젠터

 * 처리된 결과를 등록한 뷰에 전달.

 */

class MainActivityPresenter : MainActivityContract.Present {


    /**뷰 초기화*/

    override var view: MainActivityContract.View? = null


    /**뷰 지정.onCreate 에 등록*/

    override fun attachView(view: MainActivityContract.View) {

        this.view = view

    }


    /**뷰 해제*/

    override fun detachView() {

        this.view = null

    }


    override fun buttonClickCountUp(count: Int) {

        view?.buttonClickChangeUI(count + 1)

    }

}






이런식으로 사용이 가능하다.





+ Recent posts