Android界面动画初探之--探索侧边栏折叠效果的实现
2015-09-03 20:30
721 查看
不多说,看看效果先:
这个是我实现的Demo:http://pan.baidu.com/s/1c08qBtA
这就是今天要实现的效果;貌似这种效果以前有人实现过;今天我根据自己的探索带领大家来实现这个效果;
我记得在前面讲过自定义ViewGroup的侧边栏实现原理;/article/11311675.html
在这个里面我们知道了如何很方便的实现平移动画;很方便的处理触摸事件的焦点问题;今天我接着上面的知识,来带领大家搞定类似于这种自定义动画的实现。
首先大家根据效果图想一下:
要实现效果图,我们需要处理的是什么?
没错,你需要处理的是在侧边栏滑动时,需要处理菜单那一栏里面的视图绘制;也就是说我们根据边栏所在位置确定菜单内部的动画效果;当侧边栏完全打开时:我们的菜单栏应该是平铺的;随着边栏向里面滑动;菜单的内部视图应该进行一个折叠;今天我只是做了当菜单在左,主视图在右边的情况;
现在我们先分析一下这个效果:
其实解决上上述三个问题,我们也就实现了这个效果;首先我们思考一下第一个的解决方案:分割菜单栏图片;其实也就是将菜单栏的背景图片按照层叠的层数进行切割;
我查到了用:
因此切割的核心必须是上面这个方法;折叠效果的每一块都是等比例分割的,因此他们切割的位置代码如下:
现在看第二个问题:如何计算每一部分转化需要的矩阵;我又查了一下:发现有这个api:
因此切割变化的核心就是这个方法了;我们每一个都是4个点,当切割的区域在偶数层时:他们只是右边的两个左边点进行变化;当切割的区域在奇数页面时,就只是左边的两个点进行变化;切割形成的三角形的高度可以用勾股定理得出;因此可以产生大概如下的代码:
第二个问题解决如上:那我们进行第三个问题:如何实现阴影?
上面这个方法可以实现我们需要的阴影效果;当然,阴影的透明度需要根据折叠的程度来决定;
下面我就上重点代码了:大家注意看代码注释就可以看出实现的原理;重点搜索dispatchDraw方法进行查看:
相关代码1:
Activity中的代码:
其实实现的原理并不难;只要你仔细分析,类似于这种效果,你首先要将它分解开来看;然后分别找到实现相应效果的办法,最终将他组合在一起,那么就可以了;照着这篇介绍的原理,我相信大家可以自己研究一下类似于翻页效果的例子等等;
这个是我实现的Demo:http://pan.baidu.com/s/1c08qBtA
这就是今天要实现的效果;貌似这种效果以前有人实现过;今天我根据自己的探索带领大家来实现这个效果;
我记得在前面讲过自定义ViewGroup的侧边栏实现原理;/article/11311675.html
在这个里面我们知道了如何很方便的实现平移动画;很方便的处理触摸事件的焦点问题;今天我接着上面的知识,来带领大家搞定类似于这种自定义动画的实现。
首先大家根据效果图想一下:
要实现效果图,我们需要处理的是什么?
没错,你需要处理的是在侧边栏滑动时,需要处理菜单那一栏里面的视图绘制;也就是说我们根据边栏所在位置确定菜单内部的动画效果;当侧边栏完全打开时:我们的菜单栏应该是平铺的;随着边栏向里面滑动;菜单的内部视图应该进行一个折叠;今天我只是做了当菜单在左,主视图在右边的情况;
现在我们先分析一下这个效果:
第一:如何分割菜单栏图片 第二:如何计算每一部分转化需要的矩阵 第三:如何实现阴影效果
其实解决上上述三个问题,我们也就实现了这个效果;首先我们思考一下第一个的解决方案:分割菜单栏图片;其实也就是将菜单栏的背景图片按照层叠的层数进行切割;
我查到了用:
void android.graphics.Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint); 将bitmap的src区域的内容绘制到画布的dst区域上;
因此切割的核心必须是上面这个方法;折叠效果的每一块都是等比例分割的,因此他们切割的位置代码如下:
// 初始化切割图片的位置 mRects = new Rect[foldcound]; // 计算每一个折叠的宽度 int foldchildwidth = width / foldcound; // 计算好切割图片的每一个区域 for (int i = 0; i < foldcound; i++) { mRects[i] = new Rect(i * foldchildwidth, 0, (i + 1) * foldchildwidth, height); }
现在看第二个问题:如何计算每一部分转化需要的矩阵;我又查了一下:发现有这个api:
public boolean setPolyToPoly (float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) 大意是根据多边形变化前的坐标集合src和变化后的坐标集合dst计算出Matrix矩阵
因此切割变化的核心就是这个方法了;我们每一个都是4个点,当切割的区域在偶数层时:他们只是右边的两个左边点进行变化;当切割的区域在奇数页面时,就只是左边的两个点进行变化;切割形成的三角形的高度可以用勾股定理得出;因此可以产生大概如下的代码:
/** 根据变化计算矩阵转换 */ //startX指的是绘制的起点,endX指的是当前菜单页面的终点,width和height都是没有折叠时候平铺的宽高 private void getMatrixsbyscroll(int startX, int endX, int width, int height) { // 初始化矩阵变化数组 float[] spoi = new float[8], dpoi = new float[8]; // 计算每一个折叠前的宽度 int foldchildwidth = width / foldcound; // 计算每一个折叠后的宽度 int foldchangewidth = (endX - startX) / foldcound; // 根据勾股定理计算出折叠变化的高度 int deteheight = (int) Math.sqrt(Math.pow(foldchildwidth, 2) - Math.pow(foldchangewidth, 2)); for (int i = 0; i < foldcound; i++) { // 图片变化前的坐标 // 左上角 spoi[0] = startX + i * foldchildwidth; spoi[1] = 0; // 左下角 spoi[2] = spoi[0]; spoi[3] = height; // 右上角,纵坐标和左上角一样 spoi[4] = spoi[0] + foldchildwidth; spoi[5] = spoi[1]; // 右下角,横坐标和右上角一样 spoi[6] = spoi[4]; spoi[7] = height; // 图片变化后的坐标,这个要分奇数和偶数 if (i % 2 == 0) { // 偶数情况下 // 左上角 dpoi[0] = startX + i * foldchangewidth; dpoi[1] = 0; // 左下角 dpoi[2] = dpoi[0]; dpoi[3] = height; // 右上角 dpoi[4] = dpoi[0] + foldchangewidth; dpoi[5] = dpoi[1] + deteheight; // 右下角 dpoi[6] = dpoi[4]; dpoi[7] = height - deteheight; } else { // 奇数情况下 // 左上角 dpoi[0] = startX + i * foldchangewidth; dpoi[1] = deteheight; // 左下角 dpoi[2] = dpoi[0]; dpoi[3] = height - deteheight; // 右上角 dpoi[4] = dpoi[0] + foldchangewidth; dpoi[5] = 0; // 右下角 dpoi[6] = dpoi[4]; dpoi[7] = height; } matrixs[i].setPolyToPoly(spoi, 0, dpoi, 0, 4); } }
第二个问题解决如上:那我们进行第三个问题:如何实现阴影?
public LinearGradient (float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile) 以x0,y0为起点,x1,y1为终点,color0为开始颜色,color1为终止颜色的一个渐变区域绘制
上面这个方法可以实现我们需要的阴影效果;当然,阴影的透明度需要根据折叠的程度来决定;
在本例中,折叠程度和阴影程度取决于边栏滑动的程度!
下面我就上重点代码了:大家注意看代码注释就可以看出实现的原理;重点搜索dispatchDraw方法进行查看:
package com.xy.framework.widget; import com.xy.framework.Utils.Utils; import com.xy.framework.widget.SideView.SideViewinterface; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Paint.Style; import android.graphics.Shader.TileMode; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.RelativeLayout; import android.widget.Scroller; /** * @author 徐晔 * @note 实现侧边栏的View */ public class Sidebarview extends RelativeLayout { /** * @author 徐晔 * @note 侧边栏需要实现需要的配置文件 */ public static interface SidebarConfig { /** 边栏所占用的百分比 */ public int setSidebarpercent(); /** 左边还是右边 */ public int setSidebarPosition(); } /** 元素 */ private class SideElements { /** 上下文 */ private Context mContext; /** 屏幕宽度 */ private int screenwidth; /** 菜单页面所在的页面 */ private RelativeLayout mMenuLayout; /** 侧栏所在的页面 */ private RelativeLayout mSideLayout; /** 配置文件 */ private SidebarConfig mConfig; /** 触摸时需要的临时文件 */ private int menuspace; private int mMotionX, mdeteX; public void setmMotionX(int mMotionX) { this.mMotionX = mMotionX; } } /** 元素 */ private SideElements mElements; /** 平移动画实现类 */ private Scroller mScroller; /** 折叠效果所需要的变量 */ // 需要实现几个折叠页面,建议都是2的倍数 private final int foldcound = 4; // 两个折叠的图片分别需要的矩阵 private Matrix[] matrixs; // 将View切割成两部分的坐标 private Rect[] mRects; private Rect deRect; // 切割前的图片 private Bitmap viewbitmap; // 画阴影 private LinearGradient shadowGrad; private Paint mshadowpaint; public void setElements(int setnumber) { mElements.setmMotionX(setnumber); } public RelativeLayout getmMenuLayout() { return mElements.mMenuLayout; } public Sidebarview(Context context, SidebarConfig sidebarConfig) { super(context); mElements = new SideElements(); initView(context, sidebarConfig); } public Sidebarview(Context context, AttributeSet attrs, SidebarConfig sidebarConfig) { super(context, attrs); mElements = new SideElements(); initView(context, sidebarConfig); } public Sidebarview(Context context, AttributeSet attrs, int defStyle, SidebarConfig sidebarConfig) { super(context, attrs, defStyle); mElements = new SideElements(); initView(context, sidebarConfig); } /** 初始化组件 */ private void initView(Context context, SidebarConfig sidebarConfig) { mScroller = new Scroller(context); // 设置 mElements.mContext = context; mElements.screenwidth = Utils.getScreenSize(context)[0]; mElements.mConfig = sidebarConfig; this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); // 添加菜单页面 mElements.mMenuLayout = new RelativeLayout(context) { //重点方法,不知道为什么我重写这个方法的最好去百度一下 @Override protected void dispatchDraw(Canvas canvas) { // 获取原始坐标 int endX = mElements.mSideLayout.getScrollX(); // 当没有折叠时 if (-endX == mElements.menuspace) { super.dispatchDraw(canvas); return; } int width = getChildAt(0).getMeasuredWidth(); int height = getChildAt(0).getMeasuredHeight(); // 获取到侧边栏的视图 if (getChildAt(0) != null && viewbitmap == null) { viewbitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas2 = new Canvas(viewbitmap); getChildAt(0).draw(canvas2); } endX = Math.abs(endX); //准备绘制所需要的数据 perpareFolddata(width, height); //计算折叠效果需要的数据 getMatrixsbyscroll(0, endX, width, height); Rect src; for (int i = 0; i < foldcound; i++) { src = mRects[i]; canvas.save(); //将效果连接起来 canvas.concat(matrixs[i]); if (deRect == null) { deRect = new Rect(); } // 设置需要绘制到的地方 deRect.set(src.left, src.top, src.left + src.width(), src.height()); // 分别绘制每一块扇形区域 canvas.drawBitmap(viewbitmap, src, deRect, null); // 计算阴影的透明度 int shadowalpha = 255 * (width - (endX - 0)) / width; if (mshadowpaint == null) { mshadowpaint = new Paint(); mshadowpaint.setStyle(Style.FILL); } // 设置阴影的透明度,折叠程度越深,透明度越低,也就是说看起来越暗 mshadowpaint.setAlpha(shadowalpha); if (i % 2 == 0) { shadowGrad = new LinearGradient(i * width / foldcound, 0, (i + 1) * width / foldcound, 0, Color.TRANSPARENT, Color.BLACK, TileMode.CLAMP); } else { shadowGrad = new LinearGradient(i * width / foldcound, 0, (i + 1) * width / foldcound, 0, Color.BLACK, Color.TRANSPARENT, TileMode.CLAMP); } mshadowpaint.setShader(shadowGrad); // 绘制阴影 canvas.drawRect(i * width / foldcound, 0, (i + 1) * width / foldcound, height, mshadowpaint); canvas.restore(); } } }; mElements.mMenuLayout.setId(0); mElements.mMenuLayout.setTag("sidemenu"); LayoutParams mMenuParams = new LayoutParams(mElements.screenwidth * ((100 - mElements.mConfig.setSidebarpercent())) / 100, LayoutParams.MATCH_PARENT); mElements.mMenuLayout.setLayoutParams(mMenuParams); addView(mElements.mMenuLayout); // 添加侧边栏页面 mElements.mSideLayout = new RelativeLayout(context) { /** 平移动画 */ @Override public void computeScroll() { mElements.mMenuLayout.postInvalidate(); if (mScroller.computeScrollOffset()) { mElements.mSideLayout.scrollTo(mScroller.getCurrX(), 0); mElements.mSideLayout.postInvalidate(); } if (mElements.mConfig.setSidebarPosition() % 2 == 0) { if (mElements.mSideLayout.getScrollX() == mElements.menuspace) { mElements.mMenuLayout.invalidate(); return; } if (mElements.mSideLayout.getScrollX() == 0) { return; } mElements.mMenuLayout.scrollTo( (-mElements.menuspace + mElements.mSideLayout .getScrollX()) >> 1, 0); } if (mElements.mConfig.setSidebarPosition() % 2 == 1) { if (mElements.mSideLayout.getScrollX() == 0) { return; } if (mElements.mSideLayout.getScrollX() == mElements.menuspace) { mElements.mMenuLayout.invalidate(); return; } } super.computeScroll(); } }; mElements.mSideLayout.setId(1); mElements.mSideLayout.setTag("sideside"); LayoutParams mSideParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mElements.mSideLayout.setLayoutParams(mSideParams); addView(mElements.mSideLayout); mElements.menuspace = mElements.screenwidth * (100 - mElements.mConfig.setSidebarpercent()) / 100; } /** 给主页添加页面 */ public void addViewtoMenu(View view) { mElements.mMenuLayout.addView(view, new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); } /** 给菜单栏添加页面 */ public void addViewtoSide(View view, SideViewinterface sideViewinterface) { SideView mSideView = new SideView(mElements.mContext, sideViewinterface); mSideView.addView(view); mElements.mSideLayout.addView(mSideView, new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); if (mElements.mConfig.setSidebarPosition() % 2 == 0) { mSideView.setOnTouchListener(new mSidebarTouchListenerLeft()); } if (mElements.mConfig.setSidebarPosition() % 2 == 1) { mSideView.setOnTouchListener(new mSidebarTouchListenerRight()); } } /** 右边滑动时的触摸事件 */ private class mSidebarTouchListenerRight implements OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { int touchX = (int) event.getX(); switch (event.getAction()) { // 按下 case MotionEvent.ACTION_DOWN: mElements.mMotionX = touchX; return true; // 移动 case MotionEvent.ACTION_MOVE: mElements.mdeteX = touchX - mElements.mMotionX; if (mElements.mSideLayout.getScrollX() - mElements.mdeteX + mElements.menuspace >= 0 && mElements.mSideLayout.getScrollX() - mElements.mdeteX <= 0) { mElements.mSideLayout.scrollBy(-mElements.mdeteX, 0); } return true; // 抬起 case MotionEvent.ACTION_UP: if (Math.abs(mElements.mdeteX) < 24) { if (mElements.mSideLayout.getScrollX() == mElements.menuspace) { close(); } } if (mElements.mSideLayout.getScrollX() > -mElements.menuspace >> 1) { startAnimation(mElements.mSideLayout.getScrollX(), 0, 0, 0, 300); } else { startAnimation(mElements.mSideLayout.getScrollX(), 0, -mElements.menuspace, 0, 300); } mElements.mdeteX = 0; mElements.mMotionX = 0; return true; } return false; } } /** 左边触摸时实现的事件 */ private class mSidebarTouchListenerLeft implements OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { int touchX = (int) event.getX(); switch (event.getAction()) { // 按下 case MotionEvent.ACTION_DOWN: mElements.mMotionX = touchX; return true; // 滑动 case MotionEvent.ACTION_MOVE: if (mElements.mMotionX == 0) { mElements.mMotionX = touchX; } mElements.mdeteX = touchX - mElements.mMotionX; if (mElements.mSideLayout.getScrollX() - mElements.menuspace - mElements.mdeteX <= 0 && mElements.mSideLayout.getScrollX() - mElements.mdeteX >= 0) { mElements.mSideLayout.scrollBy(-mElements.mdeteX, 0); } return true; // 抬起 case MotionEvent.ACTION_UP: if (Math.abs(mElements.mdeteX) < 24) { if (mElements.mSideLayout.getScrollX() == mElements.menuspace) { close(); } } if (mElements.mSideLayout.getScrollX() >= mElements.menuspace >> 1) { startAnimation(mElements.mSideLayout.getScrollX(), 0, mElements.menuspace, 0, 300); } else { startAnimation(mElements.mSideLayout.getScrollX(), 0, 0, 0, 300); } mElements.mdeteX = 0; mElements.mMotionX = 0; return true; } return false; } } /** 侧边栏动画 */ private void startAnimation(int startX, int startY, int endX, int endY, int time) { if (!mScroller.computeScrollOffset()) { mScroller.setFinalX(endX); mScroller.setFinalY(endY); mScroller.startScroll(startX, startY, endX - startX, endY - startY, time); mElements.mSideLayout.invalidate(); } } /** * 判断其是否打开 * * @return */ public boolean isopen() { if (mElements.mSideLayout.getScrollX() == 0) { return false; } else { return true; } } /** * 打开侧边栏 */ public void open() { if (!mScroller.computeScrollOffset()) { if (mElements.mConfig.setSidebarPosition() % 2 == 0) { mScroller.setFinalX(mElements.menuspace); mScroller.setFinalY(0); mScroller.startScroll(mElements.mSideLayout.getScrollX(), 0, mElements.menuspace, 0, 600); } else { mScroller.setFinalX(-mElements.menuspace); mScroller.setFinalY(0); mScroller.startScroll( mElements.mSideLayout.getScrollX(), 0, mScroller.getFinalX() - mElements.mSideLayout.getScrollX(), 0, 600); } mElements.mSideLayout.invalidate(); } } /** * 关闭侧边栏 */ public void close() { if (!mScroller.computeScrollOffset()) { if (mElements.mConfig.setSidebarPosition() % 2 == 0) { mScroller.setFinalX(0); mScroller.setFinalY(0); mScroller.startScroll( mElements.mSideLayout.getScrollX(), 0, mScroller.getFinalX() - mElements.mSideLayout.getScrollX(), 0, 600); } else { mScroller.setFinalX(0); mScroller.setFinalY(0); mScroller.startScroll( mElements.mSideLayout.getScrollX(), 0, mScroller.getFinalX() - mElements.mSideLayout.getScrollX(), 0, 600); } mElements.mSideLayout.invalidate(); } } /** 初始化折叠的数据 */ private void perpareFolddata(int width, int height) { // 初始化各个形状变化的矩阵 if (matrixs == null) { matrixs = new Matrix[foldcound]; for (int i = 0; i < foldcound; i++) { matrixs[i] = new Matrix(); } } if (mRects == null) { // 初始化切割图片的位置 mRects = new Rect[foldcound]; // 计算每一个折叠的宽度 int foldchildwidth = width / foldcound; // 计算好切割图片的每一个区域 for (int i = 0; i < foldcound; i++) { mRects[i] = new Rect(i * foldchildwidth, 0, (i + 1) * foldchildwidth, height); } } } /** 根据变化计算矩阵转换 */ private void getMatrixsbyscroll(int startX, int endX, int width, int height) { // 初始化矩阵变化数组 float[] spoi = new float[8], dpoi = new float[8]; // 计算每一个折叠前的宽度 int foldchildwidth = width / foldcound; // 计算每一个折叠后的宽度 int foldchangewidth = (endX - startX) / foldcound; // 根据勾股定理计算出折叠变化的高度 int deteheight = (int) Math.sqrt(Math.pow(foldchildwidth, 2) - Math.pow(foldchangewidth, 2)); for (int i = 0; i < foldcound; i++) { // 图片变化前的坐标 // 左上角 spoi[0] = startX + i * foldchildwidth; spoi[1] = 0; // 左下角 spoi[2] = spoi[0]; spoi[3] = height; // 右上角,纵坐标和左上角一样 spoi[4] = spoi[0] + foldchildwidth; spoi[5] = spoi[1]; // 右下角,横坐标和右上角一样 spoi[6] = spoi[4]; spoi[7] = height; // 图片变化后的坐标,这个要分奇数和偶数 if (i % 2 == 0) { // 偶数情况下 // 左上角 dpoi[0] = startX + i * foldchangewidth; dpoi[1] = 0; // 左下角 dpoi[2] = dpoi[0]; dpoi[3] = height; // 右上角 dpoi[4] = dpoi[0] + foldchangewidth; dpoi[5] = dpoi[1] + deteheight; // 右下角 dpoi[6] = dpoi[4]; dpoi[7] = height - deteheight; } else { // 奇数情况下 // 左上角 dpoi[0] = startX + i * foldchangewidth; dpoi[1] = deteheight; // 左下角 dpoi[2] = dpoi[0]; dpoi[3] = height - deteheight; // 右上角 dpoi[4] = dpoi[0] + foldchangewidth; dpoi[5] = 0; // 右下角 dpoi[6] = dpoi[4]; dpoi[7] = height; } matrixs[i].setPolyToPoly(spoi, 0, dpoi, 0, 4); } } /** 控制组件的位置 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View childView = getChildAt(i); int measuredWidth = childView.getMeasuredWidth(); int measuredHeight = childView.getMeasuredHeight(); if (mElements.mConfig.setSidebarPosition() % 2 == 0) { if (childView.getId() == 0) { childView.layout( mElements.screenwidth * (mElements.mConfig.setSidebarpercent()) / 100, 0, mElements.screenwidth * (mElements.mConfig.setSidebarpercent()) / 100 + measuredWidth, measuredHeight); } else { childView.layout(0, 0, measuredWidth, measuredHeight); } } if (mElements.mConfig.setSidebarPosition() % 2 == 1) { childView.layout(0, 0, measuredWidth, measuredHeight); } } } /** 度量组件的大小 */ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildren(widthMeasureSpec, heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(widthSize, heightSize); } }
相关代码1:
package com.xy.framework.widget; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.RelativeLayout; /** * @author 徐晔 */ public class SideView extends RelativeLayout{ /** * @author 徐晔 * @note 传递触摸事件的接口 */ public static interface SideViewinterface { /**传递onInterceptTouchEvent*/ public boolean onSideInterceptTouchEvent(MotionEvent ev); } private SideViewinterface mSideViewinterface; public SideView(Context context, AttributeSet attrs, int defStyle,SideViewinterface mSideViewinterface ) { super(context, attrs, defStyle); this.mSideViewinterface=mSideViewinterface; } public SideView(Context context, AttributeSet attrs,SideViewinterface mSideViewinterface ) { super(context, attrs); this.mSideViewinterface=mSideViewinterface; } public SideView(Context context,SideViewinterface mSideViewinterface ) { super(context); this.mSideViewinterface=mSideViewinterface; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mSideViewinterface.onSideInterceptTouchEvent(ev); } /** 测量位置 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View childView = getChildAt(i); int measuredWidth = childView.getMeasuredWidth(); int measuredHeight = childView.getMeasuredHeight(); childView.layout(0, 0, measuredWidth, measuredHeight); } } /** 度量大小 */ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildren(widthMeasureSpec, heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(widthSize, heightSize); } }
Activity中的代码:
package com.xy.framework; import com.example.xyframework.R; import com.xy.framework.widget.SideView.SideViewinterface; import com.xy.framework.widget.Sidebarview; import com.xy.framework.widget.Sidebarview.SidebarConfig; import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent; import android.widget.RelativeLayout; public class SidebarActivity extends Activity { private Sidebarview view; private RelativeLayout menu, side; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); menu = (RelativeLayout) getLayoutInflater() .inflate(R.layout.menu, null); side = (RelativeLayout) getLayoutInflater() .inflate(R.layout.side, null); view = new Sidebarview(this, new SidebarConfig() { @Override public int setSidebarpercent() { return 50; } @Override public int setSidebarPosition() { return 1; } }); view.addViewtoMenu(menu); view.addViewtoSide(side, new SideViewinterface() { @Override public boolean onSideInterceptTouchEvent(MotionEvent ev) { // TODO 自动生成的方法存根 return false; ``` }); setContentView(view); } }
其实实现的原理并不难;只要你仔细分析,类似于这种效果,你首先要将它分解开来看;然后分别找到实现相应效果的办法,最终将他组合在一起,那么就可以了;照着这篇介绍的原理,我相信大家可以自己研究一下类似于翻页效果的例子等等;
相关文章推荐
- Android实战技巧之四十:Android5.1.1源代码编译与烧写
- Android(java)学习笔记208:Android中操作JSON数据(Json和Jsonarray)
- 图片加载库Glide
- Android SDK不能在线更新的解决方案
- Android之 特殊字符显示
- Android学习之sharedpreferences加密
- android学习笔记(13) intent Services多线程初步
- Android数据库--创建表和LitePal的基本用法
- 新浪微博开放平台提交审核时Android签名生成
- Android 使用DrawerLayout实现抽屉效果的导航菜单
- Android Material Design 动画实现
- android学习笔记(12) normal Services多线程初步
- 框架模式MVP在Android中的使用
- android切割音视频
- Android中jni工作流
- 深入讲解Android中Activity launchMode
- Android Version
- Android与设计模式:用单一职责则为Activity解耦
- Android开发之消息推送
- CSS绘制Android Robot