Android 高仿 QQ5.0 側滑菜單效果 自定義控件來襲

jopen 8年前發布 | 12K 次閱讀 Android開發 移動開發

轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/39257409,本文出自【張鴻洋的博客】

上一篇博客帶大家實現了:Android 自定義控件打造史上最簡單的側滑菜單 ,有兄弟看了以后說,你這滑動菜單過時了呀~QQ5.0的效果還不錯~~嗯,的確,上一篇也承諾過,稍微修改上一篇的代碼,實現QQ5.0側滑菜單~~好了,下面就開始為大家展示寫一個類QQ的側滑有多easy ~!

1、原理分析

首先對比一下我們上篇的實現距離QQ的效果還有多遠:

差距還是蠻大的

區別1、QQ的內容區域會伴隨菜單的出現而縮小

區別2、QQ的側滑菜單給人的感覺是隱藏在內容的后面,而不是拖出來的感覺

區別3、QQ的側滑菜單有一個縮放以及透明度的效果~

 

那么我們如何能做到呢:

對于區別1:這個好辦,我們可以在滑動的時候,不斷的改變內容區域的大小;如何改變呢?我們在菜單出現的整個過程中,不斷記錄菜單顯示的寬度與其總寬度的比值,是個從0到1的過程,然后把0~1轉化為1~0.7(假設內容區域縮小至0.7);不斷去縮小內容區域;

對于區別3:也比較好辦,上面已經可以得到0到1的這個值了,那么縮放和透明度的動畫就不在話下了;

對于區別2:我們使用的HorizontalScrollView,然后水平放置了菜單和內容,如何讓菜單可以隱藏到內容的后面呢?其實也比較簡單,在菜單出現的過程中,不斷設置菜單的x方向的偏移量;0的時候完全隱藏,0.3的時候,隱藏x方向偏移量為0.7個寬度,類推~~~

 

好了,分析完畢,那么對于這些動畫用什么實現最好呢?

想都不用想,屬性動畫哈,如果你對屬性動畫不了解,可以參:Android 屬性動畫(Property Animation) 完全解析 (上)Android 屬性動畫(Property Animation) 完全解析 (下)

2、實現


1、初步的代碼

布局文件神馬的,都和上一篇一模一樣,這里就不重復貼代碼了,不了解的,先看下上一篇;

先看一下上一篇我們已經實現的完整代碼:

package com.example.zhy_slidingmenu;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

import com.zhy.utils.ScreenUtils;

public class SlidingMenu extends HorizontalScrollView
{
    /**
     * 屏幕寬度
     */
    private int mScreenWidth;
    /**
     * dp
     */
    private int mMenuRightPadding;
    /**
     * 菜單的寬度
     */
    private int mMenuWidth;
    private int mHalfMenuWidth;

    private boolean isOpen;

    private boolean once;

    public SlidingMenu(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);

    }

    public SlidingMenu(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        mScreenWidth = ScreenUtils.getScreenWidth(context);

        TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.SlidingMenu, defStyle, 0);
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++)
        {
            int attr = a.getIndex(i);
            switch (attr)
            {
            case R.styleable.SlidingMenu_rightPadding:
                // 默認50
                mMenuRightPadding = a.getDimensionPixelSize(attr,
                        (int) TypedValue.applyDimension(
                                TypedValue.COMPLEX_UNIT_DIP, 50f,
                                getResources().getDisplayMetrics()));// 默認為10DP
                break;
            }
        }
        a.recycle();
    }

    public SlidingMenu(Context context)
    {
        this(context, null, 0);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        /**
         * 顯示的設置一個寬度
         */
        if (!once)
        {
            LinearLayout wrapper = (LinearLayout) getChildAt(0);
            ViewGroup menu = (ViewGroup) wrapper.getChildAt(0);
            ViewGroup content = (ViewGroup) wrapper.getChildAt(1);

            mMenuWidth = mScreenWidth - mMenuRightPadding;
            mHalfMenuWidth = mMenuWidth / 2;
            menu.getLayoutParams().width = mMenuWidth;
            content.getLayoutParams().width = mScreenWidth;

        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        super.onLayout(changed, l, t, r, b);
        if (changed)
        {
            // 將菜單隱藏
            this.scrollTo(mMenuWidth, 0);
            once = true;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev)
    {
        int action = ev.getAction();
        switch (action)
        {
        // Up時,進行判斷,如果顯示區域大于菜單寬度一半則完全顯示,否則隱藏
        case MotionEvent.ACTION_UP:
            int scrollX = getScrollX();
            if (scrollX > mHalfMenuWidth)
            {
                this.smoothScrollTo(mMenuWidth, 0);
                isOpen = false;
            } else
            {
                this.smoothScrollTo(0, 0);
                isOpen = true;
            }
            return true;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 打開菜單
     */
    public void openMenu()
    {
        if (isOpen)
            return;
        this.smoothScrollTo(0, 0);
        isOpen = true;
    }

    /**
     * 關閉菜單
     */
    public void closeMenu()
    {
        if (isOpen)
        {
            this.smoothScrollTo(mMenuWidth, 0);
            isOpen = false;
        }
    }

    /**
     * 切換菜單狀態
     */
    public void toggle()
    {
        if (isOpen)
        {
            closeMenu();
        } else
        {
            openMenu();
        }
    }



}

利用HorizontalScrollView,監聽了ACTION_UP的事件,當用戶抬起手指時,根據當前菜單顯示的寬度值,判斷是縮回還是完全展開;給用戶提供了一個rightPadding屬性,用于設置菜單離右屏幕的距離;以及對外提供了打開,關閉,切換的幾個方法;具體的講解看下上篇博客了;

2、實現的思路

現在我們開始解決那3個區別,已經選擇了使用屬性動畫,現在決定動畫效果應該加在哪兒?

不用說,我用大腿想一想都應該是在ACTION_MOVE中,是的,ACTION_MOVE中的確可以,不斷獲取當前的getScrollX / mMenuWidth,不斷改變菜單的透明度,縮放,X方向的偏移量;不斷改變內容區域的寬度和高度;

說一下,起初我也是在MOVE中這么做的,但是呢,出現兩個問題:

1、動畫效果并不是很流暢,特別是菜單,有抖動的效果;

2、用戶抬起后,還需要在UP里面,繼續未完成的動畫,就是說,你的透明度、縮放神馬的,當用戶抬起以后就需要自動變化了;

于是乎,我就開始換了個方向,既然是SrollView,肯定有一個ScrollChanged方法,功夫不負有心人,真心這么個方法:

@Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt)
    {

    }
這個方法只要scrollChanged就會觸發,l就是我們需要的scrollX,太贊了~~~

3、動畫比例的計算

我們在onScrollChanged里面,拿到 l 也就是個getScrollX,即菜單已經顯示的寬度值;

float scale = l * 1.0f / mMenuWidth;

與菜單的寬度做除法運算,在菜單隱藏到顯示整個過程,會得到1.0~0.0這么個變化的區間;

有了這個區間,就可以根據這個區間設置動畫了;

1、首先是內容區域的縮放比例計算:

我們準備讓在菜單出現的過程中,讓內容區域從1.0~0.8進行變化~~

那么怎么把1.0~0.0轉化為1.0~0.8呢,其實很簡單了:

float rightScale = 0.8f + scale * 0.2f; (scale 從1到0 ),是不是哦了~

接下來還有3個動畫:

2、菜單的縮放比例計算

仔細觀察了下QQ,菜單大概縮放變化是0.7~1.0

float leftScale = 1 - 0.3f * scale;

3、菜單的透明度比例:

我們設置為0.6~1.0;即:0.6f + 0.4f * (1 - scale)

4、菜單的x方向偏移量:

看一下QQ,并非完全從被內容區域覆蓋,還是有一點拖出的感覺,所以我們的偏移量這么設置:

tranlateX = mMenuWidth * scale * 0.6f ;剛開始還是讓它隱藏一點點~~~

4、完整的實現

說了這么多,其實到上一篇史上最簡單的側滑,到QQ5.0的效果的轉變,只需要幾行代碼~~

@Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt)
    {
        super.onScrollChanged(l, t, oldl, oldt);
        float scale = l * 1.0f / mMenuWidth;
        float leftScale = 1 - 0.3f * scale;
        float rightScale = 0.8f + scale * 0.2f;

        ViewHelper.setScaleX(mMenu, leftScale);
        ViewHelper.setScaleY(mMenu, leftScale);
        ViewHelper.setAlpha(mMenu, 0.6f + 0.4f * (1 - scale));
        ViewHelper.setTranslationX(mMenu, mMenuWidth * scale * 0.6f);

        ViewHelper.setPivotX(mContent, 0);
        ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
        ViewHelper.setScaleX(mContent, rightScale);
        ViewHelper.setScaleY(mContent, rightScale);

    }

就這么幾行。這里屬性動畫用的nineoldandroids為了保持向下的兼容;主要就是設置了各種動畫,上面都詳細說了~~~
然后,記得把我們的菜單和內容的布局,單獨聲明出來為我們的mMenu ,mContent ~沒了,就改了這么幾行~

3、效果圖

是騾子是馬,拉出來溜溜

菜單欄需要ListView的拖動也是不會沖突了,上篇已經測試了;

關于動畫屬性的范圍:上面介紹的特別清楚,比如內容我們是最小顯示0.8,你要是喜歡0.6,自己去修改一下;包括偏移量,透明度等范圍;

因為上一篇已經寫了如何把屬性抽取成自定義的屬性;所以這里就沒有抽取了,不然總覺得是在重復~

 

嗯,最近還有寫APP的側滑,是這樣的,就是菜單欄完全隱藏在內容區域下面,如果需要這樣需求的:

其實我還滿喜歡這樣效果的。

實現呢,注釋幾行代碼就實現了:

@Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt)
    {
        super.onScrollChanged(l, t, oldl, oldt);
        float scale = l * 1.0f / mMenuWidth;
//      float leftScale = 1 - 0.3f * scale;
//      float rightScale = 0.8f + scale * 0.2f;
//      
//      ViewHelper.setScaleX(mMenu, leftScale);
//      ViewHelper.setScaleY(mMenu, leftScale);
//      ViewHelper.setAlpha(mMenu, 0.6f + 0.4f * (1 - scale));
        ViewHelper.setTranslationX(mMenu, mMenuWidth * scale );

//      ViewHelper.setPivotX(mContent, 0);
//      ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
//      ViewHelper.setScaleX(mContent, rightScale);
//      ViewHelper.setScaleY(mContent, rightScale);

    }


好了,雖說最終的實現看起來還是很簡單的,看起來,嗯~~但是從無到有的這個過程還是不容易的~~各種嘗試,我能說我連蹲坑都在滑QQ的菜單觀察么~哈,見笑了;博客中也寫出了過程中失敗的嘗試,希望能夠更好的讓大家在里面學到些有用的東西~~YEAH!! 就到這,沒事就留個言~~~再不留言,我就來個源碼請留下郵箱,嘿嘿,開個玩笑~

 

 

 

源碼點擊下載

 

最后,我建了一個QQ群,方便大家交流。群號:55032675

 

 

---------------------------------------------------------------------------------------------------------------------------------------

本博客內容視頻版,已經上線,如果你不喜歡枯燥的文本,請猛戳:

1、高仿微信5.2.1主界面及消息提醒

2、高仿QQ5.0側滑

3、Android智能機器人“小慕”的實現

4、Android自定義控件 打造Android流式布局和熱門標簽



 

來自: http://blog.csdn.net//lmj623565791/article/details/39257409

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!