欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Android TV 3D卡片无限循环效果

程序员文章站 2022-04-14 11:38:01
tv 3d卡片无限循环效果,供大家参考,具体内容如下##前言1、需求:实现3个卡片实现无限循环效果:1-2-3-1-2-3-1…,而且要实现3d效果:中间突出,两侧呈角度显示2、viewpager实现...

tv 3d卡片无限循环效果,供大家参考,具体内容如下

##前言

1、需求:实现3个卡片实现无限循环效果:1-2-3-1-2-3-1…,而且要实现3d效果:中间突出,两侧呈角度显示
2、viewpager实现方式

(1) loopviewpager,有兴趣的同学可以去github上看一下。
(2) 通过定义一个item的个数integer,max,然后设置初始位置为:integer,max/2。

以上方式如果简单的加载图片这种方式还可取,由于需求3个界面内部控件比较多,在加上需要实现自定义的的3d效果,使用viewpager实现难为了小编,于是舍弃只能自己码代码了,欲哭无泪!!!

##思路

自定义view + 属性动画objectanimator
按键事件特殊处理。

##实现方式

1、objectanimator属性动画的知识准备。
2、父不居中自定义scheduleview,view2, view3

<com.base.module.gvclauncher2.ui.scheduleview
        android:id="@+id/schedule_view"
        android:layout_width="@dimen/main_card_width"
        android:layout_height="@dimen/main_card_height"
        android:layout_gravity="center_horizontal"
        android:layout_margintop="@dimen/main_card_margin_top"
        android:focusable="true"
        android:nextfocusleft="@+id/contacts_view"
        android:nextfocusright="@+id/call_view">
    </com.base.module.gvclauncher2.ui.scheduleview>

其中android:layout_gravity=“center_horizontal”,使卡片在界面的正中间,其余两张的卡片也是如此,达到3个view的起始位置一直,这样方便之后的动画旋转。

2.添加自定义scheduleview

public class scheduleview extends basephoneview {

    private static final string tag = "callfragment";
    private static final boolean debug = true;

    private context mcontext;
    private view mrootview;
    private framelayout mmainview;
    private schedulecontract.view mview;
    private schedulecontract.presenter mpresenter;

    public scheduleview(context context) {
        super(context);
        this.mcontext = context;
        initview();
    }

    public scheduleview(context context, attributeset attrs) {
        super(context, attrs);
        this.mcontext = context;
        initview();
    }

    public scheduleview(context context, attributeset attrs, int defstyleattr) {
        super(context, attrs, defstyleattr);
        this.mcontext = context;
        initview();
    }

    private void initview() {
        findview();
        initdata();
    }

    private void findview() {
        mrootview = layoutinflater.from(mcontext).inflate(r.layout.fragment_schedule, this);
        mmainview = (framelayout) mrootview.findviewbyid(r.id.schedule_contains);
        mmainview.setvisibility(view.visible);
    }

    private void initdata() {
        mmainview.removeallviews();
        mview = schedulecontractfactory.createscheduleview(mcontext);
        mmainview.addview((view) mview);
        mpresenter = schedulecontractfactory.createschedulepresenter(mcontext, mview);
        mpresenter.oncreate();
        //这里只是使用mvp的形式添加view.
    }

    @override
    public void clearallfocus() {
    //清除所有的焦点
        if (mview != null) {
            mview.clearallfocus();
        }
    }

    @override
    public void requestfirstfocus() {
    //第一个控件强行指定焦点
        if (mview != null) {
            mview.requestfirstfocus();
        }
    }

    @override
    public void updatelistdata() {
    //更新列表显示
        if (mpresenter != null) {
            mpresenter.reloadconferencelist();
        }
    }

}

其中fragment_schedule.xml中只有一个简单的framelayout. view2 和view3类似。

3. 动画util

(1) 设置3个卡片的初始位置

public final static float run_y = 22.0f;
public final static float run_large_y = 24.0f;
public final static float run_y_negative = -22.0f;
public final static float run_large_y_negative = -24.0f;
public final static float run_x = 1235.0f;
public final static float run_x_negative = -1235.0f;
public final static float run_large_x = 1366.0f;
public final static float run_large_x_negative = -1366.0f;

public void initleftanimator(view leftview) {
        leftview.settranslationx(run_x_negative);//离屏幕中心偏移距离
        leftview.setrotationy(run_y);//旋转角度
        leftview.setalpha(left_right_alpha);//设置透明度
    }

    public void initrightanimator(view rightview) {
        rightview.settranslationx(run_x);//离屏幕中心偏移距离
        rightview.setrotationy(run_y_negative);//旋转角度
        rightview.setalpha(left_right_alpha);//设置透明度
    }

    public void initmidanimator(view midview) {
        //由于初始位置在xml中设定是在正中间,这里就不重新设置偏移量
        midview.setalpha(middle_alpha);
    }

public void midtoleftanimator(final view runview, boolean anim) {
        objectanimator animatorx = objectanimator.offloat(runview, "translationx", 0, run_x_negative); //中间的起始位置未0
        objectanimator animatorz = objectanimator.offloat(runview, "rotationy", 0, run_y);
        objectanimator animator3 = objectanimator.offloat(runview, "alpha", middle_alpha, left_right_alpha);
        mmidtoleftanimator = new animatorset();
        mmidtoleftanimator.play(animatorx).with(animatorz).with(animator3);
        //anim设置是否需要动画执行时间
        if (anim) {
            mmidtoleftanimator.setduration(duration);
        } else {
            mmidtoleftanimator.setduration(0);
        }
        mmidtoleftanimator.addlistener(new animator.animatorlistener() {
            @override
            public void onanimationstart(animator animation) {
                misscrolling = true;
            }

            @override
            public void onanimationend(animator animation) {
                //misscrolling来判断动画是否完成,来控制下一次动画是否需要执行
                misscrolling = false;
            }

            @override
            public void onanimationcancel(animator animation) {

            }

            @override
            public void onanimationrepeat(animator animation) {

            }
        });
        mmidtoleftanimator.start();
    }

    public void midtorightanimator(final view runview, boolean anim) {
        objectanimator animatorx = objectanimator.offloat(runview, "translationx", 0, run_x);
        objectanimator animatorz = objectanimator.offloat(runview, "rotationy", 0, run_y_negative);
        objectanimator animator3 = objectanimator.offloat(runview, "alpha", middle_alpha, left_right_alpha);
        mmidtorightanimator = new animatorset();
        mmidtorightanimator.play(animatorx).with(animatorz).with(animator3);
        if (anim) {
            mmidtorightanimator.setduration(duration);
        } else {
            mmidtorightanimator.setduration(0);
        }
        mmidtorightanimator.addlistener(new animator.animatorlistener() {
            @override
            public void onanimationstart(animator animation) {
                misscrolling = true;
            }

            @override
            public void onanimationend(animator animation) {
                misscrolling = false;
            }

            @override
            public void onanimationcancel(animator animation) {

            }

            @override
            public void onanimationrepeat(animator animation) {

            }
        });
        mmidtorightanimator.start();
    }

    public void righttomidanimator(final view runview, boolean anim) {
        objectanimator animatorx = objectanimator.offloat(runview, "translationx", run_x, 0);
        objectanimator animatorz = objectanimator.offloat(runview, "rotationy", run_y_negative, 0);
        objectanimator animator3 = objectanimator.offloat(runview, "alpha", left_right_alpha, middle_alpha);
        mrighttomidanimator = new animatorset();
        mrighttomidanimator.play(animatorx).with(animatorz).with(animator3);
        if (anim) {
            mrighttomidanimator.setduration(duration);
        } else {
            mrighttomidanimator.setduration(0);
        }
        mrighttomidanimator.addlistener(new animator.animatorlistener() {
            @override
            public void onanimationstart(animator animation) {
                misscrolling = true;
            }

            @override
            public void onanimationend(animator animation) {
                misscrolling = false;
            }

            @override
            public void onanimationcancel(animator animation) {

            }

            @override
            public void onanimationrepeat(animator animation) {

            }
        });
        mrighttomidanimator.start();
    }

    public void lefttomidanimator(final view runview, boolean anim) {
        objectanimator animatorx = objectanimator.offloat(runview, "translationx", run_x_negative, 0);
        objectanimator animatorz = objectanimator.offloat(runview, "rotationy", run_y, 0);
        objectanimator animator3 = objectanimator.offloat(runview, "alpha", left_right_alpha, middle_alpha);
        mlefttomidanimator = new animatorset();
        mlefttomidanimator.play(animatorx).with(animatorz).with(animator3);
        if (anim) {
            mlefttomidanimator.setduration(duration);
        } else {
            mlefttomidanimator.setduration(0);
        }
        mlefttomidanimator.addlistener(new animator.animatorlistener() {
            @override
            public void onanimationstart(animator animation) {
                misscrolling = true;
            }

            @override
            public void onanimationend(animator animation) {
                misscrolling = false;
            }

            @override
            public void onanimationcancel(animator animation) {

            }

            @override
            public void onanimationrepeat(animator animation) {

            }
        });
        mlefttomidanimator.start();
    }

    public void righttoleftanimator(view runview, boolean anim) {
        objectanimator animator1 = objectanimator.offloat(runview, "translationx", run_x, run_large_x);
        objectanimator animator2 = objectanimator.offloat(runview, "rotationy", run_y_negative, run_large_y_negative);
        objectanimator animator3 = objectanimator.offloat(runview, "alpha", left_right_alpha, 0.0f);

        //继续往右偏移
        objectanimator animator4 = objectanimator.offloat(runview, "translationx", run_large_x, run_x_negative);
        objectanimator animator5 = objectanimator.offloat(runview, "rotationy", run_large_y_negative, run_large_y);
        //中途隐藏不显示
        objectanimator animator6 = objectanimator.offloat(runview, "alpha", 0.0f, 0.0f);

        objectanimator animator7 = objectanimator.offloat(runview, "translationx", run_x_negative, run_x_negative);
        //往左偏移显示在左边位置
        objectanimator animator8 = objectanimator.offloat(runview, "rotationy", run_large_y, run_y);
        objectanimator animator9 = objectanimator.offloat(runview, "alpha", left_right_alpha, left_right_alpha);

        //给分段动画设置时间
        if (anim) {
            animator1.setduration(170);
            animator4.setduration(60);
            animator7.setduration(170);
        } else {
            animator1.setduration(0);
            animator4.setduration(0);
            animator7.setduration(0);
        }
//with:同时执行,after(动画1):在动画1之后执行,befor(动画1):在动画1之前执行。
//请注意以下的after(animator1)。表示动画4.5.6在动画1,2,3执行完毕之后同时执行
        mrighttoleftanimator = new animatorset();
        mrighttoleftanimator.play(animator1).with(animator2).with(animator3);
        mrighttoleftanimator.play(animator4).with(animator5).with(animator6).after(animator1);
        mrighttoleftanimator.play(animator7).with(animator8).with(animator9).after(animator4);
        mrighttoleftanimator.addlistener(new animator.animatorlistener() {
            @override
            public void onanimationstart(animator animation) {
                misscrolling = true;
            }

            @override
            public void onanimationend(animator animation) {
                //misscrolling = false;
            }

            @override
            public void onanimationcancel(animator animation) {

            }

            @override
            public void onanimationrepeat(animator animation) {

            }
        });
        mrighttoleftanimator.start();
    }

    public void lefttorightanimator(view runview, boolean anim) {
        objectanimator animator1 = objectanimator.offloat(runview, "translationx", run_x_negative, run_large_x_negative);
        objectanimator animator2 = objectanimator.offloat(runview, "rotationy", run_y, run_large_y);
        objectanimator animator3 = objectanimator.offloat(runview, "alpha", left_right_alpha, 0.0f);

        objectanimator animator4 = objectanimator.offloat(runview, "translationx", run_large_x_negative, run_x);
        objectanimator animator5 = objectanimator.offloat(runview, "rotationy", run_large_y, run_large_y_negative);
        objectanimator animator6 = objectanimator.offloat(runview, "alpha", 0.0f, 0.0f);

        objectanimator animator7 = objectanimator.offloat(runview, "translationx", run_x, run_x);
        objectanimator animator8 = objectanimator.offloat(runview, "rotationy", run_large_y_negative, run_y_negative);
        objectanimator animator9 = objectanimator.offloat(runview, "alpha", left_right_alpha, left_right_alpha);

        if (anim) {
            animator1.setduration(170);
            animator4.setduration(60);
            animator7.setduration(170);
        } else {
            animator1.setduration(0);
            animator4.setduration(0);
            animator7.setduration(0);
        }

        mlefttorightanimator = new animatorset();
        mlefttorightanimator.play(animator1).with(animator2).with(animator3);
        mlefttorightanimator.play(animator4).with(animator5).with(animator6).after(animator1);
        mlefttorightanimator.play(animator7).with(animator8).with(animator9).after(animator4);
        mlefttorightanimator.addlistener(new animator.animatorlistener() {
            @override
            public void onanimationstart(animator animation) {
                misscrolling = true;
            }

            @override
            public void onanimationend(animator animation) {
                //misscrolling = false;
            }

            @override
            public void onanimationcancel(animator animation) {

            }

            @override
            public void onanimationrepeat(animator animation) {

            }
        });
        mlefttorightanimator.start();
}

好了,基本的动画效果就是这些了,其他细节就不贴代码了。

4. scheduleview添加的子view焦点处理

@override
    public void clearallfocus() {
        mconflistview.clearfocus();
        mloginbtn.clearfocus();
        updateallfocus(false);
        updateallclickable(false);
    }

    @override
    public void requestfirstfocus() {
        updateallfocus(true);
        updateallclickable(true);
        if (mpresenter.haslogin()) {
            mconflistview.requestfocus();
        } else {
            mloginbtn.requestfocus();
        }

    }

    private void updateallfocus(boolean focus) {
        mconflistview.setfocusable(focus);
        mloginbtn.setfocusable(focus);
    }

    private void updateallclickable(boolean enabled) {
        mconflistview.setenabled(enabled);
        mloginbtn.setfocusable(enabled);
    }

当scheduleview偏离中间位置时,需要清楚当前界面所有的焦点并使其不能点击。
当scheduleview旋转到中间的时候需要重新使其获取到焦点让其能够点击。

左右旋转控制

@override
    public boolean dispatchkeyevent(keyevent event) {
        int keycode = event.getkeycode();
        int action = event.getaction();
        log.d(tag, "keycode v = " + keycode);
        switch (keycode) {
            case keyevent.keycode_dpad_left:
                if (action == keyevent.action_up) {
                    if (mcurrentitem == itemtag.call && mcallview.islastfocus(0)) {
                        runleftcontrol(true);
                    } else if (mcurrentitem == itemtag.contacts && mcontactsview.islastfocus(0)) {
                        runleftcontrol(true);
                    } else if (mcurrentitem == itemtag.schedule) {
                        runleftcontrol(true);
                    }
                } else if (action == keyevent.action_down && event.getrepeatcount() == 0) {
                    if (mcurrentitem == itemtag.call) {
                        mcallview.savelastfocusid();
                    }
                }
                break;
            case keyevent.keycode_dpad_right:
                if (action == keyevent.action_up) {
                    if (mcurrentitem == itemtag.call && mcallview.islastfocus(1)) {
                        runrightcontrol(true);
                    } else if (mcurrentitem == itemtag.contacts && mcontactsview.islastfocus(1)) {
                        runrightcontrol(true);
                    } else if (mcurrentitem == itemtag.schedule) {
                        runrightcontrol(true);
                    }
                } else if (action == keyevent.action_down && event.getrepeatcount() == 0) {
                    if (mcurrentitem == itemtag.call) {
                        mcallview.savelastfocusid();
                    }
                }
                break;
            case keyevent.keycode_menu:
                if (action == keyevent.action_up) {
                    animatormanager.getinstance().shakeview(mtitlemenuview, 0.5f);
                }
                break;
        }
        return super.dispatchkeyevent(event);
    }

(1)处理方式是在监听遥控器的左右按键的up事件,使其避免在down时候处理旋转太快系统anr
(2)如果界面中含有editview,如果其在界面的边缘,那么左右按键就会和edittext有字符的时候就会对是否是最边沿的判断islastfocus(0)产生影响。解决方案:

<button
            android:id="@+id/go_left_btn"
            android:layout_width="1dp"
            android:layout_height="1dp"
            android:background="@null"
            android:clickable="false"
            android:focusable="true" />
<button
        android:id="@+id/go_right_btn"
        android:layout_width="12dp"
        android:layout_height="match_parent"
        android:background="@null"
        android:clickable="false"
        android:focusable="true"
        android:nextfocusleft="@+id/et_search"
        android:nextfocusright="@+id/go_right_btn" />

在最左边go_left_btn和最右边go_right_btn添加隐形的btn,让其获取焦点,在左右按键up的时候,焦点如果在两个按钮上就实行左右跳转,否则停留在当前界面。

6. 总结

(1)、实现方式其实还是比较简单的,view+animator.
(2)、后续需要实现鼠标拖拽旋转效果,思路:监听按键的down/move/up事件,做点动画的开始、移动、停止。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。