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
原创粉丝点击