[开源项目]Yalantis/Side-Menu.Android 学习

  • 侧边菜单栏
  • 侧边菜单栏动画
  • fragment 切换动画(另一个开源项目ozodrukh/CircularReveal)
  • 动画是用到了nineoldandroids 开源项目

  • 侧边菜单栏实现
    
    
<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">
<!--上一个fragment视图的截屏-->
<LinearLayout
android:id="@+id/content_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
<!--fragment容器-->
<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"/>
</ScrollView>
</android.support.v4.widget.DrawerLayout>
菜单栏中的内容通过addViewToContainer回调的方式添加到 left_drawer
   
   
//MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
//.... 
linearLayout = ( LinearLayout ) findViewById ( R . id . left_drawer );
// list 为菜单每个项的内容
// contentFragment 为主体显示继承自Fragment 并实现了ScreenShotable接口
// 最后一个参数为ViewAnimator.ViewAnimatorListener 接口,其中一个方法便是addViewToContainer
// 在ViewAnimator 中 创建view 并添加到 linearLayout 菜单中.
viewAnimator = new ViewAnimator < SlideMenuItem >( this , list , contentFragment , drawerLayout , this );
}
//添加到菜单的LinearLayout 中去
@Override
public void addViewToContainer(View view) {
linearLayout.addView(view);
}
ViewAnimator类, 用于菜单内容的创建, 菜单显示时候的动画效果, 以及菜单项的点击反馈.
   
   
//ViewAnimator.java
public class ViewAnimator<T extends Resourceble> {
//显示菜单
public void showMenuContent() {
setViewsClickable(false);
//清除菜单项集合
viewList.clear();
double size = list.size();
for (int i = 0; i < size; i++) {
View viewMenu = actionBarActivity.getLayoutInflater().inflate(R.layout.menu_list_item, null);
final int finalI = i;
viewMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int[] location = {0, 0};
//得到当前view 在屏幕的位置
v.getLocationOnScreen(location);
//回调视图接口,当点击某一项的时候反馈到MainActivity.onSwitch()用于替换主体Fragment内容
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);
//添加到菜单栏中,调用MainActivity.addViewToContainer(View).
animatorListener.addViewToContainer(viewMenu);
//以下是动画
final double position = i;
final double delay = 3 * ANIMATION_DURATION * (position / size);
new Handler().postDelayed(new Runnable() {
public void run() {
if (position < viewList.size()) {
animateView((int) position);
}
//当菜单显示完毕之后,调用ContentFragment.takeScreenShot()
//在ContentFragment.takeScreenShot()实现是类似于截屏,将自己的视图截屏,
//当然这是不包括左侧显示出来的菜单的.
if (position == viewList.size() - 1) {
screenShotable.takeScreenShot();
//将所有菜单项设置为可点击状态
setViewsClickable(true);
}
}
}, (long) delay);
}
}
private void setViewsClickable(boolean clickable) {
animatorListener.disableHomeButton();
for (View view : viewList) {
view.setEnabled(clickable);
}
}
 
private void animateView(int position) {
final View view = viewList.get(position);
view.setVisibility(View.VISIBLE);
FlipAnimation rotation =
new FlipAnimation(90, 0, 0.0f, view.getHeight() / 2.0f);
rotation.setDuration(ANIMATION_DURATION);
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
rotation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
view.clearAnimation();
}
});
 
view.startAnimation(rotation);
}
 
private void switchItem(Resourceble slideMenuItem, int topPosition) {
this.screenShotable = animatorListener.onSwitch(slideMenuItem, screenShotable, topPosition);
//隐藏菜单
hideMenuContent();
}
 
public interface ViewAnimatorListener {
//切换视图
//ScreenShotable为当前主体内容的截屏
public ScreenShotable onSwitch(Resourceble slideMenuItem, ScreenShotable screenShotable, int position);
 
public void disableHomeButton();
 
public void enableHomeButton();
 
public void addViewToContainer(View view);
 
}
}
视图切换动画实现
   
   
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());
//创建圆形动画
SupportAnimator animator = ViewAnimationUtils.createCircularReveal(view, 0, topPosition, 0, finalRadius);
animator.setInterpolator(new AccelerateInterpolator());
animator.setDuration(ViewAnimator.CIRCULAR_REVEAL_ANIMATION_DURATION);
//由于圆形动画是一点点的扩大的,其没有全部覆盖的部分应该为上一个视图的内容,
// 因此我们需要将前面的视图截图保存下来,可将下面代码屏蔽可明白其意义.
findViewById(R.id.content_overlay).setBackgroundDrawable(new BitmapDrawable(getResources(), screenShotable.getBitmap()));
animator.start();
ContentFragment contentFragment = ContentFragment.newInstance(this.res);
getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, contentFragment).commit();
return contentFragment;
}
 
//MainActivity.java
//在MainActivity中设置了对ViewAnimator的监听,而在ViewAnimator内部也关联上菜单点击的响应switchItem()
@Override
public ScreenShotable onSwitch(Resourceble slideMenuItem, ScreenShotable screenShotable, int position) {
String s = slideMenuItem.getName();
if (s.equals(ContentFragment.CLOSE)) {
return screenShotable;
} else {
return replaceFragment(screenShotable, position);
}
}
创建圆形动画,由 ViewAnimationUtils 工具类实现,其中用到了 ozodrukh/CircularReveal 
   
   
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static SupportAnimator createCircularReveal(View view,
int centerX, int centerY,
float startRadius, float endRadius) {
 
if(LOLLIPOP_PLUS){
return new SupportAnimatorLollipop(android.view.ViewAnimationUtils
.createCircularReveal(view, centerX, centerY, startRadius, endRadius));
}
 
if(!(view.getParent() instanceof RevealAnimator)){
throw new IllegalArgumentException("View must be inside RevealFrameLayout or RevealLinearLayout.");
}
//在布局文件中其父类为 io.codetail.widget.RevealFrameLayout,实现了RevealAnimator 接口  
RevealAnimator revealLayout = (RevealAnimator) view.getParent();
revealLayout.setTarget(view);
revealLayout.setCenter(centerX, centerY);
 
Rect bounds = new Rect();
view.getHitRect(bounds);
//创建Object 动画  
ObjectAnimator reveal = ObjectAnimator.ofFloat(revealLayout, "revealRadius", startRadius, endRadius);
reveal.addListener(getRevealFinishListener(revealLayout, bounds));
 
return new SupportAnimatorPreL(reveal);
}

   
   
package io.codetail.widget;
 
public class RevealFrameLayout extends FrameLayout implements RevealAnimator{
 
//设置需要圆形动画的view
@Override
public void setTarget(View view){
mTarget = view;
}
//圆心设置  
@Override
public void setCenter(float centerX, float centerY){
mCenterX = centerX;
mCenterY = centerY;
}
//会自动调用,将其从 startRadius递增至 endRadius
@Override
public void setRevealRadius(float radius){
mRadius = radius;
invalidate();
}
 
@Override
public float getRevealRadius(){
return mRadius;
}
 
 
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if(!mClipOutlines && child != mTarget)
return super.drawChild(canvas, child, drawingTime);
//动画实现  
final int state = canvas.save();
 
mRevealPath.reset();
mRevealPath.addCircle(mCenterX, mCenterY, mRadius, Path.Direction.CW);
canvas.clipPath(mRevealPath);
boolean isInvalided = super.drawChild(canvas, child, drawingTime);
canvas.restoreToCount(state);
return isInvalided;
}
 
}









  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值