打造一个带有模拟焦点的影视剧集选集组件
来源:互联网 发布:vue.js获取json数据 编辑:程序博客网 时间:2024/04/18 23:04
过程分析
安卓机顶盒开发中,很多时候都需要我们能够有自定义
View
的能力。而且通常需要处理按键相关的逻辑,其实就是和焦点相关的逻辑。我这里说的焦点并非之前的博文中提到的焦点。前面说到的焦点是一个被标记的View
。我这里说的是视觉上的焦点,就是你能够看到的被标记的位置。通常我们处理焦点问题,可以采用安卓提供的焦点相关的api
。当然我们也可以自己模拟实现焦点。本文将带你实现一个较为简单的模拟焦点的自定义View
。在720p分辨率的安卓盒子上运行效果如下:
代码实现
- 这里贴一下主要的类:
package com.ajay.tvdevelopdemo.widget;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.util.AttributeSet;import android.util.Log;import android.view.KeyEvent;import android.view.View;import java.util.List;/** * 该组件模拟view内部视觉焦点移动 * * @author AjayNiu * @since 2016-11-14 */public class EpisodeChooseLayout extends View { public static final String TAG = "EpisodeChooseLayout"; public static final boolean DEBUG = false; public static final int TEXT_SIZE = 20; public static final String FOCUS_COLOR = "#005DBE"; public static final String UNFOCUS_COLOR = "#B3B5BB"; public static final String UNFOCUS_HASSEEN_COLOR = "#898989"; public static final String FOCUS_TEXT_COLOR = "#FFFFFF"; public static final String UNFOCUS_TEXT_COLOR = "#000000"; private static final int SCROLL_TYPE_EDGE = 0; private static final int SCROLL_TYPE_MIDDLE = 1; private int mScrollType = SCROLL_TYPE_EDGE; private int mTextSize = TEXT_SIZE; private int mFocusItemColor; private int mUnFocusItemColor; private int mUnFocusHasSeenItemColor; private int mFocusTextColor; private int mUnFocusTextColor; // 模拟焦点距控件边缘的距离 private int focusStayExtra; // 可见轴线起始绝对坐标 private int mVisibleStartX; // 可见轴线结束绝对坐标 private int mVisibleEndX; // 上次滚动位置 private int lastScrollPosition; // 实际绘制的条目数据集合 private List<EpisodeChooseItem> mDrawingItems; private Paint mPaint = new Paint(); private Paint mTextPaint = new Paint(); private Rect focusUpRect = new Rect(); private Rect unFocusUpRect = new Rect(); private Rect mVisibleRect = new Rect(); // item之间的水平间隔 private static final int GAP = 1; // 焦点位置 private int mFocusPosition; // 是否得到焦点 private boolean isGainFocus; // 已观看的最大位置 private int mHasSeenMaxPosition; // 文本区域 private Rect mTextRect = new Rect(); public EpisodeChooseLayout(Context context) { super(context); init(); } public EpisodeChooseLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { setFocusable(true); mPaint.setAntiAlias(true); mTextPaint.setAntiAlias(true); mFocusItemColor = Color.parseColor(FOCUS_COLOR); mUnFocusItemColor = Color.parseColor(UNFOCUS_COLOR); mUnFocusHasSeenItemColor = Color.parseColor(UNFOCUS_HASSEEN_COLOR); mFocusTextColor = Color.parseColor(FOCUS_TEXT_COLOR); mUnFocusTextColor = Color.parseColor(UNFOCUS_TEXT_COLOR); } public void initItems(List<EpisodeChooseItem> mInitItems) { if (mInitItems.size() <= 0) { return; } int left = 0; for (int i = 0; i < mInitItems.size(); i++) { EpisodeChooseItem item = mInitItems.get(i); item.position = mInitItems.indexOf(item); item.rect.left = left; item.rect.top = 0; item.rect.right = item.rect.left + item.getWidth(); item.rect.bottom = item.getHeight(); left += item.getWidth(); } this.mDrawingItems = mInitItems; } @Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); if (gainFocus) { isGainFocus = true; invalidate(); } else { isGainFocus = false; invalidate(); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (changed) { mVisibleStartX = focusStayExtra; mVisibleEndX = getMeasuredWidth() - focusStayExtra; mVisibleRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); } } public void updateHasSeenMaxPosition(int mHasSeenMaxPosition) { this.mHasSeenMaxPosition = mHasSeenMaxPosition; invalidate(); } public void setFocusPosition(int mFocusPosition) { this.mFocusPosition = mFocusPosition; invalidate(); } public void setFocusStayExtra(int focusStayExtra) { this.focusStayExtra = focusStayExtra; invalidate(); } private void setScrollType(int type) { mScrollType = type; } public void setTextSize(int mTextSize) { this.mTextSize = mTextSize; } public void setmFocusItemColor(int mFocusItemColor) { this.mFocusItemColor = mFocusItemColor; } public void setmUnFocusItemColor(int mUnFocusItemColor) { this.mUnFocusItemColor = mUnFocusItemColor; } public void setmUnFocusHasSeenItemColor(int mUnFocusHasSeenItemColor) { this.mUnFocusHasSeenItemColor = mUnFocusHasSeenItemColor; } public void setmFocusTextColor(int mFocusTextColor) { this.mFocusTextColor = mFocusTextColor; } public void setmUnFocusTextColor(int mUnFocusTextColor) { this.mUnFocusTextColor = mUnFocusTextColor; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mTextPaint.setTextSize(mTextSize); canvas.save(); canvas.clipRect(mVisibleRect); int translatePosition = 0; if (mScrollType == SCROLL_TYPE_EDGE) { translatePosition = adjustPositionOnScreen(mFocusPosition); } else if (mScrollType == SCROLL_TYPE_MIDDLE) { translatePosition = adjustPositionMiddle(mFocusPosition); } canvas.translate(translatePosition, 0); for (int i = 0; i < mDrawingItems.size(); i++) { if (i == mFocusPosition) { Rect rect = mDrawingItems.get(i).rect; // draw focus up rect if (isGainFocus) { mPaint.setColor(mFocusItemColor); } else { if (i <= mHasSeenMaxPosition) { mPaint.setColor(mUnFocusHasSeenItemColor); } else { mPaint.setColor(mUnFocusItemColor); } } focusUpRect.set(rect.left, rect.top, rect.right - GAP, rect.bottom); canvas.drawRect(focusUpRect, mPaint); if (isGainFocus) { mTextPaint.setColor(mFocusTextColor); } else { mTextPaint.setColor(mUnFocusTextColor); } // draw message String text = mDrawingItems.get(i).getMessage(); mTextPaint.getTextBounds(text, 0, text.length(), mTextRect); int x = mDrawingItems.get(i).rect.left + mDrawingItems.get(i).getWidth() / 2 - mTextRect.width() / 2; int y = mDrawingItems.get(i).rect.top + mDrawingItems.get(i).getHeight() / 2 + mTextRect.height() / 2; canvas.drawText(mDrawingItems.get(i).getMessage(), x, y, mTextPaint); } else { Rect rect = mDrawingItems.get(i).rect; // draw focus up rect if (i <= mHasSeenMaxPosition) { mPaint.setColor(mUnFocusHasSeenItemColor); } else { mPaint.setColor(mUnFocusItemColor); } unFocusUpRect.set(rect.left, rect.top, rect.right - GAP, rect.bottom); canvas.drawRect(unFocusUpRect, mPaint); mTextPaint.setColor(mUnFocusTextColor); // draw message String text = mDrawingItems.get(i).getMessage(); mTextPaint.getTextBounds(text, 0, text.length(), mTextRect); int x = mDrawingItems.get(i).rect.left + mDrawingItems.get(i).getWidth() / 2 - mTextRect.width() / 2; int y = mDrawingItems.get(i).rect.top + mDrawingItems.get(i).getHeight() / 2 + mTextRect.height() / 2; canvas.drawText(mDrawingItems.get(i).getMessage(), x, y, mTextPaint); } } canvas.restore(); } private int adjustPositionMiddle(int focusPosition) { if (focusPosition >= 0 && focusPosition < mDrawingItems.size()) { int left = mDrawingItems.get(focusPosition).rect.left; int width = mDrawingItems.get(focusPosition).getWidth(); int centerX = left + width / 2; int lastPositionRight = mDrawingItems.get(mDrawingItems.size() - 1).rect.right; if (lastPositionRight > getMeasuredWidth()) { if (centerX > getMeasuredWidth() / 2 && centerX < (lastPositionRight - getMeasuredWidth() / 2)) { return getMeasuredWidth() / 2 - centerX; } else if (centerX > (lastPositionRight - getMeasuredWidth() / 2)) { return getMeasuredWidth() - lastPositionRight; } } } return 0; } private int adjustPositionOnScreen(int focusPosition) { if (focusPosition >= 0 && focusPosition < mDrawingItems.size()) { int focusLeft = mDrawingItems.get(focusPosition).rect.left; int focusRight = mDrawingItems.get(focusPosition).rect.right; debugLog("adjustPositionOnScreen focusLeft : " + focusLeft + " mVisibleStartX : " + mVisibleStartX); debugLog("adjustPositionOnScreen focusRight : " + focusRight + " mVisibleEndX : " + mVisibleEndX); debugLog("adjustPositionOnScreen extra : " + (mVisibleEndX - mVisibleStartX)); if (focusLeft < mVisibleStartX) { mVisibleStartX = focusLeft; mVisibleEndX = focusLeft + getMeasuredWidth() - focusStayExtra * 2; if (mFocusPosition == 0) { lastScrollPosition = -mVisibleStartX; } else { lastScrollPosition = -(mVisibleStartX - focusStayExtra); } return lastScrollPosition; } else if (focusRight > mVisibleEndX) { mVisibleEndX = focusRight; mVisibleStartX = focusRight - (getMeasuredWidth() - focusStayExtra * 2); if (mFocusPosition == mDrawingItems.size() - 1) { lastScrollPosition = -(mVisibleStartX - focusStayExtra * 2); } else { lastScrollPosition = -(mVisibleStartX - focusStayExtra); } return lastScrollPosition; } else { return lastScrollPosition; } } return 0; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { if (mFocusPosition > 0) { mFocusPosition--; invalidate(); return true; } } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { if (mFocusPosition < mDrawingItems.size() - 1) { mFocusPosition++; invalidate(); return true; } } else if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { if (onLayoutClickListener != null) { onLayoutClickListener.onLayoutClick(mFocusPosition); } } return super.onKeyDown(keyCode, event); } public interface OnLayoutClickListener { void onLayoutClick(int position); } OnLayoutClickListener onLayoutClickListener; public void setOnLayoutClickListener(OnLayoutClickListener onLayoutClickListener) { this.onLayoutClickListener = onLayoutClickListener; } private void debugLog(String log) { if (DEBUG) { Log.d(TAG, log); } }}
- 完整代码在这里
0 0
- 打造一个带有模拟焦点的影视剧集选集组件
- 带有省略号的分页组件
- 使用Zxing打造带有数字的二维码
- SWING组件学习3:图片以及创建一个带有图片的按钮
- 【Qt】无边框窗体中带有ActiveX组件时的一个BUG
- 一个ASP开发的网吧影视播放程序
- 597放映 每天推荐10部最新 影视剧集 -2010年06月12日
- 冬日剧集——一个女人和两个男人的故事
- 【渐进】设计一个模拟并行的线程同步组件
- 如何模拟一个象窗体一样的控件(标题栏、焦点、拖动、
- flex 带有时分秒的日期组件
- 带有一个UITextView的AlertView
- 一个带有标记的SeekBar
- 一个带有 PlaceHolder 的BRPlaceholderTextView
- 带有一个UITextView的AlertView
- [.NET] 打造一个很简单的文档转换器 - 使用组件 Spire.Office
- 影视的制作
- Java 设置JTextField组件的焦点事件
- 【学习日志】关于Python web开发测试驱动方法
- AngularJS常用指令
- Eigen中编译内联对效率的影响
- 机器学习常见十大算法
- Tempter of the Bone
- 打造一个带有模拟焦点的影视剧集选集组件
- javascript Math对象方法
- 从面试开始_C++常见面试题
- CentOS系统MySQL双机热备配置
- JEECG再创新举,开辟支付宝服务窗开发新时代,业内独家培训 《支付宝服务窗实战培训》
- Spring单元测试加载properties文件
- Struts2文件上传
- 如何实现访问apache时的目录浏览功能(apache file list view)
- 在LINUX注意1和l