Android精仿淘寶/QQ空間標題欄漸變效果

聲明:本篇文章已授權微信公衆號 YYGeeker 獨家發佈

前言

最近在淘寶上囤年貨買買買的時候,注意到淘寶購物詳情頁的漸變效果,覺得效果還是挺不錯的,有種似曾相識的感覺,沒錯,好像QQ空間的標題欄也是類似的做法。鑑於這種效果在平時可能會用得比較多,所以就自己研究了一下然後把它實現出來了,項目完整demo在文章底部有鏈接,大家可下載參考研究。文章原創,轉載請註明地址:小嵩的CSDN博客,地址:http://blog.csdn.net/qq_22393017

正文

按照慣例,先上一張要實現的效果圖吧,有興趣的小夥伴可以繼續往下看。
淘寶APP的實際效果

淘寶購物詳情頁GIF

自己實現的效果:

淘寶標題漸變效果GIF圖
(文章末尾有demo下載地址,可下載demo查看完整源代碼)

主要功能點

本文會詳細地講解這個功能的實現思路,主要有以下四個功能點:

  1. 滑動時標題欄透明度漸變。
  2. 標題欄的幾個ICON控件在滑動時背景漸變
  3. 在標題欄有透明度時,ICON是白色的;標題欄變成不透明時,ICON替換成灰色。
  4. 頭部圖片滑動視差效果

以上這四個功能點都是通過手指的滑動距離來進行處理的。那麼首先,我們就需要監聽獲取滑動距離,那麼怎樣獲取這個滑動距離並進行處理呢?不急,咱們慢慢思考,一步一步來。淘寶這個頁面的內容部分,應該是用的ScrollView,而原生的ScrollView,它沒有提供onScrollChanged的回調方法。所以我們有兩種方式:

  • 一種是通過自定義View,讓其包含ScrollView子控件。
  • 一種是自定義一個ScrollView,重寫onScrollChanged方法。

第一種方式可以參考Android–仿淘寶商品詳情(繼續拖動查看詳情)及標題欄漸變 這篇博客,這篇博客由於寫了Scrollview滑動到底部,繼續拖動查看詳情的動能,所以我就不重複寫了,可參考它的實現方式。

這篇文章我們換第二種方式,即自定義Scrollview來實現標題欄漸變。onScrollChanged(int x, int y, int oldx, int oldy)方法有x,y,oldx,oldy四個參數,x,y分別代表着本次滑動時,控件距離原來位置(原點)的X軸、Y軸的距離;oldx,oldy分別代表上一次觸發該方法時,控件距離原來位置(原點)的x,y軸距離。由於我們只需要監聽Y軸方向,所以只需要傳遞Y軸方向的參數。所以我們採用接口回調的方式,定義一個接口,在onScrollChanged方法中將滑動的豎直距離y 以及手指滑動方向:isUp利用接口回調到Activity去。

實現步驟

Step1: 自定義ScrollView

package com.titlebargradient.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;

/**
 * TODO<自定義監聽滑動的ScrollView>
 *
 * @author: 小嵩
 * @date: 2017/1/9 11:37
 */

public class ObservableScrollView extends ScrollView {

    private ScrollViewListener scrollViewListener = null;

    public ObservableScrollView(Context context) {
        super(context);
    }

    public ObservableScrollView(Context context, AttributeSet attrs,
                                int defStyle) {
        super(context, attrs, defStyle);
    }

    public ObservableScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setOnScrollListener(ScrollViewListener scrollViewListener) {
        this.scrollViewListener = scrollViewListener;
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldx, int oldy) {
        super.onScrollChanged(x, y, oldx, oldy);
        if (scrollViewListener != null) {

            if (oldy < y ) {// 手指向上滑動,屏幕內容下滑
                scrollViewListener.onScroll(oldy,y,false);

            } else if (oldy > y ) {// 手指向下滑動,屏幕內容上滑
                scrollViewListener.onScroll(oldy,y,true);
            }

        }
    }

    public  interface ScrollViewListener{//dy Y軸滑動距離,isUp 是否返回頂部
         void onScroll(int oldy,int dy,boolean isUp);
    }
}

Step2. 佈局文件引用自定義的Scrollview

控件路徑替換成自己項目自定義ScrollView的路徑

  <com.titlebargradient.widget.ObservableScrollView
            android:id="@+id/scrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
  </com.titlebargradient.widget.ObservableScrollView>

Step3. xml頁面佈局編寫

完整XML佈局代碼如下:

activity_scrollview.xml

<?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="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.titlebargradient.widget.ObservableScrollView
            android:id="@+id/scrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:paddingTop="250dp">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:padding="8dp"
                    android:textSize="20sp"
                    android:lineSpacingExtra="10dp"
                    android:text="@string/TextContent"
                    android:gravity="center"/>
            </LinearLayout>
        </com.titlebargradient.widget.ObservableScrollView>
    </LinearLayout>


    <LinearLayout
        android:id="@+id/lv_header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/iv_header"
            android:layout_width="match_parent"
            android:layout_height="@dimen/head_height"
            android:scaleType="centerCrop"
            android:layout_gravity="center"
            android:src="@mipmap/bg_header"/>
    </LinearLayout>

    <include layout="@layout/layout_toolbar"/>

    <LinearLayout
        android:id="@+id/lv_bottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_alignParentBottom="true">
        
        <include layout="@layout/layout_bottom"/>
    </LinearLayout>

</RelativeLayout>

activity_scrollview.xml中引用的layout_toolbar.xml 標題欄:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/title_height"
        android:background="@color/color_transparent">

        <!--返回按鈕-->
        <ImageView
            android:id="@+id/iv_back"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:padding="8dp"
            android:src="@mipmap/ic_back"
            android:background="@drawable/bg_circle"/>

        <ImageView
            android:id="@+id/iv_more"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:padding="10dp"
            android:src="@mipmap/ic_more"
            android:layout_gravity="right"
            android:layout_marginRight="8dp"
            android:background="@drawable/bg_circle"/>

        <ImageView
            android:id="@+id/iv_shopping_cart"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:padding="8dp"
            android:src="@mipmap/ic_shopping_cart"
            android:layout_gravity="right"
            android:layout_marginRight="8dp"
            android:background="@drawable/bg_circle"/>

    </android.support.v7.widget.Toolbar>

    <View
        android:id="@+id/spite_line"
        android:layout_width="match_parent"
        android:layout_height="0.8dp"
        android:background="@color/color_light_gray"
        android:visibility="gone"/>

</LinearLayout>

所引用的layout_bottom.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

        <TextView
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:background="@drawable/bg_square"
            android:gravity="center"
            android:padding="8dp"
            android:layout_gravity="center"
            android:drawableTop="@mipmap/ic_help"
            android:drawablePadding="2dp"
            android:text="客服"
            android:textSize="11sp" />

        <TextView
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:background="@drawable/bg_square"
            android:gravity="center"
            android:padding="8dp"
            android:layout_gravity="center"
            android:drawableTop="@mipmap/ic_market"
            android:drawablePadding="2dp"
            android:text="店鋪"
            android:textSize="11sp"/>

        <TextView
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:background="@drawable/bg_square"
            android:gravity="center"
            android:padding="8dp"
            android:layout_gravity="center"
            android:drawableTop="@mipmap/ic_collection"
            android:drawablePadding="2dp"
            android:text="收藏"
            android:textSize="11sp"/>


        <TextView
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="48dp"
            android:background="@color/color_orange"
            android:gravity="center"
            android:text="加入購物車"
            android:textColor="@android:color/white"/>

        <TextView
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="48dp"
            android:background="@color/color_red"
            android:gravity="center"
            android:text="立即購買"
            android:textColor="@android:color/white"/>

</LinearLayout>

其中,ICON 是從iconfont官網下載的,可自行搜索網站鏈接:阿里巴巴矢量圖庫

到這裏我們的佈局就已經寫好了,接下來就是在Activity中監聽滑動事件,然後實現漸變效果。

Step4. 監聽滑動距離

在Activity中,初始化自定義Scrollview並調用接口,實現對滑動距離的監聽**

部分關鍵代碼如下,完整代碼可下載demo進行研究:

 //獲取dimen屬性中 標題和頭部圖片的高度
        final float title_height = getResources().getDimension(R.dimen.title_height);
        final float head_height = getResources().getDimension(R.dimen.head_height);

        //滑動事件回調監聽(一次滑動的過程一般會連續觸發多次)
        scrollView.setOnScrollListener(new ObservableScrollView.ScrollViewListener() {
            @Override
            public void onScroll(int oldy, int dy, boolean isUp) {
               /* DensityUtil Density = new DensityUtil();
                int mHeaderHeight_px = Density.dip2px(ScrollViewActivity.this, 200.0f);*/

                float move_distance = head_height - title_height;
                if (!isUp && dy <= move_distance) {//手指往上滑,距離未超過200dp
                    //標題欄逐漸從透明變成不透明
                    toolbar.setBackgroundColor(ContextCompat.getColor(ScrollViewActivity.this, R.color.color_white));
                    TitleAlphaChange(dy, move_distance);//標題欄漸變
                    HeaderTranslate(dy);//圖片視差平移

                } else if (!isUp && dy > move_distance) {//手指往上滑,距離超過200dp
                    TitleAlphaChange(1, 1);//設置不透明百分比爲100%,防止因滑動速度過快,導致距離超過200dp,而標題欄透明度卻還沒變成完全不透的情況。

                    HeaderTranslate(head_height);//這裏也設置平移,是因爲不設置的話,如果滑動速度過快,會導致圖片沒有完全隱藏。

                    ivBack.setImageResource(R.mipmap.ic_back_dark);
                    ivMore.setImageResource(R.mipmap.ic_more_dark);
                    ivShoppingCart.setImageResource(R.mipmap.ic_shopping_dark);
                    spiteLine.setVisibility(View.VISIBLE);

                } else if (isUp && dy > move_distance) {//返回頂部,但距離頭部位置大於200dp
                    //不做處理

                } else if (isUp && dy <= move_distance) {//返回頂部,但距離頭部位置小於200dp
                    //標題欄逐漸從不透明變成透明
                    TitleAlphaChange(dy, move_distance);//標題欄漸變
                    HeaderTranslate(dy);//圖片視差平移

                    ivBack.setImageResource(R.mipmap.ic_back);
                    ivMore.setImageResource(R.mipmap.ic_more);
                    ivShoppingCart.setImageResource(R.mipmap.ic_shopping_cart);
                    spiteLine.setVisibility(View.GONE);
                }
            }
        });

Step5. 設置標題欄透明度變化:

 private void TitleAlphaChange(int dy, float mHeaderHeight_px) {//設置標題欄透明度變化
        float percent = (float) Math.abs(dy) / Math.abs(mHeaderHeight_px);
        //如果是設置背景透明度,則傳入的參數是int類型,取值範圍0-255
        //如果是設置控件透明度,傳入的參數是float類型,取值範圍0.0-1.0
        //這裏我們是設置背景透明度就好,因爲設置控件透明度的話,返回ICON等也會變成透明的。
        //alpha 值越小越透明
        int alpha = (int) (percent * 255);
        toolbar.getBackground().setAlpha(alpha);//設置控件背景的透明度,傳入int類型的參數(範圍0~255)

        ivBack.getBackground().setAlpha(255 - alpha);
        ivMore.getBackground().setAlpha(255 - alpha);
        ivShoppingCart.getBackground().setAlpha(255 - alpha);
    }

圖片滑動視差效果:

private void HeaderTranslate(float distance) {
        lvHeader.setTranslationY(-distance);
        ivHeader.setTranslationY(distance/2);
    }

結尾傳送門

GitHub項目地址:TitlebarGradient-淘寶購物詳情頁標題欄漸變。

仿知乎、美團效果的可參考我另外一篇博客:http://blog.csdn.net/qq_22393017/article/details/54377603

歡迎交流討論、有問題也非常歡迎指出來,覺得不錯的話請Star一下哦,大家的支持將會讓人更有動力繼續分享~

發佈了40 篇原創文章 · 獲贊 126 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章