Android - 뷰가 스와이핑 되는 뷰 페이저 (Kotlin)
업데이트:
- 연구주제 : Android - 뷰가 스와이핑 되는 뷰 페이저 (Kotlin) (Kotlin)
- 연구목적 : 안드로이드에서의 코틀린 활용
- 연구일시 : 2020년 02월 25일 09:00~17:00
- 연구자 : 이재환 ljh951103@naver.com
- 연구장비 : HP EliteDesk 800 G4 TWR, Kotlin, Android studio, IntelliJ
- 관련연구 : Java, Android, Kotlin, ViewPager
서론
이번 시간에는 뷰 페이저를 구성해보고자 한다.
뷰 페이저는 데이터를 페이지 단위로 표시하고 좌/우 스와이프를 통해 페이지를 전환할 수 있도록 만들어주는 컨테이너라고 볼 수 있다.
뷰 페이저는 자체적으로 뷰를 그리지는 않고 여러 종류의 뷰를 사용하여 각 뷰페이저의 페이지를 구성한다.
다음과 같이 어댑터에 뷰 페이저를 구성할 뷰를 연결시키게 되면 스와이프 가능한 뷰 페이저가 완성된다.
그럼 뷰 페이저를 직접 구현하러 가보자.
본론
우선 우리는 다음과 같은 사진들을 각각 클릭을하면 해당 사진을 가진 뷰페이저로 이동하도록 해야한다.
그리고 좌/우 스와이프시 할당된 다른 이미지가 출력되게 된다.
인텐트
우선 저번 포스팅에서 뷰 아이템 클릭에 대해 다루었으니 리스너를 붙이는 부분은 생략하겠다.
특정 이미지를 클릭하면 해당 뷰페이저가 출력되어야 하니 사진에 대한 정보가 반드시 전달 되어야 한다.
따라서 다음과 같이 데이터도 같이 전달한다.
recyclerAdapter =
RecyclerAdapterPhoto(this, thumbnailList) {
thumbnailData, num, image ->
Toast.makeText(this, "인덱스: ${num} 이름: ${thumbnailData.data}", Toast.LENGTH_SHORT)
.show()
val intent = Intent(this, com.example.wimmy.PhotoViewPager::class.java)
intent.putExtra("photo_num", num)
photoList.addAll(thumbnailList)
Log.d("사이즈", "${photoList.size}")
intent.putParcelableArrayListExtra("photo_list", photoList)
startActivityForResult(intent, 100)
}
어댑터
어댑터를 보기전에 뷰 페이저가 동작하는 방법을 알아야한다.
방법을 어떻게 설명하지 알아보던 중, 가장 직관적이고 이해하기 쉬운 자료를 찾았다.
그럼 코드를 보자.
public class PagerRecyclerAdapter(private val context: Context, var list: List<thumbnailData>, var tb: View, var bt: View) : PagerAdapter() {
private var layoutInflater: LayoutInflater? = null
private var check: Boolean = false
override fun isViewFromObject(view: View, `object`: Any): Boolean {
return view === `object`
}
override fun getCount(): Int {
return list.size
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val v = layoutInflater!!.inflate(R.layout.photoview_pager, null)
val image = v.findViewById<View>(R.id.imgView) as ImageView
val vp = container as ViewPager
vp.addView(v, 0)
image.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
if(check == false) {
// Log.d("이건?", tb.toString())
tb.visibility = View.GONE
bt.visibility = View.GONE
check = true
}
else {
tb.visibility = View.VISIBLE
bt.visibility = View.VISIBLE
check = false
}
}
})
return v
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
val vp = container as ViewPager
val v = `object` as View
vp.removeView(v)
}
fun setThumbnailList(list : List<thumbnailData>) {
this.list = list
notifyDataSetChanged()
}
}
우선 뷰 페이저의 동작은 다음과 같다.
여기서 override 되는 메소드들은 다음과 같다.
-
getCount(): 전체 페이지수를 반환
-
instantiateItem(): 화면에 표시할 페이지 뷰 생성
-
isViewFromObject(): 페이지 뷰가 내부적으로 관리되는 키 객체와 연관되는지 확인
뷰 페이저
class PhotoViewPager : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/*getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
val uiOptions = getWindow().getDecorView().getSystemUiVisibility();
var newUiOptions = uiOptions;*/
getExtra()
setContentView(R.layout.photoview_frame)
subimg = findViewById(R.id.sub_img) as ImageView // 뷰페이저로 넘어올 때, 애니메이션을 위한 눈속임
subimg!!.setImageResource(R.drawable.loding_image)
super.onEnterAnimationComplete()
var vm = ViewModelProviders.of(this).get(PhotoViewModel::class.java)
vm.getNameDir().observe(this,
Observer<List<thumbnailData>> { t -> recyclerAdapter?.setThumbnailList(t)
})
val view: View = findViewById(R.id.imgViewPager)
val text_name = findViewById<AppCompatTextView>(R.id.imgView_text)
val tb = findViewById<View>(R.id.mainphoto_toolbar)
val bt = findViewById<View>(R.id.bottom_photo_menu)
setView(view, tb, bt)
toolbar_text(index, text_name)
viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) { }
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
if(check == false ) {
viewPager.setCurrentItem(index, false)
check = true
}
subimg!!.setImageResource(0) // 애니메이션
tb.visibility = View.VISIBLE
bt.visibility = View.VISIBLE
}
override fun onPageSelected(position: Int) {
check_index = position
text_name.setText(photoList[position].data)
}
})
}
private fun setView(view: View, toolbar: View, bottombar: View) {
viewPager = view.findViewById<RecyclerView>(R.id.imgViewPager) as ViewPager
recyclerAdapter =
PagerRecyclerAdapter(
this,
thumbnailList, toolbar, bottombar
)
//Log.d("asd",recyclerAdapter?.getThumbnailList())
viewPager?.adapter = recyclerAdapter
}
fun toolbar_text(position: Int, name: AppCompatTextView){
name.setText(photoList[position].data)
}
fun getExtra(){
if (intent.hasExtra("photo_num") && intent.hasExtra("photo_list")) {
index = intent.getIntExtra("photo_num", 0)
photoList = intent.getSerializableExtra("photo_list") as ArrayList<thumbnailData>
}
else {
Toast.makeText(this, "전달된 이름이 없습니다", Toast.LENGTH_SHORT).show()
}
}
}
다음과 같이 뷰 페이저 액티비티를 구성한다.
간단하게 설명하면 뷰 페이저 어댑터와 연결하여 만들고 인텐트로 넘어온 값을 받아서 viewPager.setCurrentItem(index, false)
를 통해 해당 아이템으로 뷰 페이저를 이동시킨다.
레이아웃
그럼 뷰 페이저의 레이아웃을 한번 볼까?
메인 뷰페이저
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
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"
tools:context=".PhotoViewPager"
android:background="#000000">
<androidx.viewpager.widget.ViewPager
android:id="@+id/imgViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="MissingConstraints">
</androidx.viewpager.widget.ViewPager>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
내부 뷰페이저
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.ortiz.touchview.TouchImageView
android:id="@+id/imgView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:src="@drawable/loding_image"
android:transitionName="pair_thumb"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
다음과 같이 메인 뷰페이저와 내부 뷰페이저로 구성한다.
메인 뷰페이저는 액티비티의 레이아웃이고 내부 뷰페이저는 어댑터에서 데이터 뷰를 구성하는데 사용된다.
결론
위의 영상을 보면 코드가 정상적으로 동작하여 올바른 뷰 페이저를 출력하고있음을 확인할 수 있다.
향후과제
- 뷰 페이저에서 빠져나왔을 때, 마지막으로 출력되고있던 뷰에 초점맞추기
참고자료
https://docs.microsoft.com/ko-kr/xamarin/android/user-interface/controls/view-pager/
https://recipes4dev.tistory.com/148
Writer: Jae-Hwan Lee
댓글남기기