最近有些忙,有一段时间没有写博客了,今天跟大家分享下SideMenu侧滑菜单的实现,自定义的Animation效果,给别人看起来就感觉好屌的样子,哈哈!不多说,先看看实现的效果。
总体来说也并不复杂,下面给大家看下整个项目的结构:
以下是实现的源码及详细步骤:
新建android studio项目我这边就不讲述了,说一下android studio关联library项目,很简单,我贴几张图片大家就知道了,File->
然后
这样就可以了。
首先看下ViewAnimationUtils类
package com.lai.library.utils;
import android.app.Activity;
import android.os.Handler;
import android.support.v4.widget.DrawerLayout;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.widget.ImageView;
import com.lai.library.animations.FlipAnimation;
import com.lai.library.interfaces.Resourceble;
import com.lai.library.interfaces.ScreenShortable;
import java.util.ArrayList;
import java.util.List;
import static com.lai.sidemenu.R.layout;
/**
* Created by henry on 2016/5/11.
* 自定义的动画效果
*/
public class ViewAnimationUtils<T extends Resourceble> {
/**
* 切换隐藏与显示的效果
*/
private final int ANIMATION_DURATION = 10;
public static final int CIRCULAR_REVEAL_ANIMATION_DURATION = 20;
private Activity activity;
private List<T> list;
private List<View> viewList = new ArrayList<View>(); //View集合
private ScreenShortable screenShortable;//获取图片
private DrawerLayout drawerLayout;
private ViewAnimatorListener viewAnimatorListener;//实现定义接口
/**
* 构造器
*/
public ViewAnimationUtils(Activity activity, List<T> list, ScreenShortable screenShortable, DrawerLayout drawerLayout, ViewAnimatorListener viewAnimatorListener) {
this.activity = activity;
this.list = list;
this.screenShortable = screenShortable;
this.drawerLayout = drawerLayout;
this.viewAnimatorListener = viewAnimatorListener;
}
/**
* 显示菜单的数量
*/
public void showMenuCount() {
setViewsClickable(false);//刚显示个数时view是出于false的状态
viewList.clear();//清空组件
double size = list.size();
for (int i = 0; i < size; i++) {
View viewMenu = activity.getLayoutInflater().inflate(layout.menu_list_item, null);
final int finalI = i;
/**
* view的点击事件
*/
viewMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int[] localtion = {0, 0};//用数组坐标记录位置
v.getLocationOnScreen(localtion);//垂直
switchItem(list.get(finalI), localtion[1] + v.getHeight() / 2); //点击后调用switchItem隐藏
}
});
//得到资源并设置
((ImageView) viewMenu.findViewById(com.lai.sidemenu.R.id.menu_item_image)).setImageResource(list.get(i).getImageRes());
viewMenu.setVisibility(View.GONE);
viewMenu.setEnabled(false);
viewList.add(viewMenu);//添加list中
viewAnimatorListener.addViewToContainer(viewMenu);//添加到动画接口中
final double position = i;
final double delay = 3 * ANIMATION_DURATION * (position / size);
/**
* 耗时操作
*/
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (position < viewList.size()) {
animateView((int) position);
}
if (position == viewList.size() - 1) {
screenShortable.takeScreenShort();
setViewsClickable(true);
}
}
}, (long) delay);
}
}
/**
* 设置View
*/
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 onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
view.clearAnimation();//结束后清除动画效果
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
/**
* 点击的Item
*/
private void switchItem(Resourceble slideMenuItem, int topPosition) {
this.screenShortable = viewAnimatorListener.onSwitch(slideMenuItem, screenShortable, topPosition);
//点击后隐藏viewList
hideMenuContent();
}
/**
* 隐藏menu
*/
private void hideMenuContent() {
setViewsClickable(false);
double size = list.size();
//隐藏全部
for (int i = list.size(); i >= 0; i--) {
final double position = i;//记录在viewList中点击的position作为头部,添加动画效果
final double delay = 3 * ANIMATION_DURATION * (position / size); //线程时间(其实这一步没必要,线程的时间可以直接写死)
//耗时操作应放在handler里面操作
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (position < viewList.size()) {
//设置隐藏时的动画
animateHideView((int) position);
}
}
}, (long) delay);
}
}
/**
* 设置view隐藏时的动画
*/
private void animateHideView(final int position) {
final View view = viewList.get(position);//得到点击的view
//0表示正在处于的状态为0°,90为操作后的度数,0.0f表示起始精确位置,centerY为view高度的二分之一
FlipAnimation rotation = new FlipAnimation(0, 90, 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 onAnimationStart(Animation animation) {
}
/**
* 结束时
* @param animation
*/
@Override
public void onAnimationEnd(Animation animation) {
view.clearAnimation();
view.setVisibility(View.INVISIBLE);//隐藏view
if (position == viewList.size() - 1) //防止越界
{
viewAnimatorListener.enableHomeButton();
drawerLayout.closeDrawers();//关闭drawers
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
view.startAnimation(rotation);//开始动画
}
/**
* 设置控件是否可用
*/
private void setViewsClickable(boolean clickable) {
//点击以后view不可再重新点击
viewAnimatorListener.disableHomeButton();
/**
* 点击第几个view
*/
for (View view : viewList) {
view.setEnabled(clickable);
}
}
/**
* 定义接口
*/
public interface ViewAnimatorListener {
//点击MenuItem
public ScreenShortable onSwitch(Resourceble slideMenuItem, ScreenShortable screenShortable, int position);
public void disableHomeButton();
public void enableHomeButton();
public void addViewToContainer(View view);
}
}
上面的注释写的非常详细了,一目了然。
下面是FlipAnimation类,自定义的动画效果,代码量很少,更容易理解!
package com.lai.library.animations;
/**
* Created by henry on 2016/5/11.
*/
import android.graphics.Camera;
import android.graphics.Matrix;
import android.view.animation.Animation;
import android.view.animation.Transformation;
/**
* 自定义动画效果
*/
public class FlipAnimation extends Animation {
private final float mFromDegrees;//起始的度数
private final float mToDegrees;//要到达的度数
private final float mCenterX;// 水平方向X轴的位置
private final float mCenterY;//Y轴的位置
private Camera mCamera;
/**
* 构造器
*
* @param fromDegrees
* @param toDegrees
* @param centerX
* @param centerY
*/
public FlipAnimation(float fromDegrees, float toDegrees, float centerX, float centerY) {
this.mFromDegrees = fromDegrees;
this.mToDegrees = toDegrees;
this.mCenterX = centerX;
this.mCenterY = centerY;
}
/**
* 初始化屏幕宽度创建Camera
*
* @param width
* @param height
* @param parentWidth
* @param parentHeight
*/
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
/**
* 动画效果
*/
public void applyTransformation(float interpolatedTime, Transformation transformation) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);//总度数
/**
* 获取值
*/
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
/**
* 对图片进行处理
* Translate 平移变换
Rotate 旋转变换
Scale 缩放变换
Skew 错切变换
*/
final Matrix matrix = transformation.getMatrix();
camera.save(); //保存当前状态 ,与restore是成对出现的
camera.rotateY(degrees);//Y轴旋转
camera.getMatrix(matrix);//得到设置后的matrix
camera.restore();//回复当前状态
/**
* 下面两句标识以动画中心点为中央部分
*/
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
还有几个要实现的接口就不贴出来了,到最后面可以自己去下载源码。
写完关联的library以后,接下来就是我们实际操作的页面了。我们只需要在MainActivity里面嵌套一个Fragment就行了,然后再实现完ViewAnimationUtils的四个接口,马上开始。
第一步写一个Fragment类
package com.lai.sidemenu.fragment;
import android.app.Fragment;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.lai.library.interfaces.ScreenShortable;
import com.lai.sidemenu.R;
/**
* Created by henry on 2016/5/11.
*/
public class ContentFragment extends Fragment implements ScreenShortable {
/**
* 点击图片需要切换的选项value
*/
public static final String CLOSE = "关闭";
public static final String BUILDING = "创建";
public static final String BOOK = "书本";
public static final String PAINT = "交点";
public static final String CASE = "案例";
public static final String SHOP = "商品";
public static final String PARTY = "派对";
public static final String MOVIE = "电影";
private View rootView;
private ImageView mImageView;
private int res;
private Bitmap bitmap;
public static ContentFragment newInstance(int resId) {
ContentFragment contentFragment = new ContentFragment();
Bundle bundle = new Bundle();
bundle.putInt(Integer.class.getName(), resId);
contentFragment.setArguments(bundle);//设置Arguments
return contentFragment;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.rootView = view.findViewById(R.id.container);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
res = getArguments().getInt(Integer.class.getName());//获取Arguments的值
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, null);
mImageView = (ImageView) view.findViewById(R.id.image_content);
mImageView.setClickable(true);
mImageView.setFocusable(true);
mImageView.setImageResource(res);
return view;
}
/**
* 设置bitmap
* 耗时操作
*/
@Override
public void takeScreenShort() {
Thread thread=new Thread(){
@Override
public void run() {
Bitmap bitmap=Bitmap.createBitmap(rootView.getWidth(),rootView.getHeight(),Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas(bitmap);
rootView.draw(canvas);
ContentFragment.this.bitmap=bitmap;
}
};
thread.start();
}
@Override
public Bitmap getBitmap() {
return bitmap;
}
}
在MainActivity中主要就是为Fragment页面添加菜单项,一些点击的操作,见下面代码:
package com.lai.sidemenu;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.LinearLayout;
import com.lai.library.interfaces.Resourceble;
import com.lai.library.interfaces.ScreenShortable;
import com.lai.library.model.SlideMenuitem;
import com.lai.library.utils.ViewAnimationUtils;
import com.lai.sidemenu.fragment.ContentFragment;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends ActionBarActivity implements ViewAnimationUtils.ViewAnimatorListener {
private ContentFragment contentFragment;
private DrawerLayout drawerLayout;
private ActionBarDrawerToggle drawerToggle;
private List<SlideMenuitem> list = new ArrayList<SlideMenuitem>();
private ViewAnimationUtils viewAnimationUtils;
private int res = R.drawable.content_music;
private LinearLayout linearLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contentFragment = ContentFragment.newInstance(R.drawable.content_music);
getFragmentManager().beginTransaction().replace(R.id.container_frame, contentFragment).commit();
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
drawerLayout.setScrimColor(Color.TRANSPARENT);
linearLayout = (LinearLayout) findViewById(R.id.left_drawer);
linearLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
drawerLayout.closeDrawers();
}
});
setActionBar();
createMenuList();
viewAnimationUtils = new ViewAnimationUtils<>(this, list, contentFragment, drawerLayout, this);
}
/**
* 设置ActionBar一些基础操作
*/
private void setActionBar() {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close) {
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
linearLayout.removeAllViews();
linearLayout.invalidate();
}
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, slideOffset);
if (slideOffset > 0.6 && linearLayout.getChildCount() == 0) {
viewAnimationUtils.showMenuCount();
}
}
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
}
};
drawerLayout.setDrawerListener(drawerToggle);
}
/**
* 创建menu菜单
*/
private void createMenuList() {
SlideMenuitem menuitemOne = new SlideMenuitem(ContentFragment.CLOSE, R.drawable.icn_close);
list.add(menuitemOne);
SlideMenuitem menuitemTwo = new SlideMenuitem(ContentFragment.BUILDING, R.drawable.icn_1);
list.add(menuitemTwo);
SlideMenuitem menuitemthree = new SlideMenuitem(ContentFragment.BOOK, R.drawable.icn_2);
list.add(menuitemthree);
SlideMenuitem menuitemFour = new SlideMenuitem(ContentFragment.CASE, R.drawable.icn_3);
list.add(menuitemFour);
SlideMenuitem menuitemFive = new SlideMenuitem(ContentFragment.MOVIE, R.drawable.icn_4);
list.add(menuitemFive);
SlideMenuitem menuitemSix = new SlideMenuitem(ContentFragment.PAINT, R.drawable.icn_5);
list.add(menuitemSix);
SlideMenuitem menuitemSeven = new SlideMenuitem(ContentFragment.SHOP, R.drawable.icn_6);
list.add(menuitemSeven);
SlideMenuitem menuitemEight = new SlideMenuitem(ContentFragment.PARTY, R.drawable.icn_7);
list.add(menuitemEight);
}
/**
* 开始状态
*
* @param savedInstanceState
* @param persistentState
*/
@Override
public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onPostCreate(savedInstanceState, persistentState);
drawerToggle.syncState();
}
/**
* 配置drawerToggle
*
* @param newConfig
*/
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
drawerToggle.onConfigurationChanged(newConfig);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (drawerToggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public ScreenShortable onSwitch(Resourceble slideMenuItem, ScreenShortable screenShortable, int position) {
switch (slideMenuItem.getName()) {
case ContentFragment.CLOSE:
return screenShortable;
default:
return replaceFragment(screenShortable, position);
}
}
/**
* 切换图片操作
*
* @param screenShortable
* @param position
* @return
*/
private ScreenShortable replaceFragment(ScreenShortable screenShortable, int position) {
this.res = this.res == R.drawable.content_music ? R.drawable.content_films : R.drawable.content_music;
View view = findViewById(R.id.container_frame);
int finalRadius = Math.max(view.getWidth(), view.getHeight());
io.codetail.animation.SupportAnimator animator = io.codetail.animation.ViewAnimationUtils.createCircularReveal(view, 0, position, 0, finalRadius);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.setDuration(ViewAnimationUtils.CIRCULAR_REVEAL_ANIMATION_DURATION);
findViewById(R.id.content_overlay).setBackgroundDrawable(new BitmapDrawable(getResources(), screenShortable.getBitmap()));
animator.start();//开始动画
ContentFragment contentFragment = ContentFragment.newInstance(this.res);
getFragmentManager().beginTransaction().replace(R.id.content_frame, contentFragment).commit();
return contentFragment;
}
/**
* 设置button不可用
*/
@Override
public void disableHomeButton() {
getSupportActionBar().setHomeButtonEnabled(false);
}
@Override
public void enableHomeButton() {
getSupportActionBar().setHomeButtonEnabled(true);
drawerLayout.closeDrawers();
}
@Override
public void addViewToContainer(View view) {
linearLayout.addView(view);
}
}
主要的代码已经贴出来了,如果感兴趣的话可以从下面下载项目。