반응형

 

 

옥션이나 지마켓을 보면 쇼핑몰들은 다이나믹한 레이아웃을 사용해 사용자들의 시선을 끄는 어플이 대세인것 같다.

최근에 사업팀이 이쪽에 꼿혀서 만들어달라고 제안이 들어왔는데 그때 해본걸 정리하려고한다.

 

 

상단동작

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:tools="http://schemas.android.com/tools"
    android:animateLayoutChanges="true">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/top_app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.MaterialComponents.ActionBar"
        android:background="@color/colorPrimary"
        app:elevation="0dp">
        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways|snap">

            <androidx.appcompat.widget.Toolbar
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize" />
        </com.google.android.material.appbar.CollapsingToolbarLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="75dp"
            android:background="@color/dark_gray"/>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/main_recycle_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
        app:layout_anchor="@id/main_bottom_navbar"
        app:layout_anchorGravity="top" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/main_bottom_navbar"
        android:layout_width="match_parent"
        android:layout_height="54dp"
        android:layout_gravity="bottom"
        app:labelVisibilityMode="labeled"
         app:itemIconTint="@color/bottom_nav_color"
        app:itemTextColor="@color/bottom_nav_color"
        app:menu="@menu/bottom_nav_bar" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

- 구글이 빠른 개발속도를 위해 편리한 위젯을 많이 만들어 놓고있다 그중에서도 가장많이 쓰이는것이 AppbarLayout,collapsingToolbarLayout,Toolbar조합일 것이다.

 

- 꼭 coordinatorLayout안쪽에 구성을 해야한다. 그이유는 리사이클뷰에

app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"

layout_behavior 속성을 사용하기위해서이다.

 

- 눈여겨 봐야할 부분은 첫번째로 collapsingToolbarLayout에

app:layout_scrollFlags="scroll|enterAlways|snap"

이부분이다. 레이아웃이 반쯤 들어가면 자동으로 사라지고 아니면 자동으로 이전 레이아웃으로 복구가된다.

 

-두번째는 recyclerView에 

app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"

이부분이다 . 이부분의 터치에따라 appbar와 bottomNavigationbar가 보여지고 사라진다.

 

하단동작

public class TopNavbarBehavior extends CoordinatorLayout.Behavior<View> {
    private static final String TAG = "BottomNavBehavior";
    private float originHeight;
    private Context mContext;
    private Activity mActivity;
    private float endTransitionY;

    public TopNavbarBehavior() {
    }

    public TopNavbarBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
    }


    public TopNavbarBehavior(Context context){
        this.mContext = context;
    }


    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
        originHeight = Math.max(0f, Math.min(Float.parseFloat(String.valueOf(child.getHeight())), child.getMeasuredHeight()));
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL;//수직일때 작동
    }

    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        //Behavior을 가진 View(child)
        //setTranslationY: 상단 위치를 기준으로 수직 위치를 설정합니다.
        //getTranslationY: 상단 위치를 기준으로 하고 Layout에 배치된 위치에 추가하여 개체의 위치를 효과적으로 지정할 수 있습니다.
        //dyConsumed: 타겟의 스크롤 조작으로 인해 소비된 수직 픽셀
        float transitionY = Math.max(0f, Math.min(Float.parseFloat(String.valueOf(child.getHeight())), child.getTranslationY() + dy));
        child.setTranslationY(transitionY);
        endTransitionY = transitionY;
    }

    @Override
    public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int type) {

        //반이거나 반이상이면 숨기기
        if ((originHeight/2) >= endTransitionY ) {
            child.setTranslationY(0);
        }
        //반이하면 보이기
        else if ((originHeight/2) < endTransitionY ){
            child.setTranslationY(originHeight);
        }
    }
}

-coordinatorLayout을 사용하는 이유가 여기서 하나더있다. 

 

CoordinatorLayout.Behavior<View>

이녀석을 상속받기위해 사용한다. 

 

 

 

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
        originHeight = Math.max(0f, Math.min(Float.parseFloat(String.valueOf(child.getHeight())), child.getMeasuredHeight()));
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL;//수직일때 작동
    }

- 현재위치를 초기화시켜주고, 방향을 결정한다.

axes == 이부분은 onNestedPreScroll,onStopNestedScroll을 작동시킬껀지 결정해주는 부분이다.

 

 

 

    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        //Behavior을 가진 View(child)
        //setTranslationY: 상단 위치를 기준으로 수직 위치를 설정합니다.
        //getTranslationY: 상단 위치를 기준으로 하고 Layout에 배치된 위치에 추가하여 개체의 위치를 효과적으로 지정할 수 있습니다.
        //dyConsumed: 타겟의 스크롤 조작으로 인해 소비된 수직 픽셀
        float transitionY = Math.max(0f, Math.min(Float.parseFloat(String.valueOf(child.getHeight())), child.getTranslationY() + dy));
        child.setTranslationY(transitionY);
        endTransitionY = transitionY;
    }

- 솔직히 수학을 못해서 무슨원리로 작동하는지는 이해가 안되지만 위에 주석을 보고 어렴풋히 알것도 같다.

 

 

    @Override
    public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int type) {

        //반이거나 반이상이면 숨기기
        if ((originHeight/2) >= endTransitionY ) {
            child.setTranslationY(0);
        }
        //반이하면 보이기
        else if ((originHeight/2) < endTransitionY ){
            child.setTranslationY(originHeight);
        }
    }

- 상단동작부분에

app:layout_scrollFlags="scroll|enterAlways|snap"

이런 부분이 있엇을것이다. 위에서 설명했듯이 손가락으로 드래그해서 사라진 레이아웃이 반이상이면 부드럽게 사라지고 아니면 보이게되는 부분인데 아래쪽도 아주 똑같지는 않지만 비슷하게 동작하도록 계산을해서 보이고 숨기고를 구성해보았다.

 

 

 

 

조립하기

public class MainActivity extends AppCompatActivity {

    BottomNavigationView bottomNavbar;
    AppBarLayout toolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //리사클뷰
        RecyclerView rv =  findViewById(R.id.main_recycle_view);
        MainRecyclerview adapter = new MainRecyclerview(this);
        LinearLayoutManager lm = new LinearLayoutManager(this);
        rv.setLayoutManager(lm);
        rv.setAdapter(adapter);

        //하단 네비게이션바
        bottomNavbar = findViewById(R.id.main_bottom_navbar);
        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) bottomNavbar.getLayoutParams();
        BottomNavBehavior bnb = new BottomNavBehavior(this);
        params.setBehavior(bnb);

    }
}

 

스크린샷은 추후에 업로드

 

언제나 고수분들 태클은 환영입니다. 

 

감사합니다.

 

반응형

+ Recent posts