利用com.graphics.Camera 模拟ViewPager布局3D效果
2016-11-02 13:43
393 查看
学习了很多自定义View的知识,终于有勇气自己写一个Demo的勇气了,还是要多实践啊!!!!!!!!!
记得在AndroidManifest.xml中关闭硬件加速!!!!!!!
需要掌握的内容:坐标系等基础知识,View的绘制过程,画布的操作,Matrix原理,Matrix Camera原理,事件分发机制等。
这里我推荐一个网站,里面的内容很丰富也很有趣
http://www.gcssloop.com/customview/CustomViewIndex
下面介绍如何开始下笔!!!
首先我们要创建一个类,并继承ViewGroup,规定一些变量,用途都在注释里写了,还有构造函数
然后就是onLayout onMeasure方法
下面开始重头戏,根据手指移动的距离,使ViewGroup滚动起来
这时已经实现了循环的左右平移,想要实现3D的效果还需要下面的步骤
现在已经可以实现3D的效果了,实现的过程我写了很清楚的注释。主要实现3D的效果还是靠Camera和Matrix类,具体的原理还是需要自己去研究了。
源码:https://github.com/Techck/3DFlipViewPager
记得在AndroidManifest.xml中关闭硬件加速!!!!!!!
android:hardwareAccelerated="false"
需要掌握的内容:坐标系等基础知识,View的绘制过程,画布的操作,Matrix原理,Matrix Camera原理,事件分发机制等。
这里我推荐一个网站,里面的内容很丰富也很有趣
http://www.gcssloop.com/customview/CustomViewIndex
下面介绍如何开始下笔!!!
首先我们要创建一个类,并继承ViewGroup,规定一些变量,用途都在注释里写了,还有构造函数
public enum State { ToPre, ToNext } private State state;//标记滚动的状态 private int mWidth, mHeight;//ViewGroup的宽高 private float mDownX, mMoveX = 0;//按下的坐标和移动时的坐标 private VelocityTracker mVelocityTracker;//测速 private float mAngle = 90;//两个item间的夹角 private int standerSpeed = 200;//规定的手指移动速率 private int a = -2500;//阻力 private Scroller mScroller; private Camera mCamera; private Matrix mMatrix; private boolean flag = false;//标记是否在增加view public MyView(Context context){ this(context, null); } public MyView(Context context, AttributeSet attributeSet){ super(context, attributeSet); //这是一个滚动计算器,可以计算出滚动一段距离的中间过程 mScroller = new Scroller(context); mCamera = new Camera(); mMatrix = new Matrix(); }
然后就是onLayout onMeasure方法
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec); mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childLeft = 0; //因为一个ViewGroup里有很多个子布局,这些都在布局文件里写好的,不懂的可以看看源码 for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (child.getVisibility() == VISIBLE) { child.layout(childLeft, 0, childLeft + child.getMeasuredWidth(), child.getMeasuredHeight()); childLeft = childLeft + child.getMeasuredWidth(); } } }
下面开始重头戏,根据手指移动的距离,使ViewGroup滚动起来
@Override public boolean onTouchEvent(MotionEvent event) { //创建一个速度计算器,可以计算出手指移动时的速率 if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } //将这个事件添加到速度计算器中 mVelocityTracker.addMovement(event); //当事件是移动时 if (event.getAction() == MotionEvent.ACTION_MOVE){ //拿到当前的坐标x float x = event.getX(); //用之前记录的值减去x,得到移动的距离 int tempX = (int)(mDownX - x); //将当前的x记录下来 mDownX = x; //根据移动的距离tempX滚动ViewGroup scrollBy(tempX, 0); //根据距离判断首尾view的添加,保证循环滚动 changeView(); }else if(event.getAction() == MotionEvent.ACTION_DOWN){ //如果事件是按下,记录当前按下的坐标x mDownX = event.getX(); }else if(event.getAction() == MotionEvent.ACTION_UP){ //通过一个速率的单位计算速率的值 mVelocityTracker.computeCurrentVelocity(1000); //往左滑小于0 往右滑大于0 float xVelocity = mVelocityTracker.getXVelocity(); //滑动的速度大于规定的速度时,开始自动减速滑动直至静止 if (xVelocity > standerSpeed) { //往前翻滚 state = State.ToPre; //根据重力加速度计算需要滑动的路程 float s = xVelocity * (-xVelocity / a) / 2; //计算需要滑动的时间 int t = (int) (-xVelocity / a * 2000); //开始计算滑动过程 mScroller.startScroll(0, 0, (int) s, 0, t); }else if(xVelocity < -standerSpeed){ //往后翻滚 state = State.ToNext; //根据重力加速度计算需要滑动的路程 //消掉速度的单位 xVelocity = -xVelocity; float s = xVelocity * (-xVelocity / a) / 2; //计算需要滑动的时间 int t = (int) (-xVelocity / a * 2000); //开始计算滑动过程 mScroller.startScroll(0, 0, (int) s, 0, t); } } return true; } /** * 这个方法主要是用来在手指抬起后,计算ViewGroup还要再继续滚动多远 */ @Override public void computeScroll() { //当滚动计算器还在计算时,并且现在没有在改变子View的位置 if (mScroller.computeScrollOffset() && flag == false) { if (state == State.ToPre) { //拿到滚动计算器计算出的值,并与上一次的值相减,得出应该滚动的距离 int tempX = (int) (mScroller.getCurrX() - mMoveX); //将当前的值记录下来 mMoveX = mScroller.getCurrX(); //使ViewGroup滚动-tempX的距离,注意正负方向 scrollBy(-tempX, 0); }else if(state == State.ToNext){ //拿到滚动计算器计算出的值,并与上一次的值相减,得出应该滚动的距离 int tempX = (int) (mScroller.getCurrX() - mMoveX); //将当前的值记录下来 mMoveX = mScroller.getCurrX(); //使ViewGroup滚动tempX的距离,注意正负方向 scrollBy(tempX, 0); } //刷新界面 invalidate(); } //当这一次自动计算结束时,给mMoveX清零 if(mScroller.isFinished()) mMoveX = 0; //判断一下是否需要在首尾添加界面 changeView(); } /** * 当超过规定位置时,增加首尾的页数,达到拼接的效果 */ private void changeView(){ if (getScrollX() < 20){ addPre(); LogUtil.d("TAG", "前面加了一页"); }else if(getScrollX() > (getChildCount() - 1) * mWidth - 20){ addNext(); LogUtil.d("TAG", "后面加了一页"); } } /** * 把第一个item移动到最后一个item位置 */ private void addNext() { int childCount = getChildCount(); View view = getChildAt(0); removeViewAt(0); addView(view, childCount - 1); scrollBy(-mWidth, 0); } /** * 把最后一个item移动到第一个item位置 */ private void addPre() { int childCount = getChildCount(); View view = getChildAt(childCount - 1); flag = true; removeViewAt(childCount - 1); addView(view, 0); scrollBy(mWidth, 0); flag = false; }
这时已经实现了循环的左右平移,想要实现3D的效果还需要下面的步骤
/** * 在ViewGroup中需要重写这个方法 */ @Override protected void dispatchDraw(Canvas canvas) { //遍历子View,并画出来 for (int i = 0; i < getChildCount(); i++) { //画3D的方法 drawScreen(canvas, i, getDrawingTime()); } //如果不想要3D效果只想实现平移,注释掉上面的代码,开启下面的代码即可 // super.dispatchDraw(canvas); } /** * 画3D效果 */ private void drawScreen(Canvas canvas, int i, long drawingTime){ int curScreenX = mWidth * i; //屏幕中不显示的部分不进行绘制 if (getScrollX() + mWidth < curScreenX) { return; } if (curScreenX < getScrollX() - mWidth) { return; } //计算中心坐标 float centerX = (getScrollX() > curScreenX) ? curScreenX + mWidth : curScreenX; float centerY = mHeight / 2; //计算角度 float degree = mAngle * (getScrollX() - curScreenX) / mWidth; if (degree > 90 || degree < -90) { return; } canvas.save(); //利用Camera进行一次旋转拍照 mCamera.save(); mCamera.rotateY(-degree); //并将结果保存在Matrix矩阵中 mCamera.getMatrix(mMatrix); mCamera.restore(); //将矩阵移动到视图中心位置 mMatrix.preTranslate(-centerX, -centerY); mMatrix.postTranslate(centerX, centerY); //将矩阵设置到画布上 canvas.concat(mMatrix); //画子View drawChild(canvas, getChildAt(i), drawingTime); canvas.restore(); }
现在已经可以实现3D的效果了,实现的过程我写了很清楚的注释。主要实现3D的效果还是靠Camera和Matrix类,具体的原理还是需要自己去研究了。
源码:https://github.com/Techck/3DFlipViewPager
相关文章推荐
- 利用ViewPager实现3D画廊效果及其图片加载优化
- Android 利用ViewPager+GridView实现首页导航栏布局分页效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果(转载)
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果(牛逼)
- ViewPager和Fragment结合,利用(HorizontalScrollView)实现指示器与ViewPager同时滑动的动态效果
- 【Android 界面效果27】利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- 利用viewpager实现图片左右循环滑动效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android 利用ViewPager、PagerTabStrip实现多页面滑动效果
- Android开发之利用ViewPager实现导航页和滑动标签页效果
- Android 利用ViewPager实现导航页滑动效果
- Android利用Camera实现中轴3D卡牌翻转效果
- 布局demo三:viewPager实现翻页效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android-UI布局---RecyclerView学习(二)利用它做的相册集效果
- android自定义ViewPager之——3D效果应用
- 利用viewpager、Fragment、pagertabStrip 实现多页面滑动效果
- 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- android.graphics.Camera 仿3D效果
- 利用Viewpager制作滚动游标效果