Android TV 3D卡片无限循环效果
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事件,做点动画的开始、移动、停止。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。