我们知道SlidingMenu原生只支持侧滑菜单滑动时的动画效果(自带的Demo中有CustomScaleAnimation,CustomZoomAnimation等),而QQ5.0的侧滑时前面的主页面也有一个缩小的动画效果,那我们是不是可以在SlidingMenu的基础上扩展一下,增加一个和侧滑菜单相同的动画机制呢?我们先来看一下源码里侧滑菜单的动画效果是怎么实现的~~
1.原理分析
1.SlidingMenu源码中有两个类叫做CustomViewAbove和CustomViewBehind,分别对应主页面和侧滑菜单
2.通过阅读SlidingMenu源码发现MenuView对应的实现类叫CustomViewBehind,里面有一个CanvasTransformer的接口对象
<span style="font-family:Microsoft YaHei;font-size:14px;">private CanvasTransformer mTransformer;</span>
定义如下
<span style="font-family:Microsoft YaHei;font-size:14px;"> /**
* The Interface CanvasTransformer.
*/
public interface CanvasTransformer {
/**
* Transform canvas.
*
* @param canvas the canvas
* @param percentOpen the percent open
*/
public void transformCanvas(Canvas canvas, float percentOpen);
}</span>
他只是把Canvas对象,以及侧滑的进度值percentOpen(0.0-1.0)传递给实现类,由实现类自己定义动画效果。
在CustomViewBehind中只有三处调用:
<span style="font-family:Microsoft YaHei;font-size:14px;"> @Override
protected void dispatchDraw(Canvas canvas) {
if (mTransformer != null) {
canvas.save();
mTransformer.transformCanvas(canvas, mViewAbove.getPercentOpen());
super.dispatchDraw(canvas);
canvas.restore();
} else
super.dispatchDraw(canvas);
}</span>
<span style="font-family:Microsoft YaHei;font-size:14px;"> @Override
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
if (mTransformer != null)
invalidate();
}</span>
<span style="font-family:Microsoft YaHei;font-size:14px;"> public void setCanvasTransformer(CanvasTransformer t) {
mTransformer = t;
}
</span>
可以发现实际就是在画自己的时候(dispatchDraw)加了一个canvas的动画效果。
好,原理清楚了,那我们来修改一实现
2.实现
第一步,修改源码
先将CustomViewBehind中mTransformer以及相关的三个方法Copy到CustomViewAbove中去。
(scrollTo中的invalidate调用我没加,因为觉得滑动时页面肯定会重绘,不需要再手动再调用一遍了,目前测试没问题,不放心的同学也可以加上。。。)
然后相应的,在SlidingMenu中加上设置CanvasTransformer的方法
/**
* Sets the foreground canvas transformer.
*
* @param t the new foreground canvas transformer
*/
public void setAboveCanvasTransformer(CanvasTransformer t){
mViewAbove.setCanvasTransformer(t);
}
第二步,调整动画效果
<span style="white-space:pre"> </span>SlidingMenu sm = getSlidingMenu();
sm.setBehindOffset(200);//menu滑动到距离屏幕右边200个像素的位置
sm.setFadeDegree(0.35f);//menu的渐隐效果系数(0-1)
sm.setBehindScrollScale(0.25f);//主页面移动1个像素,menu移动0.25个像素
sm.setAboveCanvasTransformer(new CanvasTransformer() {
@Override
public void transformCanvas(Canvas canvas, float percentOpen) {
//percentOpen从0变到1,原图从1倍大小缩小到3/4
float scale = (float) (1.0 - 0.25 * percentOpen);
//x,y方向同时缩小,动画的相对中心定在“左中”,这样就不至于缩放的时候屏幕左边出现一条空白。
canvas.scale(scale, scale, 0, canvas.getHeight() / 2);
}
});
sm.setBehindCanvasTransformer(new CanvasTransformer() {
@Override
public void transformCanvas(Canvas canvas, float percentOpen) {
//原图从3/4增加到1倍大小
float scale = (float) (0.75 + 0.25 * percentOpen);
//x坐标先向屏幕左边移动1/4的视图宽度,然后再慢慢移动到0,这样来实现从屏幕左边移动进屏幕的效果
canvas.translate(-canvas.getWidth()/4 + percentOpen * canvas.getWidth()/4, 0);
//x,y方向同时放大,动画的相对中心定在“右中”
canvas.scale(scale, scale, canvas.getWidth(), canvas.getHeight() / 2);
}
});
源码:
1.MainActivity.java
package com.example.slidemenudemo;
import android.graphics.Canvas;
import android.os.Bundle;
import android.util.TypedValue;
import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu;
import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu.CanvasTransformer;
import com.jeremyfeinstein.slidingmenu.lib.app.SlidingFragmentActivity;
public class MainActivity extends SlidingFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setBehindContentView(R.layout.menu_frame);
setContentView(R.layout.content_frame);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame, new SampleListFragment())
.replace(R.id.content_frame, new SampleListFragment()).commit();
}
SlidingMenu sm = getSlidingMenu();
sm.setBehindOffset(200);//menu滑动到距离屏幕右边200个像素的位置
sm.setFadeDegree(0.35f);//menu的渐隐效果系数(0-1)
sm.setBehindScrollScale(0.25f);//主页面移动1个像素,menu移动0.25个像素
sm.setAboveCanvasTransformer(new CanvasTransformer() {
@Override
public void transformCanvas(Canvas canvas, float percentOpen) {
//percentOpen从0变到1,原图从1倍大小缩小到3/4
float scale = (float) (1.0 - 0.25 * percentOpen);
//x,y方向同时缩小,动画的相对中心定在“左中”,这样就不至于缩放的时候屏幕左边出现一条空白。
canvas.scale(scale, scale, 0, canvas.getHeight() / 2);
}
});
sm.setBehindCanvasTransformer(new CanvasTransformer() {
@Override
public void transformCanvas(Canvas canvas, float percentOpen) {
//原图从3/4增加到1倍大小
float scale = (float) (0.75 + 0.25 * percentOpen);
//x坐标先向屏幕左边移动1/4的视图宽度,然后再慢慢移动到0,这样来实现从屏幕左边移动进屏幕的效果
canvas.translate(-canvas.getWidth()/4 + percentOpen * canvas.getWidth()/4, 0);
//x,y方向同时放大,动画的相对中心定在“右中”
canvas.scale(scale, scale, canvas.getWidth(), canvas.getHeight() / 2);
}
});
sm.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
}
}
2. SampleListFragment.java
package com.example.slidemenudemo;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.widget.ArrayAdapter;
public class SampleListFragment extends ListFragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity().getApplicationContext(), android.R.layout.simple_list_item_1);
for (int i = 0; i < 20; i++) {
adapter.add("item " + i);
}
setListAdapter(adapter);
}
}
资源文件
1.menu_fragment.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
2.content_frame.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />