App之上下滑動UIScrollview隱藏或者顯示導航欄

lvxiangan發表於2018-04-17

iOS版:

一、好多App都有上下滑動UIScrollview隱藏或者顯示導航欄,在這裡我說說我覺得有用的幾種方法:

1.iOS8之後系統有一個屬性hidesBarsOnSwipe

 Objective-C程式碼如下

[objc] view plain copy
  1. self.navigationController.hidesBarsOnSwipe = YES;  

 swift程式碼如下

[objc] view plain copy
  1. self.navigationController?.hidesBarsOnSwipe = true  

當使用以上程式碼時,可以達到效果

2.使用UIScrollViewDelegate一個代理方法

 Objective-C程式碼如下

[objc] view plain copy
  1. - (void)scrollViewDidScroll:(UIScrollView *)scrollView  
  2. {  
  3.     //scrollView已經有拖拽手勢,直接拿到scrollView的拖拽手勢  
  4.     UIPanGestureRecognizer *pan = scrollView.panGestureRecognizer;  
  5.     //獲取到拖拽的速度 >0 向下拖動 <0 向上拖動  
  6.     CGFloat velocity = [pan velocityInView:scrollView].y;  
  7.       
  8.     if (velocity <- 5) {  
  9.         //向上拖動,隱藏導航欄  
  10.         [self.navigationController setNavigationBarHidden:YES animated:YES];  
  11.     }else if (velocity > 5) {  
  12.         //向下拖動,顯示導航欄  
  13.         [self.navigationController setNavigationBarHidden:NO animated:YES];  
  14.     }else if(velocity == 0){  
  15.         //停止拖拽  
  16.     }  
  17. }  

 swift程式碼如下

[objc] view plain copy
  1. func scrollViewDidScroll(scrollView: UIScrollView) {  
  2.           
  3.         let pan = scrollView.panGestureRecognizer  
  4.         let velocity = pan.velocityInView(scrollView).y  
  5.         if velocity < -5 {  
  6.             self.navigationController?.setNavigationBarHidden(true, animatedtrue)  
  7.         } else if velocity > 5 {  
  8.             self.navigationController?.setNavigationBarHidden(false, animatedtrue)  
  9.         }  
  10.           
  11.     }  

這種效果最好

3.使用UIScrollViewDelegate另一個代理方法

 Objective-C程式碼如下

[objc] view plain copy
  1. - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset</span>  
[objc] view plain copy
  1. {  
  2.     if (velocity.y > 0.0) {  
  3.         [self.navigationController setNavigationBarHidden:YES animated:YES];  
  4.     } else if (velocity.y < 0.0){  
  5.         [self.navigationController setNavigationBarHidden:NO animated:YES];  
  6.     }  
  7. }  

swift程式碼如下

[objc] view plain copy
  1. func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {  
  2.         if velocity.y > 0 {  
  3.             self.navigationController?.setNavigationBarHidden(true, animatedtrue)  
  4.         } else if velocity.y < 0 {  
  5.             self.navigationController?.setNavigationBarHidden(false, animatedtrue)  
  6.         }  
  7.     }  

二、總結:三種方法都可以,我個人覺得第二種方法效果最好,大家可以學習借鑑一下



Android版:

對於移動端,為了留出更多的空間來顯示內容,我們在向上滑動檢視的時候,總是希望,頂部工具欄和底部工具欄可以隨著我們向上滑動的手勢而自動隱藏。而當我們瀏覽完內容,向下滑動檢視的時候,又希望工具欄可以自動顯示出來響應我們操作。這是一種很好的互動模式,現在我們來看看這種互動怎麼實現。

佈局很簡單,上部工具欄和下部工具欄,還有中間的一個scrollView,裡面是一張圖片。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
     tools:context=".MainActivity">

    <include
        android:id="@+id/top_menu"
        layout="@layout/top_menu"/>
    <ScrollView
        android:id="@+id/main_content"
        android:layout_below="@+id/top_menu"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:id="@+id/title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="夏季女裝爆款"
                android:textColor="#ffffff"
                android:background="#000000"
                android:textSize="20sp" />
            <ImageView
                android:id="@+id/photo_pic"
                android:layout_below="@+id/title"
                android:adjustViewBounds="true"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:src="@drawable/clothes"/>
            <RelativeLayout
                android:layout_below="@+id/photo_pic"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:orientation="horizontal">
                <TextView
                    android:id="@+id/item_price"
                    android:layout_width="80dp"
                    android:layout_height="48dp"
                    android:background="#fd4101"
                    android:textSize="20sp"
                    android:textColor="#ffffff"
                    android:gravity="center"
                    android:text="¥987"/>
                <TextView
                    android:padding="5dp"
                    android:layout_toRightOf="@+id/item_price"
                    android:layout_width="match_parent"
                    android:layout_height="48dp"
                    android:textSize="16sp"
                    android:textColor="#333333"
                    android:text="夏季女裝爆款夏季女裝爆款夏季女裝爆款夏季女裝爆款"/>
            </RelativeLayout>
        </RelativeLayout>

    </ScrollView>

    <include
        android:id="@+id/bottom_menu"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        layout="@layout/bottom_menu_buy_now"/>

</RelativeLayout>

其中頂部工具欄和底部工具欄都是一些簡單的佈局,此處只給出頂部工具欄的佈局,具體可以檢視原始碼。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    android:background="#ffffff"
    android:gravity="center_vertical">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >


        <Button
            android:layout_marginLeft="8dp"
            android:id="@+id/nav_search"
            android:layout_alignParentLeft="true"
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:layout_centerInParent="true"
            android:background="@drawable/nav_search_select"/>
       <LinearLayout
           android:layout_toRightOf="@+id/nav_search"
           android:layout_toLeftOf="@+id/nav_message"
           android:layout_width="wrap_content"
           android:layout_height="match_parent"
           android:gravity="center"
           android:layout_centerInParent="true"
           android:orientation="horizontal">
           <TextView
               android:layout_marginLeft="4dp"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:gravity="center"
               android:text="工具欄"
               android:textSize="20sp"
               android:textColor="#262626"/>

       </LinearLayout>
        <Button
            android:id="@+id/nav_message"
            android:layout_marginRight="8dp"
            android:layout_alignParentRight="true"
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:layout_centerInParent="true"
            android:background="@drawable/nav_message_select"/>


    </RelativeLayout>

</RelativeLayout>

這個佈局裡面,頂部工具欄和底部工具欄是重合的。我們可以在onCreate 方法裡設定底部工具欄的 topMargin 的屬性值來讓底部工具欄和底部對齊。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mScrollView = (ScrollView) findViewById(R.id.main_content);
        mTopMenu = (ViewGroup) findViewById(R.id.top_menu);
        mBottomMenu = (ViewGroup) findViewById(R.id.bottom_menu);
        marginTop = DisplayUtil.dip2px(this, TOOLBARHEIGHT);
        mBottomOriginalTopMargin = DisplayUtil.getScreenHeight(this) - DisplayUtil.dip2px(this, TOOLBARHEIGHT) - DisplayUtil.getStatusBarHeight(this);
        ((RelativeLayout.LayoutParams) mBottomMenu.getLayoutParams()).topMargin += mBottomOriginalTopMargin;
        mBottomMenu.requestLayout();
        initView();
    }

這樣,通過DisplayUtil這個工具類計算出來底部工具欄和底部對齊需要的margin值,然後把這個值賦給LayoutParams,然後通知檢視重新佈局就好。

同樣通過改變topMargin值來實現檢視組的向上或者向下運動,實現動畫效果。主要思路就是監聽ScrollView的滑動方法,然後根據上滑或者下滑的距離來相應改變兩個工具欄的topMargin值,initView方法如下。

 private void initView(){
        mScrollView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        mFirstY = (int) event.getY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        mCurrentY = (int) event.getY();
                        if (mCurrentY - mFirstY > 0){
                            direction = true; //向下滑動
                        }else if (mFirstY - mCurrentY > 0){
                            direction = false; //向上滑動
                        }
                        if (!direction){
                            RelativeLayout.LayoutParams top = (RelativeLayout.LayoutParams) mTopMenu.getLayoutParams();
                            RelativeLayout.LayoutParams bottom = (RelativeLayout.LayoutParams) mBottomMenu.getLayoutParams();
                            if (top.topMargin > -marginTop){
                                top.topMargin += mCurrentY - mFirstY;
                                mTopMenu.requestLayout();
                                bottom.topMargin += mFirstY - mCurrentY;
                                mBottomMenu.requestLayout();
                            }else{
                                top.topMargin = -marginTop;
                                mTopMenu.requestLayout();
                                bottom.topMargin = (mBottomOriginalTopMargin + marginTop);
                                break;
                            }

                        }else if (direction){
                            RelativeLayout.LayoutParams top = (RelativeLayout.LayoutParams) mTopMenu.getLayoutParams();
                            RelativeLayout.LayoutParams bottom = (RelativeLayout.LayoutParams) mBottomMenu.getLayoutParams();
                            if (top.topMargin < 0){
                                top.topMargin += mCurrentY - mFirstY;
                                mTopMenu.requestLayout();
                                bottom.topMargin += mFirstY - mCurrentY;
                                mBottomMenu.requestLayout();
                            }
                            else{
                                top.topMargin = 0;
                                mTopMenu.requestLayout();
                                bottom.topMargin = mBottomOriginalTopMargin;
                                mBottomMenu.requestLayout();
                                break;
                            }

                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        break;
                }
                return false;
            }
        });
    }

最後的效果,如下所示。 
這裡寫圖片描述

原始碼地址:https://github.com/futureshine/AutoHideToolBar



相關文章