3D效果切换 ViewGroup中的子view
来源:互联网 发布:什么是java覆盖 编辑:程序博客网 时间:2024/06/18 10:34
效果图先搞一张让大家看看:
灵感来源:https://github.com/zhangke3016/FlipCards
一:介绍
1.首先介绍:
这个自定义的ViewGroup。灵感来源两个,一个是上面Github开源3D动画效果,一个就是在点击项目的退出登陆按钮时,为什么非要弹出一个对话框?其实用一种动画效果,平滑切换至另一个界面,来代替弹出框。(当然可能退出登陆并不是一个恰当的用例,看个人对app设计的理解了)
2.效果如上:
点击退出登陆按钮,3D切换至 包含退出和取消两个按钮的 界面,点击取消-返回,点击退出-提示正在退出登陆。
支持横向纵向3D切换,自动轮播切换。
3.使用:
先看下XML布局文件:
<com.dup.library.FlipCardViewGroup android:id="@+id/group" android:layout_width="250dp" android:layout_height="100dp" android:gravity="center" android:padding="20dp" app:first_item_index="0"> <Button android:id="@+id/btn_first" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/btn_bg1" android:text="退出登录" android:textColor="#ffffff" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <Button android:id="@+id/btn_second" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/btn_bg2" android:text="退出" android:textColor="#ffffff" /> <Button android:id="@+id/btn_third" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/btn_bg3" android:text="取消" android:textColor="#ffffff" /> </LinearLayout> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/text_bg3" android:gravity="center" android:text="退出登录中。。。" android:textColor="#fff" /> </com.dup.library.FlipCardViewGroup>最外层是自定义的ViewGroup,可以看到效果图中有三个子界面,那么xml中把三个子界面作为ViewGroup的子View就可以了。
二.实现思路
1.自定义ViewGroup,为了可以使用Gravity属性,这里继承RelativeLayout。
2.关键点:如何互相切换?咱们先不考虑3D切换效果。就是单纯控制其中子View的显示与不显示。那好了,在onLayout()控制其子View的布局位置大小(下面会详细介绍)。
3.点击其中一个子View中的button,点击事件中调用ViewGroup中方法,来控制是哪一个子View显示。
三.关键代码
1.重写ViewGroup中onLayout()和onMeasure():
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); if (count == 0) { return; } for (int i = 0; i < count; ++i) { View child = getChildAt(i); if (child == null || child.getVisibility() == View.GONE) { continue; } if (i == currentIndex) { child.layout(0 + getPaddingLeft(), 0 + getPaddingTop(), r - l - getPaddingRight(), b - t - getPaddingBottom()); } else { child.layout(0, 0, 0, 0); } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int count = getChildCount(); for (int i = 0; i < count; ++i) { View childView = getChildAt(i); measureChild(childView, widthMeasureSpec, heightMeasureSpec); } }onLayout()方法中:currentIndex就是指的当前要显示的子view的索引。要显示的子view,就使其layout充满父控件(注意padding也要算上)。不显示的子view就使其layout(0,0,0,0)。
2.切换至某一子view公开方法:
/** * 切换至 第index个item * * @param index 要切换至的item index */ public void changeToItem(final int index) { post(new Runnable() { @Override public void run() { int correctIndex = getCorrectIndex(index); if (correctIndex == currentIndex) { return; } currentIndex = correctIndex; final FlipCardAnimation animation = new FlipCardAnimation(0, 180, getWidth(), getHeight(), rotateType); animation.setDuration(duration); animation.setInterpolator(context, interpolator); animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { ((FlipCardAnimation) animation).setCanContentChange(); } }); animation.setOnContentChangeListener(new FlipCardAnimation.OnContentChangeListener() { @Override public void contentChange() { requestLayout(); } }); startAnimation(animation); } }); }首先改变currentIndex值。这里用到了一个开源的3D动画,当旋转至垂直于屏幕(看不到控件时)会回调OnContentChangeListener,回调时我们requestLayout()请求ViewGroup重新执行一遍onLayout()方法。这样就实现了切换效果了。
3.自动切换:
此ViewGroup也是支持自动切换的,下面是开启自动切换方法:
/** * 启动自动转动动画 * * @param fromIndex :开始index * @param toIndex :结束index * @param repeatCount:重复次数 * @param startdelay:开始动画延时 * @param idle:间隔动画延时 * @param isReverse:是否反向播放 eg:如果有三个item<br> * <li>1-1-正向:item播放顺序是:1,2,0,1</li> * <li>1-1-反向:1,0,2,1</li> * <li>0-1-正向:0,1</li> * <li>0-1-反向:0,2,1</li> * <li>2-1-正向:2,0,1</li> * <li>2-1-反向:2,1</li> */ public void playAnimation(final int fromIndex, final int toIndex, final int repeatCount, final long startdelay, final long idle, final boolean isReverse) { play_startIndex = fromIndex; play_endIndex = toIndex; play_repeatCount = repeatCount; play_startDelay = (int) startdelay; play_idle = (int) idle; play_isreverse = isReverse; post(new Runnable() { @Override public void run() { //1.瞬間到指定开始index currentIndex = fromIndex; requestLayout(); mThread = new PlayThread(fromIndex, toIndex, repeatCount, isReverse); if (executor != null) { executor.shutdownNow(); } executor = new ScheduledThreadPoolExecutor(1); executor.scheduleWithFixedDelay(mThread, startdelay, idle, TimeUnit.MILLISECONDS); } }); }这里用到了ScheduledThreadPoolExecutor线程池,用来间断性地启动 切换线程(PlayThread)。这个线程会计算出下一个显示的子View的Index,并调用上面介绍过的切换至某一子View的方法。
4.切换线程的run()方法
@Override public void run() { super.run(); //如果达到重复次数,或不为infinite就停止 if (hasRepeateCount >= repeateCount && repeateCount != -1) { executor.shutdownNow(); return; } //正向进行 if (!isReverse) { currentIndex += 1; if (currentIndex == getChildCount()) { currentIndex = 0; } if (currentIndex == toIndex) { hasRepeateCount++; } changeToItem(currentIndex); } //反向进行 else { currentIndex -= 1; if (currentIndex == -1) { currentIndex = getChildCount() - 1; } if (currentIndex == toIndex) { hasRepeateCount++; } changeToItem(currentIndex); } }就是一系列的获取下一个Index计算。
5.记得有动画的自定义View要及时停止动画
@Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); clearAnimation(); if (executor != null) { executor.shutdownNow(); } }
6.数据持久化也要做
@Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putInt(BUNDLE_FIRST, firstIndex); bundle.putInt(BUNDLE_CURRENT, currentIndex); bundle.putParcelable(BUNDLE_DEF, super.onSaveInstanceState()); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; currentIndex = bundle.getInt(BUNDLE_CURRENT); firstIndex = bundle.getInt(BUNDLE_FIRST); super.onRestoreInstanceState(bundle.getParcelable(BUNDLE_DEF)); } else { super.onRestoreInstanceState(state); } }这里关键点就是保存数据分两部分,一部分是View自己的数据,一部分就是咱们自己定义的数据。
四.总结
其中自定义的3D动画还是挺有意思的,当旋转过了90度是需要特殊处理的,否则view会倒过来显示。。这个自定义动画在博文最开始有附Github地址,大家可以去看看。我使用此动画时也做了修改,新增了横向转动的效果。具体代码和使用放到了本人Github上面:传送门:此项目Github地址链接
0 0
- 3D效果切换 ViewGroup中的子view
- 自定义 ViewGroup 实现子 View 层叠效果
- 让ViewGroup中的子View获得焦点
- 让ViewGroup中的子View获得焦点
- Android自定义Viewgroup切换View带有吸附效果
- ViewGroup中添加子View时附带动画效果
- 2.ViewGroup实现上下切换view
- 使ViewGroup中的View无效
- Layout之间3D切换效果Demo
- 3D切换界面效果代码分享
- android layout 3D切换效果
- viewgroup中删除和添加子view
- viewgroup中删除和添加子view
- 自定义ViewGroup获取子View参数
- ViewGroup与子View之间事件传递
- 自定义ViewGroup和其子View
- 自定义ViewGroup,子View可对换位置
- 在一个ViewGroup中添加子view
- POJ1054 The Troublesome Frog
- winform 自由缩放
- IOS回调白名单URL scheme
- 良好的代码习惯(三)
- 分页页码的前端显示的实现
- 3D效果切换 ViewGroup中的子view
- 创建Android启动界面
- NUOJ 737 石子合并(一)区间DP
- git拉取远程分支并创建本地分支
- 在活动中使用Intent(笔记)
- MyEclipse错误:Servlet execution threw an exception with root cause
- mysql UNIX时间戳与日期的相互转换
- 进程线程中睡眠函数(sleep)被信号中断后失效,处理方式总结
- Spark错误:Lost task 0.0 in stage 10.0 (TID 17, slave1): java.io.FileNotFoundException