Tv开发初体验 焦点移动
来源:互联网 发布:钓鱼岛实际控制权知乎 编辑:程序博客网 时间:2024/06/06 06:46
开发tv项目 与传统app项目的差别其中之一是焦点问题控制,今天就错略说下焦点控制问题,传统app 项目 在做事件触发一般是通过点击和触摸。但是Tv开发由于一般的电视都是要通过遥控器来控制,所以tv项目是要处理遥控器按键的。如何根据遥控器按键来做相应的处理就是问题的关键。
首先第一步就是监听按键的事件。这个可以通过dispatcKeyEvent 方法来处理。获取到了用户按键的事件
获取到了按键 我们就可以 在用户按下按键的时候让某些控件获取焦点。并且通过控件的焦点变化监听 。在里面对该控件进行相应的背景变化或者做相应的动画效果。
第二步 如何移动的时候获取下一个应该拿到焦点的控件。实际开发中可以根据情况来判断。如果相邻的控件是都要获取焦点的可以通过 FocusFinder.getInstance().findNextFocus(this,focusView,View.FOCUS_RIGHT);
这个方法就可以拿到当前焦点view 的下一个焦点view ,如果拿到了我们就可以让下一个焦点view 来requestfocus 获取焦点。这样就实现了相邻view 的焦点移动。
其实焦点移动方面。知道以上两点就可以差不多完成了。下面我附上一个recyclerview 在tv 上的焦点移动 代码。供大家参考
首先是recyclerview 的 他主要处理按键和焦点的移动 这里增加了改变recyclerview 的绘制顺序。避免放大后item被其他相邻item遮挡问题。让焦点item最后绘制。
public class MyRecyclerView extends RecyclerView { private int mSelectedPosition; public MyRecyclerView(Context context) { super(context); init(); } public MyRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public MyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { //启用子视图排序功能 setChildrenDrawingOrderEnabled(true); } @Override public void onDraw(Canvas c) { mSelectedPosition = getChildAdapterPosition(getFocusedChild()); super.onDraw(c); } //改变 绘制顺序让焦点view 最后绘制。避免放大被其他控件遮挡 @Override protected int getChildDrawingOrder(int childCount, int i) { int position = mSelectedPosition; if (position < 0) { return i; } else { if (i == childCount - 1) { if (position > i) { position = i; } return position; } if (i == position) { return childCount - 1; } } return i; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { return ev.getAction() == MotionEvent.ACTION_MOVE || super.dispatchTouchEvent(ev); } @Override public boolean dispatchKeyEvent(KeyEvent event) { boolean result =super.dispatchKeyEvent(event); int dx=this.getChildAt(0).getWidth(); View focusView=this.getFocusedChild(); if(focusView!=null){ switch(event.getKeyCode()){ case KeyEvent.KEYCODE_DPAD_RIGHT: if(event.getAction()==KeyEvent.ACTION_UP){ return true; }else{ View rightView= FocusFinder.getInstance().findNextFocus(this,focusView,View.FOCUS_RIGHT); // 在recyclerview 的最右侧的item 的时候是拿不到下一个焦点view 的。这时候可以通过else 来进行处理。 if(rightView!=null){ //拿到了下一个焦点view 那么让他获取到焦点。 boolean b = rightView.requestFocusFromTouch(); return true; }else{ //如果recyclerview控件宽度不够 显示不全时 可通过次方法移动 recyclerview this.smoothScrollBy(dx,0); return true; } } case KeyEvent.KEYCODE_DPAD_LEFT: if(event.getAction()==KeyEvent.ACTION_UP){ return true; }else{ View leftView=FocusFinder.getInstance().findNextFocus(this,focusView,View.FOCUS_LEFT); if(leftView!=null){ leftView.requestFocusFromTouch(); return true; }else{ this.smoothScrollBy(-dx,0); return true; } } } } return result; }}然后是adapter 的主要处理item 获取到了焦点的动画处理 public abstract class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyHolder> { Context context; public MyRecyclerAdapter(Context context){ this.context=context; } @Override public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) { MyHolder myHolder=new MyHolder(View.inflate(context,R.layout.item_layout,null)); return myHolder; } @SuppressLint("WrongConstant") public void showTaos(String msg){ Toast.makeText(context,msg,0).show(); } @Override public void onBindViewHolder(final MyHolder holder, int position) { holder.itemview.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View view, boolean b) { if (b) { focusStatus(view); } else { normalStatus(view); } } }); if(position==0){ //延时申请焦点 避免布局没有刷新完毕就 执行动画产生空指针等问题 holder.itemview.postDelayed(new Runnable() { @Override public void run() { boolean b = holder.itemview.requestFocus(); } },500); } } private void focusStatus(View itemView){ if(itemView==null){ return; } onItemFocus(itemView); if(Build.VERSION.SDK_INT>=21){ //太高z抽 ViewCompat.animate(itemView).scaleX(1.10f).scaleY(1.10f).translationZ(1).start(); }else{ ViewCompat.animate(itemView).scaleX(1.10f).scaleY(1.10f).start(); ViewGroup parent = (ViewGroup) itemView.getParent(); parent.requestLayout(); parent.invalidate(); } } /** * 当item获得焦点时处理 * * @param itemView itemView */ protected abstract void onItemFocus(View itemView); /** * item失去焦点时 * * @param itemView item对应的View */ private void normalStatus(View itemView) { if (itemView == null) { return; } if (Build.VERSION.SDK_INT >= 21) { ViewCompat.animate(itemView).scaleX(1.0f).scaleY(1.0f).translationZ(0).start(); } else { ViewCompat.animate(itemView).scaleX(1.0f).scaleY(1.0f).start(); ViewGroup parent = (ViewGroup) itemView.getParent(); parent.requestLayout(); parent.invalidate(); } onItemGetNormal(itemView); } /** * 当条目失去焦点时调用 * * @param itemView 条目对应的View */ protected abstract void onItemGetNormal(View itemView); @Override public int getItemCount() { return 20; } class MyHolder extends RecyclerView.ViewHolder{ View itemview; public MyHolder(View itemView) { super(itemView); this.itemview=itemView; } }}mainactivity 中 GridLayoutManager gridLayoutManager=new GridLayoutManager(getApplicationContext(),3); rcv.setLayoutManager(gridLayoutManager); rcv.setAdapter(new MyRecyclerAdapter(getApplicationContext()) { @Override protected void onItemFocus(View itemView) { } @Override protected void onItemGetNormal(View itemView) { } });后面附上一个可以获取焦点自动放大和加边框的LinearLayout 但是边框简单有需要的可以重新配置public class TvLinearLayout extends LinearLayout { private Canvas mcanvas; private Paint paint; public TvLinearLayout(Context context) { super(context); init(); } public TvLinearLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public TvLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } boolean isFocus = false; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Log.i("2024", "===绘制"); if (isFocus) { //画边框 Rect rec = canvas.getClipBounds(); paint = new Paint(); //设置边框颜色 paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); //设置边框宽度 paint.setStrokeWidth(2f); canvas.drawRect(rec, paint); } else { //失去焦点啥也不花 } } @Override protected void onFocusChanged(boolean gainFocus, int direction, @Nullable Rect previouslyFocusedRect) { if (gainFocus) { invalidate(); isFocus = true; if (Build.VERSION.SDK_INT >= 21) { //太高z抽 ViewCompat.animate(this).scaleX(1.10f).scaleY(1.10f).translationZ(1).start(); } else { ViewCompat.animate(this).scaleX(1.50f).scaleY(1.50f).start(); ViewGroup parent = (ViewGroup) getParent(); parent.requestLayout(); parent.invalidate(); } } else { invalidate(); isFocus = false; if (Build.VERSION.SDK_INT >= 21) { ViewCompat.animate(this).scaleX(1.0f).scaleY(1.0f).translationZ(0).start(); } else { ViewCompat.animate(this).scaleX(1.0f).scaleY(1.0f).start(); ViewGroup parent = (ViewGroup) getParent(); parent.requestLayout(); parent.invalidate(); } } super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); } private void init() { //设置 可以获得焦点。 setFocusableInTouchMode(true); //调用setWillNotDraw(false),去掉其WILL_NOT_DRAW flag 才能让LinearLayout 走ondraw 方法看源码可看到 setWillNotDraw(false); }}
实际操作中还发现一个问题就是 item 获取不到焦点。需要我们做一些配置。
- Tv开发初体验 焦点移动
- Android TV 焦点上下左右移动
- android tv 焦点移动特效
- Android TV开发 焦点控制
- Android TV开发 焦点框
- android Tv盒子开发 焦点
- 安卓TV开发(三) 移动智能设备之实现主流TV电视盒子焦点可控UI
- 安卓TV开发(五) 移动智能终端UI之实现主流TV焦点可控UI
- Android TV listView焦点平滑移动
- 安卓Tv开发(一)移动智能电视之焦点控制(触控事件)
- 安卓Tv开发(二)移动智能电视之焦点控制(按键事件)
- Android TV 盒子开发焦点控制
- android tv开发基础知识焦点处理
- Tv开发中Actionbar焦点问题
- Android TV 鼠标空鼠焦点体验改善
- Android TV 鼠标空鼠焦点体验改善
- 移动VR开发初体验
- 安卓TV开发(六) 移动智能终端UI之实现类似GridView的焦点控制FocusView框架
- 665. Non-decreasing Array
- bzoj1008 越狱【动态规划+快速幂】
- [AHOI2009]中国象棋
- 史上最全的maven pom.xml文件教程详解
- String类的学习和使用
- Tv开发初体验 焦点移动
- 01迷宫
- HDU OJ 1074: Doing Homework
- 计算化学程序的实现:一些问题
- Revit API之Document与UIDocument的深入理解【比目鱼原创】
- 运行时权限
- 第4章 数据类型
- D3.js中的Calendar View详解
- 设计模式:原型模式