側(cè)滑菜單+圓形揭示動(dòng)畫

前言

由于工作需要,小編對(duì)最近十分炫酷的android動(dòng)畫效果進(jìn)行了一波瘋狂搜集。經(jīng)過(guò)一下午的努力,小編終于成功的將大部分好看的動(dòng)畫瀏覽了一遍,差點(diǎn)閃瞎我的24k鈦合金狗眼。所以,小編本著渴望裝逼的心情,將好的動(dòng)畫的實(shí)現(xiàn)原理,所使用的框架一個(gè)一個(gè)的整理出來(lái)。逼王之路,指日可待?。?!

正文

今天小編帶給各位大佬的是側(cè)滑菜單+圓形揭示動(dòng)畫的實(shí)現(xiàn)。廢話少說(shuō),看效果先。


side-menu.jpg

怎么樣?是不是很炫酷?下面我們就一起來(lái)看看這個(gè)效果是如何實(shí)現(xiàn)的?

動(dòng)畫實(shí)現(xiàn)方式

點(diǎn)擊demo地址獲得實(shí)現(xiàn)代碼。
關(guān)于動(dòng)畫實(shí)現(xiàn)其實(shí)十分簡(jiǎn)單。

  • 導(dǎo)入庫(kù):
    在文件的build.gradle文件中添加以下內(nèi)容:
repositories {
    maven {
        url "https://jitpack.io"
    }
}

dependencies {
    compile 'com.github.ozodrukh:CircularReveal:1.0.4'
    compile 'com.github.yalantis:Side-Menu.Android:1.0.1'
}
  • 主布局文件
    我們需要在主布局文件中進(jìn)行以下內(nèi)容添加:
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <io.codetail.widget.RevealFrameLayout
        android:id="@+id/container_frame"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

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

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

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:minHeight="?attr/actionBarSize"
            android:background="?attr/colorPrimary"/>

    </io.codetail.widget.RevealFrameLayout>

    <ScrollView
        android:id="@+id/scrollView"
        android:scrollbarThumbVertical="@android:color/transparent"
        android:layout_width="@dimen/sliding_menu_width"
        android:layout_height="match_parent"
        android:layout_gravity="start|bottom">

        <LinearLayout
            android:id="@+id/left_drawer"
            android:orientation="vertical"
            android:layout_width="@dimen/sliding_menu_width"
            android:layout_height="wrap_content"
            android:divider="@android:color/transparent"
            android:background="@android:color/transparent">
        </LinearLayout>
    </ScrollView>
</android.support.v4.widget.DrawerLayout>

我們也看到了,主布局文件相當(dāng)簡(jiǎn)單。其中content_overlay層蓋在content_frame之上,顯示圓形揭露效果。left_drawer盛放多個(gè)菜單項(xiàng)。

  • 新建fragment
    此處我們需要新建一個(gè)fragment來(lái)盛放需要展示的內(nèi)容,也就是可以使用圓形揭露方式進(jìn)行內(nèi)容刷新,切換的區(qū)域。需要注意的是,此fragment必須實(shí)現(xiàn)ScreenShotable接口。而ScreenShotable內(nèi)部只包含兩個(gè)抽象方法如下:
 //此方法將此時(shí)fragment區(qū)域的View做了一個(gè)截圖并保存到ContentFragment.this.bitmap
 @Override
    public void takeScreenShot() {
        Thread thread = new Thread() {
            @Override
            public void run() {
                Bitmap bitmap = Bitmap.createBitmap(containerView.getWidth(),
                        containerView.getHeight(), Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(bitmap);
                containerView.draw(canvas);
                ContentFragment.this.bitmap = bitmap;
            }
        };

        thread.start();

    }

    //這里提供了可以從外界獲取ContentFragment.this.bitmap的方法。
    @Override
    public Bitmap getBitmap() {
        return bitmap;
    }
  • 圓形揭示動(dòng)畫
    上述動(dòng)圖所展示圓形揭示效果就由下面代碼完成。
     //此處進(jìn)行fragment的圖片的替換
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private ScreenShotable replaceFragment(ScreenShotable screenShotable, int topPosition) {
        this.res = this.res == R.drawable.content_music ? R.drawable.content_films : R.drawable.content_music;
        View view = findViewById(R.id.content_frame);
        int finalRadius = Math.max(view.getWidth(), view.getHeight());
        //這里定義圓形揭露動(dòng)畫。
        Animator animator = ViewAnimationUtils.createCircularReveal(view, 0, topPosition, 0, finalRadius);
        animator.setInterpolator(new AccelerateInterpolator());
        animator.setDuration(ViewAnimator.CIRCULAR_REVEAL_ANIMATION_DURATION);
        //在動(dòng)畫執(zhí)行前將之前獲得的fragment區(qū)域的截圖覆蓋到真正的fragment之上。
        findViewById(R.id.content_overlay).setBackgroundDrawable(new BitmapDrawable(getResources(), screenShotable.getBitmap()));
        animator.start();
        //在動(dòng)畫執(zhí)行中更換真正的新的fragment。
        ContentFragment contentFragment = ContentFragment.newInstance(this.res);
        getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, contentFragment).commit();
        return contentFragment;
    }
  • 側(cè)滑菜單實(shí)現(xiàn)
    看完了上述代碼,我們圓形揭示的動(dòng)畫效果的核心部分已經(jīng)結(jié)束,接下來(lái)我們看一下側(cè)滑菜單以及它是如何與圓形揭露動(dòng)畫交互的。需要實(shí)現(xiàn)側(cè)滑菜單,首先需要在Activity實(shí)現(xiàn)接口ViewAnimator.ViewAnimatorListener,ViewAnimator類就是我們上面通過(guò)compile 'com.github.yalantis:Side-Menu.Android:1.0.1'導(dǎo)入的類。在使用時(shí),我們需要實(shí)例化一個(gè)ViewAnimator 類的實(shí)例,并且在側(cè)滑菜單開(kāi)始滑動(dòng)時(shí),調(diào)用showMenuContent方法。那么如何啟動(dòng)側(cè)滑菜單并且監(jiān)聽(tīng)呢?答案就是ActionBarDrawerToggle,具體代碼如下:
    private void setActionBar() {
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        //定義左上角圖標(biāo)是否可以點(diǎn)擊
        getSupportActionBar().setHomeButtonEnabled(true);
        //在左邊添加向左箭頭返回鍵
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        //注冊(cè)側(cè)滑菜單監(jiān)聽(tīng)器
        drawerToggle = new ActionBarDrawerToggle(
                this,/* host Activity */
                drawerLayout,/* DrawerLayout object */
                toolbar,  /* nav drawer icon to replace 'Up' caret */
                R.string.drawer_open,  /* "open drawer" description */
                R.string.drawer_close  /* "close drawer" description */
        ) {

            //當(dāng)側(cè)滑菜單關(guān)閉時(shí)
            @Override
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                linearLayout.removeAllViews();
                linearLayout.invalidate();
            }

            //當(dāng)側(cè)滑菜單滑動(dòng)時(shí)
            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                super.onDrawerSlide(drawerView, slideOffset);
                if (slideOffset > 0.6 && linearLayout.getChildCount() == 0)
                    viewAnimator.showMenuContent();
            }

            //當(dāng)側(cè)滑菜單打開(kāi)時(shí)
            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
            }
        };
        drawerLayout.setDrawerListener(drawerToggle);
    }

值得注意的是ActionBarDrawerToggle只能在onPostCreateonConfigurationChanged之間使用。

 //當(dāng)onCreate徹底執(zhí)行完畢
    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        //ActionBarDrawerToggle的syncState()方法會(huì)和Toolbar關(guān)聯(lián),將圖標(biāo)放入到Toolbar上。
        drawerToggle.syncState();
    }


    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        //當(dāng)狀態(tài)改變時(shí)圖標(biāo)也改變
        drawerToggle.onConfigurationChanged(newConfig);
    }

經(jīng)過(guò)上述代碼我們可以知道該動(dòng)畫通過(guò)activity中對(duì)側(cè)滑菜單狀態(tài)進(jìn)行監(jiān)聽(tīng),實(shí)現(xiàn)關(guān)于側(cè)滑菜單的動(dòng)效。而實(shí)現(xiàn)部分全部放在ViewAnimator之中,那么我們來(lái)看看ViewAnimator內(nèi)部都做了什么?從調(diào)用方式就可以知道,其中只有一個(gè)外部方法那就是showMenuContent

 public void showMenuContent() {
        setViewsClickable(false);
        viewList.clear();
        double size = list.size();
        for (int i = 0; i < size; i++) {
            View viewMenu = appCompatActivity.getLayoutInflater().inflate(R.layout.menu_list_item, null);
            final int finalI = i;
            //為viewMenu添加事件監(jiān)聽(tīng)
            viewMenu.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int[] location = {0, 0};
                    v.getLocationOnScreen(location);
                    switchItem(list.get(finalI), location[1] + v.getHeight() / 2);
                }
            });
            ((ImageView) viewMenu.findViewById(R.id.menu_item_image)).setImageResource(list.get(i).getImageRes());
            viewMenu.setVisibility(View.GONE);
            viewMenu.setEnabled(false);
            viewList.add(viewMenu);
            animatorListener.addViewToContainer(viewMenu);
            final double position = i;
            final double delay = 3 * ANIMATION_DURATION * (position / size);
            //執(zhí)行逐個(gè)滑出動(dòng)畫并保存每個(gè)fragment當(dāng)前顯示的截圖。
            new Handler().postDelayed(new Runnable() {
                public void run() {
                    if (position < viewList.size()) {
                        animateView((int) position);
                    }
                    if (position == viewList.size() - 1) {
                        screenShotable.takeScreenShot();
                        setViewsClickable(true);
                    }
                }
            }, (long) delay);
        }
    }

上述代碼一共做了兩件事,第一件:為viewMenu添加事件監(jiān)聽(tīng),第二件,執(zhí)行逐個(gè)滑出動(dòng)畫并保存每個(gè)fragment當(dāng)前顯示的截圖。其中進(jìn)行圓形揭示的方法為

private void switchItem(Resourceble slideMenuItem, int topPosition) {
        this.screenShotable = animatorListener.onSwitch(slideMenuItem, screenShotable, topPosition);
        hideMenuContent();
    }

我們可以看到,它就是調(diào)用了我們?cè)贏ctivity中實(shí)現(xiàn)的接口方法onSwitch執(zhí)行揭露動(dòng)畫,之后執(zhí)行側(cè)滑菜單退回的動(dòng)畫。
最后我們來(lái)看看其余三個(gè)接口方法是干嘛的:

 @Override
    public void disableHomeButton() {
        //定義左上角圖標(biāo)是否可以點(diǎn)擊
        getSupportActionBar().setHomeButtonEnabled(false);

    }

    @Override
    public void enableHomeButton() {
        getSupportActionBar().setHomeButtonEnabled(true);
        drawerLayout.closeDrawers();

    }

    @Override
    public void addViewToContainer(View view) {
        linearLayout.addView(view);
    }

剩余的三個(gè)接口方法其實(shí)就是對(duì)toolbar按鈕的一些控制,還有對(duì)側(cè)滑菜單區(qū)域view的管理。內(nèi)容很簡(jiǎn)單,就不細(xì)說(shuō)了。

總述

終于搞完了,可能是第一次總結(jié)這種類型的東西,總感覺(jué)有點(diǎn)繁雜,還是希望幫到大家。


文章參考
http://www.itdecent.cn/p/d2b1689a23bf
https://github.com/Yalantis/Side-Menu.Android

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、通過(guò)CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫(kù)組件 SD...
    陽(yáng)明AI閱讀 16,211評(píng)論 3 119
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,150評(píng)論 25 708
  • 四周終于安靜下來(lái),可以沏一杯茶了。。。 呼吸吐納,再吸氣呼氣十次,心好像還無(wú)法徹底靜下來(lái)!腦子里始終無(wú)法停在當(dāng)下!...
    嫣然579閱讀 714評(píng)論 1 3
  • 一:概念現(xiàn)金流量表是公司資金一張進(jìn)進(jìn)出出的表。正值代表流入公司,負(fù)值代表流出公司。主要包括三項(xiàng);營(yíng)業(yè)活動(dòng)現(xiàn)金流量,...
    郵吻閱讀 382評(píng)論 0 0
  • 《莊子》解,每章一讀。 文: 莊子將死,弟子欲厚葬之。莊子曰:“吾以天地為棺槨,以日月為連璧,星辰為珠璣,萬(wàn)物為赍...
    千里飄蓬閱讀 1,490評(píng)論 0 0

友情鏈接更多精彩內(nèi)容