前言
由于工作需要客叉,小編對(duì)最近十分炫酷的android動(dòng)畫效果進(jìn)行了一波瘋狂搜集缎除。經(jīng)過一下午的努力,小編終于成功的將大部分好看的動(dòng)畫瀏覽了一遍冲九,差點(diǎn)閃瞎我的24k鈦合金狗眼。所以跟束,小編本著渴望裝逼的心情娘侍,將好的動(dòng)畫的實(shí)現(xiàn)原理,所使用的框架一個(gè)一個(gè)的整理出來泳炉。逼王之路憾筏,指日可待!;ǘ臁氧腰!
正文
今天小編帶給各位大佬的是側(cè)滑菜單+圓形揭示動(dòng)畫的實(shí)現(xiàn)。廢話少說刨肃,看效果先古拴。
怎么樣?是不是很炫酷真友?下面我們就一起來看看這個(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)入庫:
在文件的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來盛放需要展示的內(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é)束麻顶,接下來我們看一下側(cè)滑菜單以及它是如何與圓形揭露動(dòng)畫交互的。需要實(shí)現(xiàn)側(cè)滑菜單舱卡,首先需要在Activity實(shí)現(xiàn)接口ViewAnimator.ViewAnimatorListener
辅肾,ViewAnimator
類就是我們上面通過compile 'com.github.yalantis:Side-Menu.Android:1.0.1'
導(dǎo)入的類。在使用時(shí)轮锥,我們需要實(shí)例化一個(gè)ViewAnimator
類的實(shí)例矫钓,并且在側(cè)滑菜單開始滑動(dòng)時(shí),調(diào)用showMenuContent
方法舍杜。那么如何啟動(dòng)側(cè)滑菜單并且監(jiān)聽呢新娜?答案就是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)聽器
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è)滑菜單打開時(shí)
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
}
};
drawerLayout.setDrawerListener(drawerToggle);
}
值得注意的是ActionBarDrawerToggle
只能在onPostCreate
和onConfigurationChanged
之間使用既绩。
//當(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)過上述代碼我們可以知道該動(dòng)畫通過activity中對(duì)側(cè)滑菜單狀態(tài)進(jìn)行監(jiān)聽饲握,實(shí)現(xiàn)關(guān)于側(cè)滑菜單的動(dòng)效私杜。而實(shí)現(xiàn)部分全部放在ViewAnimator
之中,那么我們來看看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)聽
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)聽铝耻,第二件,執(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)畫箍铭。
最后我們來看看其余三個(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ì)說了。
總述
終于搞完了状答,可能是第一次總結(jié)這種類型的東西冷守,總感覺有點(diǎn)繁雜,還是希望幫到大家惊科。
文章參考
http://www.reibang.com/p/d2b1689a23bf
https://github.com/Yalantis/Side-Menu.Android