티스토리 뷰

반응형
SMALL



https://jihunstudy.tistory.com/20?category=807405


기본적인 DrawerLayout 에서  + 전체 화면 + RecyclerView 가로 스크롤 Intercept 조건.





<?xml version="1.0" encoding="utf-8"?>
<com.example.customdrawerlayout.CustomView.DrawerLayoutHorizontalSupport
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
 
    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:gravity="center">
 
        <Button
                android:id="@+id/btn_menu"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="openMenu"/>
 
        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="테스트 입니다."/>
 
    </LinearLayout>
 
    <com.example.customdrawerlayout.CustomView.MenuView
            android:id="@+id/menu_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="left"
            android:background="#FF0000">
 
    </com.example.customdrawerlayout.CustomView.MenuView>
 
</com.example.customdrawerlayout.CustomView.DrawerLayoutHorizontalSupport>
cs



class MenuView @JvmOverloads constructor (
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
 
    init {
        LayoutInflater.from(context).inflate(R.layout.view_menu, thistrue)
        setList()
    }
 
    private fun setList() {
        list.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
        list.addItemDecoration(PaddingItemDecoration(context, 1010))
        list.adapter = ListAdapter()
    }
}
cs







class DrawerLayoutHorizontalSupport @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : DrawerLayout(context, attrs, defStyleAttr) {
 
    private var mRecyclerView: RecyclerView? = null
    private var mNavigationView: LinearLayout? = null
 
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
 
        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
            throw IllegalArgumentException(
                "DrawerLayout must be measured with MeasureSpec.EXACTLY."
            )
        }
 
        setMeasuredDimension(widthSize, heightSize)
 
        // Gravity value for each drawer we've seen. Only one of each permitted.
        val foundDrawers = 0
        val childCount = childCount
        for (i in 0 until childCount) {
            val child = getChildAt(i)
 
            if (child.visibility == View.GONE) {
                continue
            }
 
            val lp = child.layoutParams as LayoutParams
 
            if (isContentView(child)) {
                // Content views get measured at exactly the layout's size.
                val contentWidthSpec = MeasureSpec.makeMeasureSpec(
                    widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY
                )
                val contentHeightSpec = MeasureSpec.makeMeasureSpec(
                    heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY
                )
                child.measure(contentWidthSpec, contentHeightSpec)
            } else if (isDrawerView(child)) {
                val childGravity = getDrawerViewGravity(child) and Gravity.HORIZONTAL_GRAVITY_MASK
                if (foundDrawers and childGravity != 0) {
                    throw IllegalStateException(
                        "Child drawer has absolute gravity " +
                                gravityToString(childGravity) + " but this already has a " +
                                "drawer view along that edge"
                    )
                }
                val drawerWidthSpec = ViewGroup.getChildMeasureSpec(
                    widthMeasureSpec,
                    MIN_DRAWER_MARGIN + lp.leftMargin + lp.rightMargin,
                    lp.width
                )
                val drawerHeightSpec = ViewGroup.getChildMeasureSpec(
                    heightMeasureSpec,
                    lp.topMargin + lp.bottomMargin,
                    lp.height
                )
                child.measure(drawerWidthSpec, drawerHeightSpec)
            } else {
                throw IllegalStateException(
                    "Child " + child + " at index " + i +
                            " does not have a valid layout_gravity - must be Gravity.LEFT, " +
                            "Gravity.RIGHT or Gravity.NO_GRAVITY"
                )
            }
        }
    }
 
    private fun isContentView(child: View): Boolean {
        return (child.layoutParams as LayoutParams).gravity == Gravity.NO_GRAVITY
    }
 
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    internal fun isDrawerView(child: View): Boolean {
        val gravity = (child.layoutParams as LayoutParams).gravity
        val absGravity = Gravity.getAbsoluteGravity(
            gravity,
            child.layoutDirection
        )
        return absGravity and (Gravity.LEFT or Gravity.RIGHT) != 0
    }
 
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    internal fun getDrawerViewGravity(drawerView: View): Int {
        val gravity = (drawerView.layoutParams as LayoutParams).gravity
        return Gravity.getAbsoluteGravity(gravity, drawerView.layoutDirection)
    }
 
    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        mRecyclerView?.let { mRv ->
            Log.d(
                "onInterceptTouchEvent",
                (mRv.layoutManager as LinearLayoutManager).findLastVisibleItemPosition().toString() + ""
            )
            mNavigationView?.let { mNavi ->
                return if (isInside(ev, mRv) && isDrawerOpen(mNavi)) {
                    if ((mRv.layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
                        == (mRv.adapter?.itemCount ?: 1- 1return super.onInterceptTouchEvent(ev) else false
                } else super.onInterceptTouchEvent(ev)
            }
        }
 
        return super.onInterceptTouchEvent(ev)
 
    }
 
    private fun isInside(ev: MotionEvent, mRv: RecyclerView): Boolean { //check whether user touch recylerView or not
 
        return ev.x >= mRv.left && ev.x <= mRv.right &&
                ev.y >= mRv.top && ev.y <= mRv.bottom
    }
 
    operator fun set(navigationView: LinearLayout, recyclerView: RecyclerView) {
        mRecyclerView = recyclerView
        mNavigationView = navigationView
    }
 
    companion object {
 
        private val MIN_DRAWER_MARGIN = 0 // dp
 
        internal fun gravityToString(gravity: Int): String {
            if ((gravity and Gravity.LEFT) == Gravity.LEFT) {
                return "LEFT"
            }
            return if ((gravity and Gravity.RIGHT) == Gravity.RIGHT) {
                "RIGHT"
            } else Integer.toHexString(gravity)
        }
    }
 
}
 
cs



아래 코드 참고


Custom 된 DrawerLayout 의 문제점이라고 한다면 


가로 스크롤 되는 RecyclerView 가 있다고 가정 했을 때, findLastVisibleItemPosition() 으로 체크를 해서 그런지, 스크롤 중 마지막 아이템이 조금이라도 보인 상태에서 


손을 떼고 다시 스크롤을 시도 할 때 TouchEvent 를 DrawerLayout 에 던져 주기 때문에 DrawerLayout 이 닫힌다.

" 스크롤이 완벽하게 끝으로 도달 했을 때 " 를 체크 해야 될 것으로 보인다.



override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        mRecyclerView?.let { mRv ->
            Log.d(
                "onInterceptTouchEvent",
                (mRv.layoutManager as LinearLayoutManager).findLastVisibleItemPosition().toString() + ""
            )
            mNavigationView?.let { mNavi ->
                return if (isInside(ev, mRv) && isDrawerOpen(mNavi)) {
                    if ((mRv.layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
                        == (mRv.adapter?.itemCount ?: 1- 1return super.onInterceptTouchEvent(ev) else false
                } else super.onInterceptTouchEvent(ev)
            }
        }
        return super.onInterceptTouchEvent(ev)
    }
cs



위 사진 처럼 마지막 아이템이 반만 보이는 상태 임에도 불구 하고 메뉴가 닫히는 현상이 나옴.

반응형
LIST
댓글