본문 바로가기

안드로이드

ViewModel , Retrofit 이용한 대학교 검색

open api 를 이용해서 입력 텍스트가 변경될때마다 학교를 검색하는 예제입니다.

https://www.career.go.kr/cnet/front/openapi/openApiSchoolCenter.do

 

Open API 센터 | 진로정보망 커리어넷

학교정보를 찾아볼 수 있는 정보를 제공 합니다. 관심 있는 학교를 선택하면 해당 학교명에 해당하는 자세한 설명을 제공합니다.   오픈API 신청 JSON 형태 URL : //www.career.go.kr/cnet/openapi/getOpenApi.jso

www.career.go.kr

 

1. 모델 클래스 (SchoolModel.kt)

data class SchoolModel (
    var dataSearch: Datas
)

data class Datas(
    var content: List<ConTent>
)

data class ConTent (
    var region: String = "",
    var estType: String = "",
    var schoolName: String = "",
    var totalCount: String = "",
    var adres: String = "",
    var link: String = "",
    var campusName: String = "",
    var seq: String = "",
    var collegeinfourl: String = "",
    var schoolType: String = ""

)

 

2.검색 인터페이스

interface SearchSchool {
    @GET("cnet/openapi/getOpenApi.json")
    fun getSchool(
        @Query("apiKey") apiKey: String,
        @Query("svcType") svcType: String,
        @Query("svcCode") svcCode: String,
        @Query("contentType") contentType: String,
        @Query("gubun") text: String,
        @Query("searchSchulNm") searchSchulNm: String
    ): Call<SchoolModel>


    @POST("cnet/openapi/getOpenApi.json")
    fun getData(
        @Field("apiKey") apiKey: String

    ): Call<SchoolModel>
}

 

3. 뷰모델

class MainViewModel : ViewModel() {
    // TODO: Implement the ViewModel


    private var BASE_URL: String = "https://www.career.go.kr/";
    var liveData: MutableLiveData<SchoolModel> = MutableLiveData<SchoolModel>()

    fun getData(keyword: String) {
        val retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
        val api = retrofit.create(SearchSchool::class.java)
        val callGetSearchNews = api.getSchool(
            "발급받은 api키를 넣으세요.",
            "api",
            "SCHOOL",
            "json",
            "univ_list",
            keyword
        )

        callGetSearchNews.enqueue(object : Callback<SchoolModel> {
            override fun onResponse(call: Call<SchoolModel>, response: Response<SchoolModel>) {

                Log.d("결과", "성공 : ${response.raw()}")
                response.body()?.let {
                    liveData.value=it
                }
            }
            override fun onFailure(call: Call<SchoolModel>, t: Throwable) {

                Log.d("결과:", "실패 : $t")
            }
        })


    }


}

4. 메인 프레그먼트

class MainFragment : Fragment() {

    companion object {
        fun newInstance() = MainFragment()
    }

    private lateinit var viewModel: MainViewModel
    private lateinit var  myRecyclerView : RecyclerView
    private lateinit var  rkeyword : EditText

    private lateinit var mAdapter : RecyclerAdapter
    private val addressItemList = mutableListOf<ConTent>() // 서버에서 가져온 원본 데이터 리스트

    private lateinit var appContext: Context


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        var rootView =  inflater.inflate(R.layout.main_fragment, container, false)
        myRecyclerView =rootView.findViewById(R.id.recyler);
        rkeyword =rootView.findViewById(R.id.keyword);
        appContext  = requireContext().applicationContext
        return rootView;
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)


        rkeyword.addTextChangedListener(object  : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {

            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                if(s.toString().isNotEmpty()){
                    viewModel.getData(s.toString())
                }

            }

            override fun afterTextChanged(s: Editable?) {

            }

        })
        viewModel.liveData.observe(viewLifecycleOwner, listUpdateObserver)

    }

    private var listUpdateObserver: Observer<SchoolModel> =
        Observer {

            var contents : List<ConTent> = it.dataSearch.content;

            mAdapter = RecyclerAdapter(contents, appContext) // Adapter 생성
            myRecyclerView.adapter = mAdapter // 어댑터를 리스트뷰에 세팅
            myRecyclerView.layoutManager = LinearLayoutManager(appContext)
         //   mAdapter.submitList(it)

            addressItemList.clear()
            addressItemList.addAll(contents)
            mAdapter.notifyDataSetChanged()


        }




}

 

5. 어댑터

class RecyclerAdapter(val postList : List<ConTent>, val context : Context)
    : RecyclerView.Adapter<RecyclerAdapter.ViewHolder>(){


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            LayoutInflater.from(context)
            .inflate(R.layout.list_item,parent ,false))

    }



    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(postList[position],context)
    }

    override fun getItemCount(): Int {
        return postList.count()
    }


    class ViewHolder (itemView: View? ) : RecyclerView.ViewHolder(itemView!!){


        private var title = itemView?.findViewById<TextView>(R.id.title)
        private var contents  = itemView?.findViewById<TextView>(R.id.contents)


        fun bind(sdata: ConTent, context: Context){

            title?.text = sdata?.schoolName
            contents?.text = sdata?.adres

        }

    }

}

 

6.메인엑티비티

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)

        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.container, MainFragment.newInstance())
                .commitNow()
        }
    }
}

 

7.메인레이아웃 (main_acitvity.xml)

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" />

8.메인 프래그먼트(main_fragment.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    tools:context=".ui.main.MainFragment">

    <EditText
        android:id="@+id/keyword"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical"
        android:hint="검색어 입력" android:paddingRight="10dp"
        android:paddingLeft="10dp" android:layout_marginRight="110dp"
        android:textColor="@color/black" app:layout_constraintRight_toLeftOf="@+id/done"
        app:layout_constraintTop_toTopOf="parent">

    </EditText>

    <Button
        android:layout_width="100dp" android:id="@+id/done"
        android:layout_height="50dp"
        android:layout_marginRight="15dp"
        android:text="검색"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </Button>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyler"
        android:layout_width="match_parent" android:layout_marginBottom="10dp"
        android:layout_height="0dp"  app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        app:layout_constraintTop_toBottomOf="@+id/keyword">

    </androidx.recyclerview.widget.RecyclerView>


</androidx.constraintlayout.widget.ConstraintLayout>

 

9.리스트뷰(list_item.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="70dp" android:padding="10dp"
    android:background="#ffffff"
    tools:context=".ui.main.MainFragment">

    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:gravity="center_vertical" android:textSize="15dp"
        android:textColor="@color/black"   android:text="2222"
        app:layout_constraintTop_toTopOf="parent">

    </TextView>

    <TextView
        android:id="@+id/contents"
        android:layout_width="match_parent"
        android:layout_height="30dp" android:textSize="12dp"
        android:gravity="center_vertical" app:layout_constraintTop_toBottomOf="@+id/title"
        android:textColor="#828282" android:text="1111"
       >

    </TextView>


</androidx.constraintlayout.widget.ConstraintLayout>

 

10.결과