Android高效的EPG界面实现方式
你现在没有在为怎么去实现EPG界面而烦恼呢?仔细研究下我这篇博客后,肯定会对你有很大的帮助的。我这实现后的效果最大的特点就是UI响应速度很快(上下左右切换时,UI能很快的刷新数据)。
首先看下效果图:
最开始进入EPG界面时,焦点在Genres列表里,按右键后,整个界面会往左滑动,直到Genres列表完全消失,然后把焦点落在program上。
上面的UI是用Fragment的方式实现的,UI布局看下面的XML相信你很快能明白。需要注意的就是红色框框里layout_width的值,它是全屏宽度+group listview的宽度。上面说的向左滑动时,滑动的距离就是group listview的宽度。
加载EPG数据,可以有两种方式:
1)把所有channel的EPG一次性加载到内存里。这样做的优点是:用户进行操作时,不需要再去做数据的加载动作,毕竟要耗时的,只需要从内存里取数据,响应比较及时;缺点就是:当EPG数据比较多时,有的时候数据有40M左右,这样就很占用内存,对于内存比较小的机器就会有很大的影响。
2)始终只加载当前显示的那些节目的EPG,当然如果内存中有了就不用去加载了。这种做法的优点很明显了,节省了大量内存;缺点就是:对于用户的操作,有些情况需要再次去加载EPG数据,UI上的数据刷新会有点延后。
我采用的是第2种方法,从实际效果看,不影响体验感!具体逻辑如下:
1)所有频道的节目信息保存在内存里,由ProgramsDataManager管理。
2)初次显示EPG主界面时,从数据库里加载当前可见频道当天的EPG信息,并保存到内存。
3)上下切换频道时,切换动作停止后,如果内存里有当前可见频道从当天0点时刻到当前页天的24点时刻的EPG信息就直接显示,否则从数据库里读取EPG数据到内存,然后显示。
4)上下切换频道时,当前页的时间可能是后续某天时间,切换停住后,如果这些天的EPG在内存里都有,直接显示;否则会清除这些可见频道已经存在内存里的EPG数据,然后再从数据里读取这些可见频道这些天的EPG到内存,然后显示。
5)day+时,如果内存里已经存有了可见频道day+的EPG数据,直接显示就行了。否则会清除这些可见频道已经存在内存里的EPG数据,然后从数据库里读取可见频道从当天0点时刻到day+天24点之间的EPG数据到内存,然后显示。
6)day-时,按照day+的逻辑,实际上要显示的数据已经在内存里有了,直接显示就行了。
7)为了节省内存,每当EPG主界面退出后,会把内存里的EPG数据清除掉。
下面介绍主要代码
EpgMenuFragment相关代码:
//相关变量
private static final int UI_MSG_GROUP_LIST_GET_FOCUS = 101;
private static final int UI_MSG_REFRESH_TIME_LINE = 102;
private static final int UI_MSG_REDRAW_EGPVIEW = 103;
private static final int UI_MSG_UPDATE_GROUP_CHANNEL_DATA = 104;
private static final int UI_MSG_SHOW_NEXT_DAY_EPG = 105;
private static final int UI_MSG_SHOW_PREV_DAY_EPG = 106;
private static final int SUB_MSG_LOAD_CHANNEL_DATA = 1001;
private static final int SUB_MSG_LOAD_CHANNELS_PROGRAM_DATA = 1002;
private IChannelDataManager mChannelDataManager;
private FavoriteManager mFavoriteManager;
private DvrManager dvrManager;
private MonitorProgramHelper monitorProgramHelper;
public boolean mHidden = true;
public ObjectAnimator mTransXAnimator;
private View root_view;
private LinearLayout genresLinearLayout;
private LinearLayout mLlupper;
private ListView mGroupListView;
private EpgGroupListAdapter mGroupListAdapter = null;
private ListView mChannelListView;
private ChannelListAdapter mChannelListAdapter = null;
private int lastFirstItemViewBottom;
private boolean channelListFirstItemScreenLocationChanged = false;
private ObjectAnimator animator1;
private ObjectAnimator animator2;
private EpgView mEpgView;
private TextView curDataWeek;
//存放当前组的channel
private ArrayList<Channel> mCurrentGroupChannels;
public ArrayList<ScheduleRecording> mScheduleData; // timer数据
private ArrayList<EpgChannelGroup> mAllGroupList = new ArrayList<>();
private HandlerThread subThread = new HandlerThread("SubThread");
private Handler mSubHandler;
private boolean mChangedGroup = false;
private int mCurrentGroupPosition = 0;
private int mLastGroupPosition = 0;
private EpgChannelGroup mCurrentGroupInfo;
//当前播放节目的id,该id的值在tv.db数据库中
private long mCurrentChannelId = 0;
//节目播放后记录该节目的索引位置
private int mLastChannelPosition = 0;
private long left_key_interval = 0;
private TvClock mTvClock;
public static EpgMenuFragment newInstance() {
EpgMenuFragment epgMenuFragment = new EpgMenuFragment();
return epgMenuFragment;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
startSubThread();
}
private void startSubThread() {
subThread.start();
mSubHandler = new Handler(subThread.getLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case SUB_MSG_LOAD_CHANNEL_DATA:
//加载某个组的channel
Log.d(TAG, "receive SUB_MSG_LOAD_CHANNEL_DATA");
EpgChannelGroup channel_group = (EpgChannelGroup) msg.obj;
loadChannelDataByGroup(channel_group);
mEpgMenuUIHandler.sendEmptyMessage(UI_MSG_UPDATE_GROUP_CHANNEL_DATA);
break;
case SUB_MSG_LOAD_CHANNELS_PROGRAM_DATA:
//加载索引从index开始的channels的EPG信息
int index = msg.arg1;
int needClearData = msg.arg2;
Log.d(TAG, "receive SUB_MSG_LOAD_CHANNELS_PROGRAM_DATA, index = " + index + ", needClearData = " + needClearData);
//今天的凌晨0点时刻,即查询的起始时间
long queryStartTime = EPGUtils.getDayStartTime(mEpgView.getTimeOffset());
//当前页所属天的凌晨0点时刻
long firstOfCurrentPageDay = EPGUtils.getDayStartTime(mEpgView.getTimeLowerBoundary());
//Log.d("wujiang", "receive SUB_MSG_LOAD_CHANNELS_PROGRAM_DATA, firstOfCurrentPageDay = " + firstOfCurrentPageDay);
//当前页所属天的晚上24点时刻
long endOfCurrentPageDay = firstOfCurrentPageDay + EPGUtils.DAY_OF_SECONDS * EPGUtils.ONE_THOUSAND;
//Log.d("wujiang", "receive SUB_MSG_LOAD_CHANNELS_PROGRAM_DATA, endOfCurrentPageDay = " + endOfCurrentPageDay);
loadChannelsProgramFromDatabase(index, queryStartTime, endOfCurrentPageDay, needClearData);
break;
}
}
};
}
/**
* 从数据库里加载当前页节目的EPG信息
* @param start_channel_index
*/
private void loadChannelsProgramFromDatabase(int start_channel_index, long start_time, long end_time, int needClearData) {
ArrayList<Long> queryChannelsArrayList = new ArrayList<>();
//Log.d("wujiang", "loadChannelsProgramFromDatabase: start_time = " + TimeUtil.millis2String(start_time));
//Log.d("wujiang", "loadChannelsProgramFromDatabase: end_time = " + TimeUtil.millis2String(end_time));
int current_index = start_channel_index;
int count;
for (count = 0; current_index < mCurrentGroupChannels.size() && count < EpgView.MaxItemCountInOnePage + 1; count++, current_index++) {
queryChannelsArrayList.add(mCurrentGroupChannels.get(current_index).getId());
//Log.d("wujiang", "loadChannelsProgramFromDatabase: channel id = " + mCurrentGroupChannels.get(current_index).getId());
//Log.d("wujiang", "loadChannelsProgramFromDatabase: channel name = " + mCurrentGroupChannels.get(current_index).getDisplayName());
}
ProgramsDataManager.getInstance().queryChannelsProgramFromDatabase(start_time, end_time, queryChannelsArrayList, needClearData);
}
//得到某个组下面的channel,根据自己的情况来实现
private void loadChannelDataByGroup(EpgChannelGroup channelGroup) {
mCurrentGroupChannels.clear();
mCurrentGroupChannels.addAll(((TvActivity) getActivity()).getChannelDataManager().filterChannelList(channelGroup.getType(), channelGroup.getId()));
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initData();
initView();
}
private void initData() {
mHidden = false;
mAlarmDataManager = (AlarmDataManager) DataFactory.get().getManager(TvApplication.getAppContext(), DataFactory.ALARM_MANAGER);
mChannelDataManager = (IChannelDataManager) DataFactory.get().getManager(TvApplication.getAppContext(), DataFactory.CHANNEL_MANAGER);
mFavoriteManager = ((FavoriteManager) DataFactory.get().getManager(TvApplication.getAppContext(), DataFactory.FAVORITE_MANAGER));
dvrManager = (DvrManager) DataFactory.get().getManager(TvApplication.getAppContext(), DataFactory.DVR_MANAGER);
ProgramsDataManager.getInstance().addListener(queryFinishListener);
mTvClock = new TvClock(getContext());
if (mCurrentGroupChannels == null) {
mCurrentGroupChannels = new ArrayList<>();
}
getAllGroups();
//显示ALL组的节目
mLastGroupPosition = mCurrentGroupPosition = FilterType.ALL;
if (mGroupListAdapter != null) {
mGroupListAdapter.setCurrentGroupPosition(mCurrentGroupPosition);
mGroupListAdapter.setCurrentGroupInfo(mAllGroupList.get(FilterType.ALL));
mGroupListAdapter.notifyDataSetChanged();
mGroupListView.setSelection(0);
}
}
//得到所有Group的数据(根据自己平台情况实现)
private void getAllGroups() {
if (mAllGroupList != null) {
mAllGroupList.clear();
} else {
mAllGroupList = new ArrayList<>();
}
mAllGroupList.add(new EpgChannelGroup("All", -1, FilterType.ALL));
mAllGroupList.add();...
}
private void initView() {
root_view = getView().findViewById(R.id.root_view);
genresLinearLayout = getView().findViewById(R.id.ll_favourite_group);
mLlupper = getView().findViewById(R.id.ll_upper);
curDataWeek = getView().findViewById(R.id.cur_data_week);
mEpgView = getView().findViewById(R.id.epg_view);
findGroupListView();
findChannelListView();
root_view.postDelayed(new Runnable() {
@Override
public void run() {
mGroupListView.requestFocus();
}
}, 300);
mEpgMenuUIHandler.sendEmptyMessageDelayed(UI_MSG_REFRESH_TIME_LINE, 60 * 1000);
}
private void findGroupListView() {
mGroupListView = getView().findViewById(R.id.lv_group_list);
mGroupListAdapter = new EpgGroupListAdapter(getContext(), mAllGroupList);
mGroupListAdapter.setCurrentGroupPosition(FilterType.ALL);
mGroupListAdapter.setCurrentGroupInfo(mAllGroupList.get(FilterType.ALL));
mGroupListView.setAdapter(mGroupListAdapter);
mGroupListView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
setCurrentGroupPosition(position);
setCurrentGroupInfo(mGroupListAdapter.getItem(position));
setGroupListViewBackground(mLastGroupPosition, parent);
setGroupListViewBackground(position, parent);
mLastGroupPosition = position;
mSubHandler.removeMessages(SUB_MSG_LOAD_CHANNEL_DATA);
Message msg = mSubHandler.obtainMessage();
msg.what = SUB_MSG_LOAD_CHANNEL_DATA;
msg.obj = mGroupListAdapter.getItem(position);
mSubHandler.sendMessageDelayed(msg, 300);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
mGroupListView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean focus) {
if (focus) {
genresLinearLayout.post(new Runnable() {
@Override
public void run() {
float TranslationX = mLlupper.getTranslationX();
float end_position = 0;
animator1 = ObjectAnimator.ofFloat(mLlupper, "translationX", TranslationX, end_position);
animator1.setDuration(300);
animator1.start();
}
});
} else {
genresLinearLayout.post(new Runnable() {
@Override
public void run() {
float TranslationX = mLlupper.getTranslationX();
float end_position = TranslationX - genresLinearLayout.getWidth() - getResources().getDimensionPixelSize(R.dimen.listviews_space);
animator2 = ObjectAnimator.ofFloat(mLlupper, "translationX", TranslationX, end_position);
animator2.setDuration(300);
animator2.start();
}
});
}
}
});
}
public void setCurrentGroupPosition(int group_position) {
mCurrentGroupPosition = group_position;
if (mGroupListAdapter != null) {
mGroupListAdapter.setCurrentGroupPosition(group_position);
}
}
public void setCurrentGroupInfo(EpgChannelGroup epgChannelGroup) {
mCurrentGroupInfo = epgChannelGroup;
Log.d(TAG, "setCurrentGroupInfo: mCurrentGroupInfo name = " + mCurrentGroupInfo.getName());
if (mGroupListAdapter != null) {
mGroupListAdapter.setCurrentGroupInfo(epgChannelGroup);
}
}
private void setGroupListViewBackground(int position, AdapterView<?> parent) {
if (parent.getFirstVisiblePosition() <= position && position <= parent.getLastVisiblePosition()) {
View convertView = parent.getChildAt(position - parent.getFirstVisiblePosition());
if (position >= 0 && mGroupListAdapter.getCount() > position && mCurrentGroupPosition == position) {
convertView.setBackgroundResource(R.drawable.sel_epg_group_list_selected);
} else {
convertView.setBackgroundResource(R.drawable.sel_epg_group_list_normal);
}
}
}
private void findChannelListView() {
mChannelListView = getView().findViewById(R.id.lv_channel_list);
mChannelListView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
if (KeyEvent.ACTION_DOWN != keyEvent.getAction()) {
return false;
}
switch (keyCode) {
case KeyEvent.KEYCODE_PAGE_DOWN:
KeyEvent downEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
mChannelListView.dispatchKeyEvent(downEvent);
return true;
case KeyEvent.KEYCODE_PAGE_UP:
KeyEvent upEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
mChannelListView.dispatchKeyEvent(upEvent);
return true;
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
int current_position = mChannelListView.getSelectedItemPosition();
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
if (current_position == 0) {
mChannelListView.setSelection(mChannelListView.getCount() - 1);
return true;
}
} else {
if (current_position == (mChannelListView.getCount() - 1)) {
mChannelListView.setSelection(0);
return true;
}
}
break;
}
return false;
}
});
mChannelListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long l) {
long current_time = mTvClock.currentTimeMillis();
if (mEpgView.currentProgram == null) {
return;
}
long start_time = mEpgView.currentProgram.getStartTimeUtcMillis();
//Log.d(TAG, "mChannelListView's onItemClick, start_time = " + start_time);
if (start_time > current_time) {
//显示EPG的详情信息
showDetailDialogFullWindowDialog(mEpgView.currentProgram);
return;
}
Channel channel = mChannelListAdapter.getItem(position);
setCurrentChannelId(channel.getId());
//Log.d("wujiang", "mChannelListView's onItemClick, 1 mLastChannelPosition = " + mLastChannelPosition);
setChannelListViewBackground(mLastChannelPosition, parent);
setChannelListViewBackground(position, parent);
mLastChannelPosition = position;
//Log.d("wujiang", "mChannelListView's onItemClick, 2 mLastChannelPosition = " + mLastChannelPosition);
//去播放channel
((TvActivity) getActivity()).checkNeedStopDvrWhenTuneChannel(channel);
((TvActivity) getActivity()).getNesFragmentManager().hideAll();
}
});
mChannelListView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long l) {
Log.d(TAG, "mChannelListView's onItemSelected");
mEpgMenuUIHandler.removeMessages(UI_MSG_REDRAW_EGPVIEW);
if (mEpgView.currentChannelPosition >= 0) {
if (mChannelListView.isFocused()) {
mEpgView.setDrawNeedSelect(true);
}
if (position > mEpgView.currentChannelPosition) {
mEpgView.moveToDestPosition(position, EpgView.DOWN_DIRECTION);
} else if (position < mEpgView.currentChannelPosition) {
mEpgView.moveToDestPosition(position, EpgView.UP_DIRECTION);
}
}
//如果刚刚是切换组了,就不要再去加载EPG,因为切组那里会去查询当前页节目的EPG数据。
if (mChangedGroup) {
mChangedGroup = false;
Log.d(TAG, "mChannelListView's onItemSelected: 刚刚已经切换组了,现在不需要重新去加载EPG。");
return;
}
mSubHandler.removeMessages(SUB_MSG_LOAD_CHANNELS_PROGRAM_DATA);
//去数据里查询当前页节目的EPG数据
Message obtainMessage = mSubHandler.obtainMessage();
obtainMessage.what = SUB_MSG_LOAD_CHANNELS_PROGRAM_DATA;
obtainMessage.arg1 = mChannelListView.getFirstVisiblePosition();
obtainMessage.arg2 = 0;
mSubHandler.sendMessageDelayed(obtainMessage, 500);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
mChannelListView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean focus) {
Log.d(TAG, "mChannelListView's onFocusChange: focus = " + focus);
if (focus) {
mEpgView.setDrawNeedSelect(true);
mEpgMenuUIHandler.sendEmptyMessageDelayed(UI_MSG_REDRAW_EGPVIEW, 200);
} else {
mEpgView.setDrawNeedSelect(false);
}
}
});
mChannelListAdapter = new ChannelListAdapter(getContext(), mCurrentGroupChannels);
mChannelListView.setAdapter(mChannelListAdapter);
if (mGroupListAdapter.getCount() > 0) {
mSubHandler.removeMessages(SUB_MSG_LOAD_CHANNEL_DATA);
Message msg = mSubHandler.obtainMessage();
msg.what = SUB_MSG_LOAD_CHANNEL_DATA;
msg.obj = mGroupListAdapter.getItem(0);
mSubHandler.sendMessageDelayed(msg, 30);
}
}
public void setCurrentChannelId(long channelId) {
mCurrentChannelId = channelId;
if (mChannelListAdapter != null) {
mChannelListAdapter.setCurrentChannelId(channelId);
}
}
private void setChannelListViewBackground(int position, AdapterView<?> parent) {
//Log.d("wujiang", "setChannelListViewBackground: position = " + position);
//Log.d("wujiang", "setChannelListViewBackground: getFirstVisiblePosition = " + parent.getFirstVisiblePosition());
//Log.d("wujiang", "setChannelListViewBackground: getLastVisiblePosition = " + parent.getLastVisiblePosition());
if (parent.getFirstVisiblePosition() <= position && position <= parent.getLastVisiblePosition()) {
View convertView = parent.getChildAt(position - parent.getFirstVisiblePosition());
if ((position >= 0)
&& (mChannelListAdapter.getCount() > position)
&& (mCurrentChannelId == mChannelListAdapter.getItem(position).getId())) {
//Log.d("wujiang", "setChannelListViewBackground 1111 selected");
convertView.setBackgroundResource(R.drawable.sel_epg_channel_list_selected);
} else {
//Log.d("wujiang", "setChannelListViewBackground 2222 normal");
convertView.setBackgroundResource(R.drawable.sel_epg_channel_list_normal);
}
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
mEpgMenuUIHandler.removeCallbacksAndMessages(null);
mSubHandler.removeCallbacksAndMessages(null);
mSubHandler.getLooper().quit();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
mHidden = hidden;
if (hidden) {
mEpgMenuUIHandler.removeCallbacksAndMessages(null);
ProgramsDataManager.getInstance().removeListener(queryFinishListener);
if (mCurrentGroupChannels != null) {
mCurrentGroupChannels.clear();
}
ProgramsDataManager.getInstance().clearData();
monitorProgramHelper.stop();
} else {
initData();
mGroupListAdapter.setData(mAllGroupList);
if (mGroupListAdapter.getCount() > 0) {
mSubHandler.removeMessages(SUB_MSG_LOAD_CHANNEL_DATA);
Message msg = mSubHandler.obtainMessage();
msg.what = SUB_MSG_LOAD_CHANNEL_DATA;
msg.obj = mGroupListAdapter.getItem(0);
mSubHandler.sendMessageDelayed(msg, 30);
}
mEpgMenuUIHandler.removeMessages(UI_MSG_REFRESH_TIME_LINE);
mEpgMenuUIHandler.sendEmptyMessageDelayed(UI_MSG_REFRESH_TIME_LINE, 60 * 1000);
}
}
private ProgramsDataManager.QueryFinishListener queryFinishListener = new ProgramsDataManager.QueryFinishListener() {
@Override
public void queryFinishCallback() {
Log.d(TAG, "queryFinishCallback call");
/**
* 这里必须重新定位,找到哪个program是被选中状态,不然从Genres回到ListView时,EPG会出现无选中的program。
*/
mEpgView.findBeSelectedProgram();
mEpgMenuUIHandler.sendEmptyMessage(UI_MSG_REDRAW_EGPVIEW);
}
};
public Handler mEpgMenuUIHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case UI_MSG_UPDATE_GROUP_CHANNEL_DATA:
Log.d("wujiang", "receive UI_MSG_UPDATE_GROUP_CHANNEL_DATA");
mChangedGroup = true;
setCurrentChannelId(mChannelDataManager.getLastWatchChannelId());
mChannelListAdapter.setChannelsData(mCurrentGroupChannels);
mEpgView.mDefaultFocusPosition = getEpgViewDefaultFocusPosition(mCurrentChannelId);
mEpgView.setChannelsData(getContext(), mCurrentGroupChannels, EpgMenuFragment.this, mEpgView.mDefaultFocusPosition);
mEpgView.scrollTo(mEpgView.getScrollX(), 0);
mEpgView.mSkipDraw = true;
mChannelListView.setSelection(mEpgView.mDefaultFocusPosition);
if (mChannelListView.isFocused()) {
mEpgView.recalculateAndRedraw(false, mEpgView.mDefaultFocusPosition, true);
} else {
mEpgView.recalculateAndRedraw(false, mEpgView.mDefaultFocusPosition, false);
}
mSubHandler.removeMessages(SUB_MSG_LOAD_CHANNELS_PROGRAM_DATA);
//去数据里查询当前页节目的EPG数据
Message obtainMessage = mSubHandler.obtainMessage();
obtainMessage.what = SUB_MSG_LOAD_CHANNELS_PROGRAM_DATA;
obtainMessage.arg1 = mEpgView.mDefaultFocusPosition;
obtainMessage.arg2 = 0;
mSubHandler.sendMessage(obtainMessage);
break;
case UI_MSG_GROUP_LIST_GET_FOCUS:
Log.d(TAG, "UI_MSG_GROUP_LIST_GET_FOCUS");
mEpgView.setDrawNeedSelect(false);
mEpgView.redraw();
mGroupListView.requestFocus();
mGroupListView.setSelection(mLastGroupPosition);
break;
case UI_MSG_REFRESH_TIME_LINE:
mEpgView.redraw();
sendEmptyMessageDelayed(UI_MSG_REFRESH_TIME_LINE, 60 * 1000);
break;
case UI_MSG_REDRAW_EGPVIEW:
Log.d(TAG, "UI_MSG_REDRAW_EGPVIEW");
mEpgView.redraw();
break;
case UI_MSG_SHOW_NEXT_DAY_EPG:
mEpgView.moveToNextDay();
break;
case UI_MSG_SHOW_PREV_DAY_EPG:
mEpgView.moveToPrevDay();
break;
default:
break;
}
}
};
EpgGroupListAdapter相关代码:
public class EpgGroupListAdapter extends ArrayAdapter<EpgChannelGroup> {
private static final String TAG = EpgGroupListAdapter.class.getSimpleName();
private LayoutInflater mInflater;
private long mCurrentGroupPosition;
private ArrayList<EpgChannelGroup> mGroupList;
private EpgChannelGroup mCurrentGroupInfo;
public EpgGroupListAdapter(Context context, ArrayList<EpgChannelGroup> groupList) {
super(context, 0);
mInflater = LayoutInflater.from(context);
mGroupList = new ArrayList<EpgChannelGroup>();
mGroupList.addAll(groupList);
}
public void setData(ArrayList<EpgChannelGroup> list) {
mGroupList.clear();
mGroupList.addAll(list);
notifyDataSetChanged();
}
@Override
public int getCount() {
return mGroupList.size();
}
@Nullable
@Override
public EpgChannelGroup getItem(int position) {
return mGroupList.get(position);
}
@Override
public int getPosition(@Nullable EpgChannelGroup item) {
return super.getPosition(item);
}
public void setCurrentGroupPosition(long position) {
mCurrentGroupPosition = position;
}
public void setCurrentGroupInfo(EpgChannelGroup epgChannelGroup) {
mCurrentGroupInfo = epgChannelGroup;
}
@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
TextView textView;
if (null == convertView) {
convertView = mInflater.inflate(R.layout.epg_group_list, parent, false);
textView = (TextView) convertView.findViewById(R.id.txt_title);
convertView.setTag(textView);
} else {
textView = (TextView) convertView.getTag();
}
textView.setText(mGroupList.get(position).getName());
if (position < mGroupList.size() && mCurrentGroupInfo.equals(mGroupList.get(position))) {
convertView.setBackgroundResource(R.drawable.sel_epg_group_list_selected);
} else {
convertView.setBackgroundResource(R.drawable.sel_epg_group_list_normal);
}
return convertView;
}
}
ChannelListAdapter相关代码:
public class ChannelListAdapter extends BaseAdapter {
private final String TAG = ChannelListAdapter.class.getSimpleName();
private List<Channel> channelList;
private LayoutInflater mInflater;
private long mCurrentChannelId;
public ChannelListAdapter(Context context, ArrayList<Channel> channelArrayList) {
super();
mInflater = LayoutInflater.from(context);
if (channelList == null) {
channelList = new ArrayList<>();
}
channelList.clear();
channelList.addAll(channelArrayList);
}
public void setCurrentChannelId(long id) {
mCurrentChannelId = id;
}
public void setChannelsData(ArrayList<Channel> channelArrayList) {
if (channelList == null) {
channelList = new ArrayList<>();
}
channelList.clear();
channelList.addAll(channelArrayList);
notifyDataSetChanged();
}
@Override
public int getCount() {
return channelList.size();
}
@Override
public Channel getItem(int i) {
if (getCount() > 0) {
return channelList.get(i);
}
return null;
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ChannelListViewHolder viewHolder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.epg_channel_list_item, parent, false);
viewHolder = new ChannelListViewHolder();
viewHolder.channelIndex = (TextView) convertView.findViewById(R.id.channel_index);
viewHolder.channelName = (TextView) convertView.findViewById(R.id.channel_name);
viewHolder.channelFav = (ImageView) convertView.findViewById(R.id.channel_fav);
convertView.setTag(viewHolder);
} else {
viewHolder = (ChannelListViewHolder) convertView.getTag();
}
if (position < channelList.size()) {
Channel channel = channelList.get(position);
if (CommonUtils.isOnLcnSwitch()) {
viewHolder.channelIndex.setText(channel.getDisplayNumber());
} else {
viewHolder.channelIndex.setText(String.valueOf(position + 1));
}
viewHolder.channelName.setText(channel.getDisplayName());
viewHolder.channelFav.setVisibility(View.GONE);
}
if (position < channelList.size() && mCurrentChannelId == channelList.get(position).getId()) {
convertView.setBackgroundResource(R.drawable.sel_epg_channel_list_selected);
} else {
convertView.setBackgroundResource(R.drawable.sel_epg_channel_list_normal);
}
return convertView;
}
public class ChannelListViewHolder {
TextView channelIndex;
TextView channelName;
ImageView channelFav;
}
}
ProgramsDataManager相关代码:
public class ProgramsDataManager {
public static String TAG = ProgramsDataManager.class.getSimpleName();
//public static String TAG = "wujiang";
private static ProgramsDataManager instance = null;
private HashMap<Long, ArrayList<Program>> mChannelsProgramMap;
private QueryChannelsProgramsManager mQueryChannelsProgramsManager;
private List<QueryFinishListener> mListenerList;
private boolean mIsQuerying = false;
public static ProgramsDataManager getInstance() {
if (null == instance) {
instance = new ProgramsDataManager();
}
return instance;
}
public ProgramsDataManager() {
mChannelsProgramMap = new HashMap<>();
mListenerList = new ArrayList<>();
mQueryChannelsProgramsManager = ((QueryChannelsProgramsManager) DataFactory.get().getManager(TvApplication.getAppContext(), DataFactory.QUERY_PROGRAM_MANAGER));
mQueryChannelsProgramsManager.addListener(new ProgramLoadedListener() {
@Override
public void onProgramLoadedCallback(Map<Long, ? extends ArrayList<Program>> channelsProgramMaps) {
Log.d(TAG, "onProgramLoadedCallback: channelsProgramMaps size = " + channelsProgramMaps.size());
if (channelsProgramMaps.size() <= 0) {
Log.d(TAG, "onProgramLoadedCallback: channelsProgramMaps size = 0");
mIsQuerying = false;
return;
}
HashMap<Long, ArrayList<Program>> programMaps = (HashMap<Long, ArrayList<Program>>) channelsProgramMaps;
Set<Map.Entry<Long, ArrayList<Program>>> setEntry = programMaps.entrySet();
Iterator<Map.Entry<Long, ArrayList<Program>>> iterator = setEntry.iterator();
while (iterator.hasNext()) {
Map.Entry<Long, ArrayList<Program>> entry = iterator.next();
Long channel_id = entry.getKey();
ArrayList<Program> programArrayList = entry.getValue();
//Log.d(TAG, "onProgramLoadedCallback: channel_id = " + channel_id);
//Log.d(TAG, "onProgramLoadedCallback: event size = " + programArrayList.size());
//for (int i = 0; i < programArrayList.size(); i++) {
// Program program = programArrayList.get(i);
// Log.d(TAG, "onProgramLoadedCallback: event name = " + program.getTitle());
//}
mChannelsProgramMap.remove(channel_id);
mChannelsProgramMap.put(channel_id, programArrayList);
}
for (QueryFinishListener listener : mListenerList) {
listener.queryFinishCallback();
}
mIsQuerying = false;
}
});
}
/**
* 查询一些节目的EPG信息
* @param startTime 起始时间
* @param endTime 结束时间
* @param channelsId channel集合
* @param needClearData 是否清除掉channel的EPG
*/
public void queryChannelsProgramFromDatabase(Long startTime, Long endTime, ArrayList<Long> channelsId, int needClearData) {
ArrayList<Long> needQueryChannels = new ArrayList<>();
mIsQuerying = true;
long queryEarlyMorningTime1 = EPGUtils.getDayStartTime(startTime);
long queryEarlyMorningTime2 = EPGUtils.getDayStartTime(endTime);
long queryEarlyMorningTime3 = queryEarlyMorningTime2 + EPGUtils.DAY_OF_SECONDS * EPGUtils.ONE_THOUSAND;
int queryDays;
if (((endTime / EPGUtils.ONE_THOUSAND - queryEarlyMorningTime1 / EPGUtils.ONE_THOUSAND) % EPGUtils.DAY_OF_SECONDS) == 0) {
queryDays = (int) ((endTime / EPGUtils.ONE_THOUSAND - queryEarlyMorningTime1 / EPGUtils.ONE_THOUSAND) / EPGUtils.DAY_OF_SECONDS);
} else {
queryDays = (int) ((queryEarlyMorningTime3 / EPGUtils.ONE_THOUSAND - queryEarlyMorningTime1 / EPGUtils.ONE_THOUSAND) / EPGUtils.DAY_OF_SECONDS);
}
//Log.d(TAG, "queryChannelsProgramFromDatabase: queryEarlyMorningTime1 = " + queryEarlyMorningTime1);
//Log.d(TAG, "queryChannelsProgramFromDatabase: queryEarlyMorningTime2 = " + queryEarlyMorningTime2);
//Log.d(TAG, "queryChannelsProgramFromDatabase: queryEarlyMorningTime3 = " + queryEarlyMorningTime3);
//Log.d(TAG, "queryChannelsProgramFromDatabase: queryDays = " + queryDays);
long lastDayEndTime = queryEarlyMorningTime1 + EPGUtils.DAY_OF_SECONDS * EPGUtils.ONE_THOUSAND * queryDays;
long lastDayStartTime = lastDayEndTime - EPGUtils.DAY_OF_SECONDS * EPGUtils.ONE_THOUSAND;
//Log.d(TAG, "queryChannelsProgramFromDatabase: lastDayEndTime = " + lastDayEndTime);
//Log.d(TAG, "queryChannelsProgramFromDatabase: lastDayStartTime = " + lastDayStartTime);
for (Long channel_id : channelsId) {
boolean haveEpgDataInCache = false;
if (needClearData == 1) {
mChannelsProgramMap.remove(channel_id);
} else {
ArrayList<Program> programArrayList = mChannelsProgramMap.get(channel_id);
if (programArrayList == null || programArrayList.size() <= 0) {
needQueryChannels.add(channel_id);
continue;
}
//Log.d(TAG, "queryChannelsProgramFromDatabase: programArrayList size = " + programArrayList.size());
for (int i = programArrayList.size() - 1; i >= 0; i--) {
Program program = programArrayList.get(i);
if (program.getStartTimeUtcMillis() > lastDayStartTime && program.getEndTimeUtcMillis() <= lastDayEndTime) {
Log.d(TAG, "queryChannelsProgramFromDatabase: There have epg data in cache, its channel id = " + channel_id);
haveEpgDataInCache = true;
break;
}
}
}
//Log.d(TAG, "queryChannelsProgramFromDatabase: haveEpgDataInCache = " + haveEpgDataInCache);
if (!haveEpgDataInCache) {
needQueryChannels.add(channel_id);
}
}
Log.d(TAG, "queryChannelsProgramFromDatabase: needQueryChannels size = " + needQueryChannels.size());
mQueryChannelsProgramsManager.loadChannelsProgramsFromDB(startTime, endTime, needQueryChannels);
}
/**
* 从Map中删除某个节目的EPG信息列表
* @param channel_id
*/
public void removeProgramDataByChannelId(long channel_id) {
mChannelsProgramMap.remove(channel_id);
}
/**
* 增加某个节目的EPG列表到Map中
* @param channel_id
* @param list
*/
public void addProgramDataByChannelId(long channel_id, ArrayList<Program> list) {
mChannelsProgramMap.remove(channel_id);
mChannelsProgramMap.put(channel_id, list);
}
/**
* 获取某个节目的EPG信息列表
* @param channel_id
* @return
*/
public ArrayList<Program> getChannelProgramsByChannelId(long channel_id) {
return mChannelsProgramMap.get(channel_id);
}
/**
* 获取某个节目的EPG信息列表中指定位置的Program
* @param channel_id
* @param position
* @return
*/
public Program getProgramOfChannelByPosition(long channel_id, int position) {
Program program = null;
try {
program = mChannelsProgramMap.get(channel_id).get(position);
} catch (Exception ex) {
Log.e(TAG, "getProgramOfChannelByPosition = " + channel_id + ", position = " + position);
}
return program;
}
/**
* 获取program在它所属的channel的program list中的位置
* @param channel_id
* @param program
* @return
*/
public int getPositionOfProgram(long channel_id, Program program) {
if (program == null) {
Log.e(TAG, "getPositionOfProgram: program == null");
return -1;
}
if (mChannelsProgramMap.get(channel_id) == null || mChannelsProgramMap.get(channel_id).size() < 1) {
Log.d(TAG, "getPositionOfProgram: channel_id: " + channel_id + ", its program list is empty.");
return -1;
}
//Log.d("wujiang", "getPositionOfProgram 3");
//Log.d("wujiang", "getPositionOfProgram: program start time = " + program.getStartTimeUtcMillis());
//Log.d("wujiang", "getPositionOfProgram: program end time = " + program.getEndTimeUtcMillis());
return mChannelsProgramMap.get(channel_id).indexOf(program);
}
/**
* 获取到指定节目program的数量
* @return
*/
public int getProgramCountOfChannel(long channel_id) {
if (mChannelsProgramMap.get(channel_id) == null) {
return 0;
}
return mChannelsProgramMap.get(channel_id).size();
}
/**
* 清楚掉所有program的选中状态
*/
public void clearSelection() {
if (mChannelsProgramMap.size() > 0) {
Set<Map.Entry<Long, ArrayList<Program>>> setEntry = mChannelsProgramMap.entrySet();
Iterator<Map.Entry<Long, ArrayList<Program>>> iterator = setEntry.iterator();
while (iterator.hasNext()) {
Map.Entry<Long, ArrayList<Program>> entry = iterator.next();
ArrayList<Program> programArrayList = entry.getValue();
for (Program program : programArrayList) {
program.setSelected(false);
}
}
}
}
/**
* 清除Map中的数据
*/
public void clearData() {
mChannelsProgramMap.clear();
}
public boolean isQuerying() {
return mIsQuerying;
}
public void addListener(QueryFinishListener listener) {
mListenerList.add(listener);
}
public void removeListener(QueryFinishListener listener) {
mListenerList.remove(listener);
}
public interface QueryFinishListener {
void queryFinishCallback();
}
}
QueryChannelsProgramsManager相关代码:
import static com.nes.datalibrary.manager.ProgramDataManager.PARAM_END_TIME;
import static com.nes.datalibrary.manager.ProgramDataManager.PARAM_START_TIME;
public class QueryChannelsProgramsManager {
private final String TAG = QueryChannelsProgramsManager.class.getSimpleName();
private Context mContext;
private ExecutorService DB_EXECUTOR = Executors.newSingleThreadExecutor(new NamedThreadFactory("query-db"));
//存放当前查询节目的epg信息
private HashMap<Long, ArrayList<Program>> mAllChannelProgramMap = new HashMap<>();
private ArrayList<ProgramLoadedListener> mListenerList;
private TvClock mTvClock;
private long execute_time = 0;
private final String SORT_BY_TIME = (TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS
+ ", "
+ TvContract.Programs.COLUMN_CHANNEL_ID
+ ", "
+ TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS);
public QueryChannelsProgramsManager(Context context) {
mContext = context;
mListenerList = new ArrayList<>();
mTvClock = new TvClock(context);
}
public void loadChannelsProgramsFromDB(Long startTime, Long endTime, ArrayList<Long> channelsId) {
QueryParams queryParams = new QueryParams(startTime, endTime, channelsId);
LoadProgramFromDBTask loadProgramFromDBTask = new LoadProgramFromDBTask(DB_EXECUTOR);
loadProgramFromDBTask.executeOnDbThread(queryParams);
}
public class LoadProgramFromDBTask extends AsyncDbTask<QueryParams, Void, Map<Long, ArrayList<Program>>> {
protected LoadProgramFromDBTask(Executor mExecutor) {
super(mExecutor);
}
@Override
protected Map<Long, ArrayList<Program>> doInBackground(QueryParams... params) {
execute_time = mTvClock.currentTimeMillis();
Long startTime = params[0].getStartTime();
Long endTime = params[0].getEndTime();
ArrayList<Long> channelIdList = params[0].getQueryChannels();
Log.d("wujiang", "doInBackground: startTime = " + startTime);
Log.d("wujiang", "doInBackground: endTime = " + endTime);
mAllChannelProgramMap.clear();
for (Long channelId : channelIdList) {
ArrayList<Program> programArrayList = queryOneChannelPrograms(channelId, startTime, endTime);
mAllChannelProgramMap.put(channelId.longValue(), programArrayList);
}
Log.d(TAG, "doInBackground: spend time = " + (mTvClock.currentTimeMillis() - execute_time));
return mAllChannelProgramMap;
}
@Override
protected void onPostExecute(Map<Long, ArrayList<Program>> longArrayListMap) {
for (ProgramLoadedListener listener : mListenerList) {
listener.onProgramLoadedCallback(longArrayListMap);
mAllChannelProgramMap.clear();
}
}
}
private Map<Long, ArrayList<Program>> queryMultiChannelPrograms(ArrayList<Long> channelIdList, Long start_time, Long end_time) {
HashMap<Long, ArrayList<Program>> programMaps = new HashMap<>();
programMaps.clear();
//存放了所有节目的EPG信息
ArrayList<Program> allChannelProgramsList = new ArrayList<>();
allChannelProgramsList.clear();
Cursor cursor = null;
Uri uri = TvContract.Programs.CONTENT_URI.buildUpon()
.appendQueryParameter(PARAM_START_TIME, start_time.toString())
.appendQueryParameter(PARAM_END_TIME, end_time.toString())
.build();
try {
cursor = mContext.getContentResolver().query(uri, Program.PROJECTION, null, null, SORT_BY_TIME);
if (cursor == null) {
return null;
}
int programsCount = cursor.getCount();
if (programsCount > 0) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
allChannelProgramsList.add(Program.fromCursor(cursor));
cursor.moveToNext();
}
} else {
return null;
}
} finally {
try {
if (null != cursor && !cursor.isClosed()) {
cursor.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
//Log.d("wujiang", "queryMultiChannelPrograms: allChannelProgramsList size = " + allChannelProgramsList.size());
Program lastReadProgram = null;
for (Program program : allChannelProgramsList) {
if (Program.isDuplicate(program, lastReadProgram)) {
Log.d(TAG, "queryMultiChannelPrograms: duplicate program ...");
continue;
} else {
lastReadProgram = program;
}
ArrayList<Program> oneChannelProgramArrayList = programMaps.get(program.getChannelId());
if (oneChannelProgramArrayList == null) {
oneChannelProgramArrayList = new ArrayList<>();
programMaps.put(program.getChannelId(), oneChannelProgramArrayList);
}
oneChannelProgramArrayList.add(program);
}
return programMaps;
}
/**
* 查询某个节目指定时间内的EPG信息
*
* @param channel_id
* @param start_time
* @param end_time
* @return event列表
*/
private ArrayList<Program> queryOneChannelPrograms(Long channel_id, Long start_time, Long end_time) {
Log.d(TAG, "queryOneChannelPrograms: start query channel_id = " + channel_id + " epg from database");
Uri uri = TvContract.Programs.CONTENT_URI.buildUpon()
.appendQueryParameter(PARAM_START_TIME, start_time.toString())
.appendQueryParameter(PARAM_END_TIME, end_time.toString())
.build();
//存放该节目EPG数据,去掉了重复的数据
ArrayList<Program> programsList = new ArrayList<>();
programsList.clear();
Program lastReadProgram = null;
Cursor cursor = null;
try {
cursor = mContext.getContentResolver().query(uri, Program.PROJECTION, TvContract.Programs.COLUMN_CHANNEL_ID + "=?", new String[]{channel_id.toString()}, TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS);
if (cursor == null) {
return programsList;
}
int programsCount = cursor.getCount();
if (programsCount > 0) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Program program = Program.fromCursor(cursor);
if (Program.isDuplicate(program, lastReadProgram)) {
Log.d(TAG, "queryOneChannelPrograms: duplicate program ...");
cursor.moveToNext();
continue;
} else {
if (lastReadProgram != null && (lastReadProgram.getEndTimeUtcMillis() != program.getStartTimeUtcMillis())) {
/**
* 两个EPG时间不是连续的,需要补上空白EPG,不然UI会出现异常
*/
long fill_time = program.getStartTimeUtcMillis() - lastReadProgram.getEndTimeUtcMillis();
if (fill_time > 0) {
Program fill_program = new Program();
fill_program.setStartTimeUtcMillis(lastReadProgram.getEndTimeUtcMillis());
fill_program.setEndTimeUtcMillis(program.getStartTimeUtcMillis());
fill_program.setId(lastReadProgram.getId());
fill_program.setTitle("No Information");
programsList.add(fill_program);
}
}
programsList.add(program);
lastReadProgram = program;
}
cursor.moveToNext();
}
}
} finally {
try {
if (null != cursor && !cursor.isClosed()) {
cursor.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
if (programsList.size() > 0) {
/** 如果第一个event的开始时间就比当前时间大,则在它前面补上一个event **/
Program firstProgram = programsList.get(0);
long current_time = mTvClock.currentTimeMillis();
if (firstProgram.getStartTimeUtcMillis() > current_time) {
Program program = new Program();
program.setStartTimeUtcMillis(current_time - 30 * 60 * 1000);
program.setEndTimeUtcMillis(firstProgram.getStartTimeUtcMillis());
program.setId(firstProgram.getId());
program.setTitle("No Information");
programsList.add(0, program);
}
/** 如果最后一个event的结束时间没有到达end_time, 则在它后面补上一个event **/
if (lastReadProgram.getEndTimeUtcMillis() < end_time.longValue()) {
Program program = new Program();
program.setStartTimeUtcMillis(lastReadProgram.getEndTimeUtcMillis());
program.setEndTimeUtcMillis(end_time);
program.setId(lastReadProgram.getId());
program.setTitle("No Information");
programsList.add(program);
}
}
return programsList;
}
public void addListener(ProgramLoadedListener listener) {
mListenerList.add(listener);
}
public void removeListener(ProgramLoadedListener listener) {
mListenerList.remove(listener);
}
private class QueryParams {
private Long startTime;
private Long endTime;
private ArrayList<Long> queryChannels;
public QueryParams(Long startTime, Long endTime, ArrayList<Long> queryChannels) {
this.startTime = startTime;
this.endTime = endTime;
this.queryChannels = queryChannels;
}
public Long getStartTime() {
return startTime;
}
public Long getEndTime() {
return endTime;
}
public ArrayList<Long> getQueryChannels() {
return queryChannels;
}
}
}
这里,AsyncDbTask的代码我就不贴出来了。
EpgView是一个自定义的类,EPG的数据就是靠它显示出来的,我把全部的代码都贴出来。
public class EpgView extends ViewGroup {
private final String TAG = EpgView.class.getSimpleName();
public static int DAYS_BACK_MILLIS;
public static final int DAYS_FORWARD_MILLIS = 5 * 24 * 60 * 60 * 1000;
private static int HOURS_IN_VIEWPORT_MILLIS = 2 * 60 * 60 * 1000;
private static final int TIME_LABEL_SPACING_MILLIS = 30 * 60 * 1000;
public static final int MaxItemCountInOnePage = 10;
public static final int UP_DIRECTION = 0;
public static final int DOWN_DIRECTION = 1;
private Context contextObj;
private EpgMenuFragment mEpgMenuFragment = null;
private int cuDays = 0;
private boolean isMoveRight;
private boolean isMoveLeft;
private boolean isAutoFocusChange;
private boolean mNeedDrawSelectedProgram = false; //选中的event是否要变成焦点颜色
private boolean drawCurrentTimeLine = false;
private int adjustment_height = 0;
private boolean adjust_only_one_time = false;
private int mChannelLayoutHeight; //一行epg数据所占的屏幕高度
private int mChannelLayoutPadding; //epg数据行每个event的间距
private int mChannelLayoutWidth; //在原始代码中,这个值是channel listview的宽度,这里我们定义成0
private final int mChannelLayoutMargin; //epg数据行的间距
private int mTwoListViewWidth;
private int mChannelListViewWidth;
private int mTimeBarTextSize;
private final Rect mClipRect;
private final Rect mDrawingRect;
private final Rect mMeasuringRect;
private final Paint mPaint;
private final Scroller mScroller;
private final int mEPGBackground; //EPG View的背景颜色
private final int mEventLayoutBackground; //event无焦点时的颜色
private final int mEventLayoutTextColor; //event文字颜色
private final int mEventLayoutSelected; //event选中时的颜色
private final int mEventLayoutTextSize; //event title字体大小
private final int mTimeBarHeight; //time bar的高度
private final int mTimerBarBackground; //time bar的背景颜色
private final int mTimeBarLineWidth; //时刻线的宽度
private final int mTimeBarLineColor; //时刻线的颜色
private final int mEventLayoutBackgroundCurrent;
private final int mHelpBarHeight;
//event显示区域(2个小时)里,每一个像素占的毫秒数
private long mMillisPerPixel;
//时刻表上最左边的那个时间,就是EPG主界面刚显示时当前的时间,单位毫秒
private long mTimeOffset;
//当前EPG信息最左侧、最右侧的时间,按左右键他们的值是会变得,单位毫秒
private long mTimeLowerBoundary;
private long mTimeUpperBoundary;
private int mMaxHorizontalScroll;
private int mMaxVerticalScroll;
private List<Channel> serviceList;
public Program currentProgram = null;
private Program referenceEvent;
private int referenceEventPosition;
public int mDefaultFocusPosition = 0;
public int currentChannelPosition = -1;
private Bitmap mRecordBitmap;
private Bitmap mReminderBitmap;
private Bitmap mRecordingBitmap;
private TvClock mTvClock;
public boolean mSkipDraw = false;
public EpgView(Context context) {
this(context, null);
}
public EpgView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public EpgView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//Log.d("wujiang", "EpgView: call this function");
DAYS_BACK_MILLIS = 0;
contextObj = context;
if (cuDays > 0) {
DAYS_BACK_MILLIS = cuDays * 24 * 60 * 60 * 1000;
} else {
DAYS_BACK_MILLIS = 0;
}
mTvClock = new TvClock(contextObj);
setWillNotDraw(false);
resetBoundaries();
mDrawingRect = new Rect();
mClipRect = new Rect();
mMeasuringRect = new Rect();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setTypeface(Typeface.createFromAsset(context.getAssets(), "Roboto-Light.ttf"));
mScroller = new Scroller(context);
mScroller.setFriction(ViewConfiguration.getScrollFriction());
mEPGBackground = getResources().getColor(R.color.epg_background);
mChannelLayoutMargin = getResources().getDimensionPixelSize(R.dimen.epg_line_space);
mChannelLayoutPadding = getResources().getDimensionPixelSize(R.dimen.epg_event_space);
mChannelLayoutHeight = getResources().getDimensionPixelSize(R.dimen.px_85_0);
mChannelLayoutWidth = getResources().getDimensionPixelSize(R.dimen.zero);
mTwoListViewWidth = getResources().getDimensionPixelSize(R.dimen.epg_group_listview_width)
+ getResources().getDimensionPixelSize(R.dimen.epg_channel_listview_width)
+ getResources().getDimensionPixelSize(R.dimen.listviews_space);
mChannelListViewWidth = getResources().getDimensionPixelSize(R.dimen.epg_channel_listview_width);
mEventLayoutBackground = getResources().getColor(R.color.epg_channel_list_item_normal);
mEventLayoutTextColor = getResources().getColor(R.color.white);
mEventLayoutSelected = getResources().getColor(R.color.color_blue);
mEventLayoutTextSize = getResources().getDimensionPixelSize(R.dimen.epg_event_text_size);
mTimeBarHeight = getResources().getDimensionPixelSize(R.dimen.px_104_0);
mTimeBarLineWidth = getResources().getDimensionPixelSize(R.dimen.epg_time_line_width);
mTimeBarLineColor = getResources().getColor(R.color.epg_time_line);
mTimerBarBackground = getResources().getColor(R.color.epg_help_bar);
mEventLayoutBackgroundCurrent = getResources().getColor(R.color.epg_background_black);
mTimeBarTextSize = getResources().getDimensionPixelSize(R.dimen.epg_time_bar_text_size);
mHelpBarHeight = getResources().getDimensionPixelSize(R.dimen.epg_help_bar_height);
initData();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean b, int left, int top, int right, int bottom) {
}
private void initData() {
mRecordBitmap = ((BitmapDrawable) contextObj.getDrawable(R.mipmap.ic_schedule_rec)).getBitmap();
mReminderBitmap = ((BitmapDrawable) contextObj.getDrawable(R.mipmap.ic_schedule_play)).getBitmap();
mRecordingBitmap = ((BitmapDrawable) contextObj.getDrawable(R.mipmap.ic_programm_recording)).getBitmap();
}
private boolean isEventVisible(final long start, final long end) {
return (start >= mTimeLowerBoundary && start <= mTimeUpperBoundary)
|| (end > mTimeLowerBoundary && end <= mTimeUpperBoundary)
|| (start <= mTimeLowerBoundary && end >= mTimeUpperBoundary);
}
private void setEventDrawingRectangle(final int channelPosition, final long start, final long end, final Rect drawingRect, String eventName) {
if (start < mTimeOffset) {
drawingRect.left = 0;//getXFrom(mTimeOffset);
} else {
drawingRect.left = getXFrom(start);
}
drawingRect.top = getTopFrom(channelPosition);
drawingRect.right = getXFrom(end) - mChannelLayoutMargin;
drawingRect.bottom = drawingRect.top + mChannelLayoutHeight;
}
public int getFirstVisibleChannelPosition() {
final int y = getScrollY();
int position = (y - mChannelLayoutMargin - mTimeBarHeight) / (mChannelLayoutHeight + mChannelLayoutMargin);
if (position < 0) {
position = 0;
}
return position;
}
public int getLastVisibleChannelPosition() {
final int y = getScrollY();
final int totalChannelCount = serviceList.size();
final int screenHeight = getHeight();
int position = (y + screenHeight + mTimeBarHeight - mChannelLayoutMargin - mHelpBarHeight) / (mChannelLayoutHeight + mChannelLayoutMargin);
if (position > totalChannelCount - 1) {
position = totalChannelCount - 1;
}
// Add one extra row if we don't fill screen with current..
return (y + screenHeight) > (position * mChannelLayoutHeight) && position < totalChannelCount - 1 ? position + 1 : position;
}
private int getLastBlankLineChannelPosition() {
final int y = getScrollY();
final int totalChannelCount = MaxItemCountInOnePage;
final int screenHeight = getHeight();
int position = (y + screenHeight + mTimeBarHeight - mChannelLayoutMargin - mHelpBarHeight) / (mChannelLayoutHeight + mChannelLayoutMargin);
if (position > totalChannelCount - 1) {
position = totalChannelCount - 1;
}
// Add one extra row if we don't fill screen with current..
return (y + screenHeight) > (position * mChannelLayoutHeight) && position < totalChannelCount - 1 ? position + 1 : position;
}
private void drawBlankEvents(Canvas canvas, Rect drawingRect) {
final int firstPos = getFirstVisibleChannelPosition();
final int lastPos = getLastBlankLineChannelPosition();
for (int pos = firstPos; pos <= lastPos; pos++) {
mClipRect.left = getScrollX() + mChannelLayoutWidth + mChannelLayoutMargin;
mClipRect.top = getTopFrom(pos);
mClipRect.right = getScrollX() + getWidth();
mClipRect.bottom = mClipRect.top + mChannelLayoutHeight;
canvas.save();
canvas.clipRect(mClipRect);
mPaint.setColor(mEventLayoutBackground);
canvas.drawRect(mClipRect, mPaint);
canvas.restore();
}
}
private void drawTimeBar(Canvas canvas, Rect drawingRect) {
//drawingRect.left = getScrollX() + mChannelLayoutWidth + mChannelLayoutMargin;
//DLog.d("wujiang", "drawTimeBar: call this function");
drawingRect.left = getScrollX() + mChannelLayoutWidth;
drawingRect.top = getScrollY();
drawingRect.right = drawingRect.left + getWidth();
drawingRect.bottom = drawingRect.top + mTimeBarHeight;
//DLog.d("wujiang", "drawTimeBar: getScrollX = " + getScrollX());
//DLog.d("wujiang", "drawTimeBar: drawingRect.left = " + drawingRect.left);
//mClipRect.left = getScrollX() + mChannelLayoutWidth + mChannelLayoutMargin;
mClipRect.left = getScrollX() + mChannelLayoutWidth;
mClipRect.top = getScrollY();
mClipRect.right = drawingRect.left + getWidth();
mClipRect.bottom = mClipRect.top + mTimeBarHeight;
//DLog.d("wujiang", "drawTimeBar: mClipRect.left = " + mClipRect.left);
canvas.save();
canvas.clipRect(mClipRect);
// Background
mPaint.setColor(mTimerBarBackground);
canvas.drawRect(drawingRect, mPaint);
// Time stamps
mPaint.setColor(mEventLayoutTextColor);
mPaint.setTextSize(mTimeBarTextSize);
/*for (int i = 0; i < HOURS_IN_VIEWPORT_MILLIS / TIME_LABEL_SPACING_MILLIS; i++) {
// Get time and round to nearest half hour
final long time = TIME_LABEL_SPACING_MILLIS *
(((mTimeLowerBoundary + (TIME_LABEL_SPACING_MILLIS * i)) +
(TIME_LABEL_SPACING_MILLIS / 2)) / TIME_LABEL_SPACING_MILLIS);
mPaint.setColor(mEventLayoutTextColor);
canvas.drawText(EPGUtils.get24hTimer(time), getXFrom(time),
drawingRect.top + (((drawingRect.bottom - drawingRect.top) / 2) + (mTimeBarTextSize / 2)), mPaint);
}*/
int i = 0;
long time = TIME_LABEL_SPACING_MILLIS * (((mTimeLowerBoundary + (TIME_LABEL_SPACING_MILLIS * i)) + (TIME_LABEL_SPACING_MILLIS / 2)) / TIME_LABEL_SPACING_MILLIS);
while(time <= mTimeUpperBoundary) {
mPaint.setColor(mEventLayoutTextColor);
canvas.drawText(EPGUtils.get24hTimer(time), getXFrom(time), drawingRect.top + (((drawingRect.bottom - drawingRect.top) / 2) + (mTimeBarTextSize / 2)), mPaint);
i++;
time = TIME_LABEL_SPACING_MILLIS * (((mTimeLowerBoundary + (TIME_LABEL_SPACING_MILLIS * i)) + (TIME_LABEL_SPACING_MILLIS / 2)) / TIME_LABEL_SPACING_MILLIS);
}
canvas.restore();
//drawTimebarDayIndicator(canvas, drawingRect);
drawTime();
drawTimebarBottomStroke(canvas, drawingRect);
}
private void drawTime() {
if (mEpgMenuFragment != null) {
mEpgMenuFragment.setEpgDateTextViewString(EPGUtils.getWeekdayName(mTimeLowerBoundary));
}
}
private void drawTimebarBottomStroke(Canvas canvas, Rect drawingRect) {
drawingRect.left = getScrollX();
drawingRect.top = getScrollY() + mTimeBarHeight;
drawingRect.right = drawingRect.left + getWidth();
drawingRect.bottom = drawingRect.top + mChannelLayoutMargin;
// Bottom stroke
mPaint.setColor(mEPGBackground);
canvas.drawRect(drawingRect, mPaint);
}
private void drawTimeLine(Canvas canvas, Rect drawingRect) {
long now = EPGUtils.getCurrentTimeInMillis();
//DLog.d("wujiang", "drawTimeLine: call this function");
if (shouldDrawTimeLine(now)) {
drawingRect.left = getXFrom(now);
drawingRect.top = getScrollY() + mTimeBarHeight;
drawingRect.right = drawingRect.left + mTimeBarLineWidth;
drawingRect.bottom = drawingRect.top + getHeight() - mTimeBarHeight;
mPaint.setColor(mTimeBarLineColor);
canvas.drawRect(drawingRect, mPaint);
//画倒三角形
Path path2 = new Path();
path2.moveTo(drawingRect.left + 0.5f, drawingRect.top); //移动画笔
path2.lineTo(drawingRect.left - 6, drawingRect.top - 8); //画线
path2.lineTo(drawingRect.left + 7, drawingRect.top - 8); //画线
path2.close();
canvas.drawPath(path2, mPaint);
}
}
private boolean shouldDrawTimeLine(long now) {
return now >= mTimeLowerBoundary && now < mTimeUpperBoundary;
}
@Override
protected void onDraw(Canvas canvas) {
if (serviceList != null && serviceList.size() > 0) {
//Log.d("wujiang", "onDraw: 111111111111");
mTimeLowerBoundary = getTimeFrom(getScrollX());
mTimeUpperBoundary = getTimeFrom(getScrollX() + getWidth());
//Log.d("wujiang", "onDraw: getScrollX = " + getScrollX());
//Log.d("wujiang", "onDraw: getScrollY = " + getScrollY());
//Log.d("wujiang", "onDraw: getWidth = " + getWidth());
//Log.d("wujiang", "onDraw: mTimeLowerBoundary = " + mTimeLowerBoundary);
//Log.d("wujiang", "onDraw: mTimeUpperBoundary = " + mTimeUpperBoundary);
Rect drawingRect = mDrawingRect;
drawingRect.left = getScrollX();
drawingRect.top = getScrollY();
drawingRect.right = drawingRect.left + getWidth();
drawingRect.bottom = drawingRect.top + getHeight();
//drawChannelListItems(canvas, drawingRect);
drawEvents(canvas, drawingRect);
drawTimeBar(canvas, drawingRect);
drawTimeLine(canvas, drawingRect);
//drawResetButton(canvas, drawingRect);
// If scroller is scrolling/animating do scroll. This applies when doing a fling.
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
}
} else {
//Log.d("wujiang", "onDraw: 22222222222");
mTimeLowerBoundary = getTimeFrom(getScrollX());
mTimeUpperBoundary = getTimeFrom(getScrollX() + getWidth());
Rect drawingRect = mDrawingRect;
drawingRect.left = getScrollX();
drawingRect.top = getScrollY();
drawingRect.right = drawingRect.left + getWidth();
drawingRect.bottom = drawingRect.top + getHeight();
drawBlankEvents(canvas, drawingRect);
drawTimeBar(canvas, drawingRect);
}
}
private void drawEvents(Canvas canvas, Rect drawingRect) {
final int firstPos = getFirstVisibleChannelPosition();
final int lastPos = getLastVisibleChannelPosition();
//解决切换组的时候,有多余的刷新EPG内容的操作,而且都是从firstPos = 0开始的。
if (firstPos == 0 && mSkipDraw) {
Log.d(TAG, "drawEvents: skip this draw");
mSkipDraw = false;
return;
}
//Log.d("wujiang", "drawEvents: firstPos = " + firstPos);
//Log.d("wujiang", "drawEvents: lastPos = " + lastPos);
for (int pos = firstPos; pos <= lastPos; pos++) {
// Set clip rectangle
mClipRect.left = getScrollX() + mChannelLayoutWidth + mChannelLayoutMargin;
mClipRect.top = getTopFrom(pos);
mClipRect.right = getScrollX() + getWidth();
mClipRect.bottom = mClipRect.top + mChannelLayoutHeight;
canvas.save();
canvas.clipRect(mClipRect);
// Draw each event
boolean foundFirst = false;
Channel channel = serviceList.get(pos);
//Log.d("wujiang", "drawEvents: channel name = " + channel.getDisplayName());
List<Program> epgEvents = ProgramsDataManager.getInstance().getChannelProgramsByChannelId(channel.getId());
//Log.d("wujiang", "drawEvents: epgEvents size = " + epgEvents.size());
//记录第一个被画的event
Program firstDrewEvent = null;
//记录最后一个被画的event
Program lastDrewEvent = null;
if (epgEvents != null && epgEvents.size() > 0) {
//Log.d("wujiang", "drawEvents: epgEvents.size() = " + epgEvents.size());
for (Program event : epgEvents) {
if (isEventVisible(event.getStartTimeUtcMillis(), event.getEndTimeUtcMillis())) {
//Log.d("wujiang", "drawEvents: 111111");
lastDrewEvent = event;
drawEvent(canvas, pos, event, drawingRect);
foundFirst = true;
if (firstDrewEvent == null) {
firstDrewEvent = event;
}
} else if (foundFirst) {
/** 找到最后一个可见的event后就不需要再往后面去找了,赞! **/
break;
}
}
if (foundFirst == false) {
//Log.d("wujiang", "drawEvents: 222222222");
drawNoInforEvent(canvas, pos, drawingRect);
} else if (foundFirst && (firstDrewEvent != null && firstDrewEvent.getStartTimeUtcMillis() > mTimeLowerBoundary)) {
//如果第一个被画的前面有空白区,则画一个event进行补全。
Log.d("wujiang", "drawEvents: need to draw blank area ahead firstDrewEvent");
drawNoInforEventAheadFirstEvent(canvas, pos, drawingRect, firstDrewEvent);
}
if (lastDrewEvent != null && lastDrewEvent.getEndTimeUtcMillis() < mTimeUpperBoundary) {
//如果最后一个被画的event还没有填满整行,则画一个event进行补全
Log.d("wujiang", "drawEvents: need to draw blank area following lastDrewEvent");
drawNoInforEventFollowingLastEvent(canvas, pos, drawingRect, lastDrewEvent);
}
} else {
//Log.d("wujiang", "drawEvents: 3333333333333");
drawNoInforEvent(canvas, pos, drawingRect);
}
canvas.restore();
}
//不够MaxItemCountInOnePage行,为了美化UI,补齐剩下的行。 这套UI需要这样
/*if ((lastPos - firstPos) < MaxItemCountInOnePage) {
for (int pos = lastPos + 1; pos < MaxItemCountInOnePage; pos++) {
mClipRect.left = getScrollX() + mChannelLayoutWidth + mChannelLayoutMargin;
mClipRect.top = getTopFrom(pos);
mClipRect.right = getScrollX() + getWidth();
mClipRect.bottom = mClipRect.top + mChannelLayoutHeight;
canvas.save();
canvas.clipRect(mClipRect);
mPaint.setColor(mEventLayoutBackground);
canvas.drawRect(mClipRect, mPaint);
canvas.restore();
}
}*/
}
/**
* @param canvas
* @param channelPosition channel的位置
* @param event 要画的event
* @param drawingRect 要画的event的位置信息,是通过event的开始、结束时间计算出来的
*/
private void drawEvent(final Canvas canvas, final int channelPosition, final Program event, final Rect drawingRect) {
//Log.d("wujiang", "drawEvent: call this function, mNeedDrawSelectedProgram = " + mNeedDrawSelectedProgram);
setEventDrawingRectangle(channelPosition, event.getStartTimeUtcMillis(), event.getEndTimeUtcMillis(), drawingRect, event.getTitle());
// Background
mPaint.setColor(isCurrent(event) && drawCurrentTimeLine ? mEventLayoutBackgroundCurrent : mEventLayoutBackground);
if (event.isSelected() && mNeedDrawSelectedProgram) {
//Log.d("wujiang", "drawEvent: set focus color");
mPaint.setColor(mEventLayoutSelected);
//Log.d("wujiang", "drawEvent: event.title() = " + event.getTitle());
Log.d("wujiang", "drawEvent: focus event start_time = " + event.getStartTimeUtcMillis());
Log.d("wujiang", "drawEvent: focus event end_time = " + event.getEndTimeUtcMillis());
}
canvas.drawRect(drawingRect, mPaint);
long program_channel_id = event.getChannelId();
long program_start_time = event.getStartTimeUtcMillis();
if (mEpgMenuFragment != null && mEpgMenuFragment.mScheduleData.size() <= 0) {
event.setTimerStatus(0);
} else {
for (int i = 0; i < mEpgMenuFragment.mScheduleData.size(); i++) {
ScheduleRecording scheduleRecording = mEpgMenuFragment.mScheduleData.get(i);
long start_time = scheduleRecording.getStartTime();
long channel_id = scheduleRecording.getChannelId();
if (channel_id == program_channel_id && start_time == program_start_time) {
//Log.d("wujiang", "drawEvent: 1111111111111111");
if (scheduleRecording.getType() == SchedulerContract.Schedules.TYPE_WATCH) {
event.setTimerStatus(1);
if (drawingRect.right >= getXFrom(mTimeUpperBoundary)) {
canvas.drawBitmap(mReminderBitmap, getXFrom(mTimeUpperBoundary) - mReminderBitmap.getWidth() - 4, drawingRect.top + (drawingRect.bottom - drawingRect.top) / 2 - mReminderBitmap.getHeight() / 2, null);
} else {
canvas.drawBitmap(mReminderBitmap, drawingRect.right - mReminderBitmap.getWidth() - 4, drawingRect.top + (drawingRect.bottom - drawingRect.top) / 2 - mReminderBitmap.getHeight() / 2, null);
}
break;
} else if (scheduleRecording.getType() == SchedulerContract.Schedules.TYPE_RECORD) {
//Log.d("wujiang", "drawEvent: 22222222222");
event.setTimerStatus(2);
if (drawingRect.right >= getXFrom(mTimeUpperBoundary)) {
canvas.drawBitmap(mRecordBitmap, getXFrom(mTimeUpperBoundary) - mRecordBitmap.getWidth() - 4, drawingRect.top + (drawingRect.bottom - drawingRect.top) / 2 - mRecordBitmap.getHeight() / 2, null);
} else {
canvas.drawBitmap(mRecordBitmap, drawingRect.right - mRecordBitmap.getWidth() - 4, drawingRect.top + (drawingRect.bottom - drawingRect.top) / 2 - mRecordBitmap.getHeight() / 2, null);
}
break;
}
} else {
//Log.d("wujiang", "drawEvent: 333333333333333");
event.setTimerStatus(0);
}
}
}
// Add left and right inner padding
drawingRect.left += mChannelLayoutPadding;
drawingRect.right -= mChannelLayoutPadding;
// Text
mPaint.setColor(mEventLayoutTextColor);
mPaint.setTextSize(mEventLayoutTextSize);
//Log.e("TAG", " @ @ @ @ " + event.getGenres());
// Set Genre
Rect genreBackgroundRect = null;
if (genreBackgroundRect == null) {
genreBackgroundRect = new Rect();
genreBackgroundRect.set(drawingRect.left, drawingRect.top + mTimeBarHeight + 10, (int) (drawingRect.left), drawingRect.bottom - 5);
}
// Move drawing.top so text will be centered (text is drawn bottom>up)
mPaint.getTextBounds(event.getTitle(), 0, event.getTitle().length(), mMeasuringRect);
drawingRect.top += (((drawingRect.bottom - drawingRect.top) / 2) + (mMeasuringRect.height() / 2));
// Text
/*if (event.isSelected() && mNeedDrawSelectedProgram) {
mPaint.setColor(Color.YELLOW);
mPaint.setTextSize(mEventLayoutTextSize);
} else {
mPaint.setColor(mEventLayoutTextColor);
mPaint.setTextSize(mEventLayoutTextSize);
}*/
//NEW CHANGES USING SHOW_TYPE of EVENT
String title = null;
title = event.getTitle();
title = title.substring(0, mPaint.breakText(title, true, drawingRect.right - drawingRect.left, null));
TextPaint textPaint = new TextPaint(Paint.LINEAR_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(mPaint.getColor());
textPaint.setTextSize(mPaint.getTextSize());
textPaint.setStyle(mPaint.getStyle());
textPaint.setTypeface(mPaint.getTypeface());
CharSequence txt = null;
if (event.getStartTimeUtcMillis() < mTimeLowerBoundary) {
txt = TextUtils.ellipsize(title, textPaint, (drawingRect.right - getXFrom(mTimeLowerBoundary)) - mReminderBitmap.getWidth(), TextUtils.TruncateAt.END);
canvas.drawText(txt, 0, txt.length(), getXFrom(mTimeLowerBoundary) + 6, drawingRect.top, textPaint);
} else {
if (drawingRect.right >= getXFrom(mTimeUpperBoundary)) {
txt = TextUtils.ellipsize(title, textPaint, (getXFrom(mTimeUpperBoundary) - drawingRect.left) - mReminderBitmap.getWidth(), TextUtils.TruncateAt.END);
} else {
txt = TextUtils.ellipsize(title, textPaint, (drawingRect.right - drawingRect.left) - mReminderBitmap.getWidth(), TextUtils.TruncateAt.END);
}
//txt = TextUtils.ellipsize(title, textPaint, (drawingRect.right - drawingRect.left) - 10, TextUtils.TruncateAt.END);
canvas.drawText(txt, 0, txt.length(), drawingRect.left + 6, drawingRect.top, textPaint);
}
}
private void drawNoInforEvent(final Canvas canvas, final int channelPosition, final Rect drawingRect) {
//DLog.d("wujiang", "drawNoInforEvent: call this function");
String title = "No Information";
setEventDrawingRectangle(channelPosition, mTimeLowerBoundary - 1, mTimeUpperBoundary, drawingRect, title);
// Background
mPaint.setColor(drawCurrentTimeLine ? mEventLayoutBackgroundCurrent : mEventLayoutBackground);
if (currentChannelPosition == channelPosition && mNeedDrawSelectedProgram) {
mPaint.setColor(mEventLayoutSelected);
}
canvas.drawRect(drawingRect, mPaint);
// Add left and right inner padding
drawingRect.left += mChannelLayoutPadding;
drawingRect.right -= mChannelLayoutPadding;
// Text
mPaint.setColor(mEventLayoutTextColor);
mPaint.setTextSize(mEventLayoutTextSize);
// Move drawing.top so text will be centered (text is drawn bottom>up)
mPaint.getTextBounds(title, 0, title.length(), mMeasuringRect);
drawingRect.top += (((drawingRect.bottom - drawingRect.top) / 2) + (mMeasuringRect.height() / 2));
// Text
/*if (currentChannelPosition == channelPosition && mNeedDrawSelectedProgram) {
mPaint.setColor(Color.YELLOW);
mPaint.setTextSize(mEventLayoutTextSize);
} else {
mPaint.setColor(mEventLayoutTextColor);
mPaint.setTextSize(mEventLayoutTextSize);
}*/
//NEW CHANGES USING SHOW_TYPE of EVENT
title = title.substring(0, mPaint.breakText(title, true, drawingRect.right - drawingRect.left, null));
TextPaint textPaint = new TextPaint(Paint.LINEAR_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(mPaint.getColor());
textPaint.setTextSize(mPaint.getTextSize());
textPaint.setStyle(mPaint.getStyle());
textPaint.setTypeface(mPaint.getTypeface());
CharSequence txt = null;
txt = TextUtils.ellipsize(title, textPaint, (drawingRect.right - drawingRect.left) - 10, TextUtils.TruncateAt.END);
canvas.drawText(txt, 0, txt.length(), drawingRect.left + 6, drawingRect.top, textPaint);
}
private void drawNoInforEventAheadFirstEvent(final Canvas canvas, final int channelPosition, final Rect drawingRect, Program firstEvent) {
Log.d(TAG, "drawNoInforEventAheadFirstEvent: call this function");
String title = "No Information";
setEventDrawingRectangle(channelPosition, mTimeLowerBoundary - 1, firstEvent.getStartTimeUtcMillis() - 1, drawingRect, title);
// Background
mPaint.setColor(drawCurrentTimeLine ? mEventLayoutBackgroundCurrent : mEventLayoutBackground);
canvas.drawRect(drawingRect, mPaint);
// Text
mPaint.setColor(mEventLayoutTextColor);
mPaint.setTextSize(mEventLayoutTextSize);
// Move drawing.top so text will be centered (text is drawn bottom>up)
mPaint.getTextBounds(title, 0, title.length(), mMeasuringRect);
drawingRect.top += (((drawingRect.bottom - drawingRect.top) / 2) + (mMeasuringRect.height() / 2));
// Text
/*if (currentChannelPosition == channelPosition && mNeedDrawSelectedProgram) {
mPaint.setColor(Color.YELLOW);
mPaint.setTextSize(mEventLayoutTextSize);
} else {
mPaint.setColor(mEventLayoutTextColor);
mPaint.setTextSize(mEventLayoutTextSize);
}*/
//NEW CHANGES USING SHOW_TYPE of EVENT
title = title.substring(0, mPaint.breakText(title, true, drawingRect.right - drawingRect.left, null));
TextPaint textPaint = new TextPaint(Paint.LINEAR_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(mPaint.getColor());
textPaint.setTextSize(mPaint.getTextSize());
textPaint.setStyle(mPaint.getStyle());
textPaint.setTypeface(mPaint.getTypeface());
CharSequence txt = null;
txt = TextUtils.ellipsize(title, textPaint, (drawingRect.right - drawingRect.left) - 10, TextUtils.TruncateAt.END);
canvas.drawText(txt, 0, txt.length(), drawingRect.left + 6, drawingRect.top, textPaint);
}
private void drawNoInforEventFollowingLastEvent(final Canvas canvas, final int channelPosition, final Rect drawingRect, Program lastEvent) {
String title = "No Information";
setEventDrawingRectangle(channelPosition, lastEvent.getEndTimeUtcMillis() + 1, mTimeUpperBoundary - 1, drawingRect, title);
// Background
mPaint.setColor(drawCurrentTimeLine ? mEventLayoutBackgroundCurrent : mEventLayoutBackground);
canvas.drawRect(drawingRect, mPaint);
// Text
mPaint.setColor(mEventLayoutTextColor);
mPaint.setTextSize(mEventLayoutTextSize);
// Move drawing.top so text will be centered (text is drawn bottom>up)
mPaint.getTextBounds(title, 0, title.length(), mMeasuringRect);
drawingRect.top += (((drawingRect.bottom - drawingRect.top) / 2) + (mMeasuringRect.height() / 2));
// Text
/*if (currentChannelPosition == channelPosition && mNeedDrawSelectedProgram) {
mPaint.setColor(Color.YELLOW);
mPaint.setTextSize(mEventLayoutTextSize);
} else {
mPaint.setColor(mEventLayoutTextColor);
mPaint.setTextSize(mEventLayoutTextSize);
}*/
//NEW CHANGES USING SHOW_TYPE of EVENT
title = title.substring(0, mPaint.breakText(title, true, drawingRect.right - drawingRect.left, null));
TextPaint textPaint = new TextPaint(Paint.LINEAR_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(mPaint.getColor());
textPaint.setTextSize(mPaint.getTextSize());
textPaint.setStyle(mPaint.getStyle());
textPaint.setTypeface(mPaint.getTypeface());
CharSequence txt = null;
txt = TextUtils.ellipsize(title, textPaint, (drawingRect.right - drawingRect.left) - 10, TextUtils.TruncateAt.END);
canvas.drawText(txt, 0, txt.length(), drawingRect.left + 6, drawingRect.top, textPaint);
}
public void redraw() {
//Log.d("wujiang", "redraw: call this fun");
isAutoFocusChange = false;
invalidate();
requestLayout();
}
public void redraw(boolean autoFocusChange) {
//Log.d("wujiang", "redraw: call this fun with autoFocusChange");
isAutoFocusChange = autoFocusChange;
invalidate();
requestLayout();
}
void resetBoundaries() {
mMillisPerPixel = calculateMillisPerPixel();
mTimeOffset = calculatedBaseLine();
mTimeLowerBoundary = getTimeFrom(0);
mTimeUpperBoundary = getTimeFrom(getWidth());
//Log.d("wujiang", "resetBoundaries: mMillisPerPixel = " + mMillisPerPixel);
//Log.d("wujiang", "resetBoundaries: mTimeOffset = " + mTimeOffset);
//Log.d("wujiang", "resetBoundaries: mTimeLowerBoundary = " + mTimeLowerBoundary);
//Log.d("wujiang", "resetBoundaries: mTimeUpperBoundary = " + mTimeUpperBoundary);
//Log.d("wujiang", "resetBoundaries: getWidth = " + getWidth());
}
public void setDrawNeedSelect(boolean select) {
//Log.d("wujiang", "setDrawNeedSelect: select = " + select);
mNeedDrawSelectedProgram = select;
}
public void setAdjustmentHeight(int height) {
adjustment_height = height;
//Log.d("wujiang", "setAdjustmentHeight: adjustment_height = " + adjustment_height);
}
public void setAdjustOnlyTimes(boolean enable) {
adjust_only_one_time = enable;
//Log.d("wujiang", "setAdjustmentHeight: adjust_only_one_time = " + adjust_only_one_time);
}
/**
* @return :EpgView屏幕宽度当成2个小时,计算出每一毫秒占的像素值
*/
private long calculateMillisPerPixel() {
return HOURS_IN_VIEWPORT_MILLIS / (getResources().getDisplayMetrics().widthPixels - mChannelListViewWidth);
}
private Rect calculateProgramsHitArea() {
mMeasuringRect.top = mTimeBarHeight;
int visibleChannelsHeight = serviceList.size() * (mChannelLayoutHeight + mChannelLayoutMargin);
mMeasuringRect.bottom = visibleChannelsHeight < getHeight() ? visibleChannelsHeight : getHeight();
mMeasuringRect.left = mChannelLayoutWidth;
mMeasuringRect.right = getWidth();
return mMeasuringRect;
}
private long calculatedBaseLine() {
long current_time_millis = mTvClock.currentTimeMillis();
DateTime dateTime = new DateTime(current_time_millis).minusMillis(DAYS_BACK_MILLIS);
if (DAYS_BACK_MILLIS > 0) {
return dateTime.withTimeAtStartOfDay().getMillis();
} else {
return EPGUtils.roundOfTimeToLowerBound(dateTime.getMillis());
}
}
private void calculateMaxVerticalScroll() {
//final int maxVerticalScroll = getTopFrom(epgData.getChannelCount() - 2) + mChannelLayoutHeight;
//mMaxVerticalScroll = maxVerticalScroll < getHeight() ? 0 : maxVerticalScroll - getHeight();
}
private void calculateMaxHorizontalScroll() {
//mMaxHorizontalScroll = (int) (mChannelLayoutMargin + mChannelLayoutWidth + (DAYS_BACK_MILLIS + EPGUtils.roundOfTimeToLowerBound(LocalDateTime.now().toDateTime().getMillis()) + HOURS_IN_VIEWPORT_MILLIS + ((LocalDateTime.now().toDateTime().withTime(23, 59, 59, 999).getMillis())) + DAYS_FORWARD_MILLIS) / mMillisPerPixel);
}
private int getXPositionStart() {
if (DAYS_BACK_MILLIS > 0) {
int temp = getXFrom(EPGUtils.roundOfTimeToLowerBound(mTvClock.currentTimeMillis())) - mChannelLayoutMargin - mChannelLayoutWidth;
return temp;
} else {
return 0;
}
}
/**
* 设置要显示的节目
* @param context
* @param channelArrayList
* @param fragment
* @param default_position
*/
public void setChannelsData(Context context, ArrayList<Channel> channelArrayList, EpgMenuFragment fragment, int default_position) {
contextObj = context;
serviceList = channelArrayList;
mEpgMenuFragment = fragment;
isAutoFocusChange = false;
Log.d(TAG, "EpgView: setChannelsData->serviceList size = " + serviceList.size());
if (serviceList.size() == 0) {
return;
}
currentChannelPosition = default_position;
//Log.d("wujiang", "EpgView: currentChannelPosition = " + currentChannelPosition);
int programPosition = getProgramPositionNew(currentChannelPosition, mTvClock.currentTimeMillis());
currentProgram = ProgramsDataManager.getInstance().getProgramOfChannelByPosition(serviceList.get(currentChannelPosition).getId(), programPosition);
//Log.d("wujiang", "setEPGData: programPosition = " + programPosition);
//Log.d("wujiang", "setEPGData: channel name = " + serviceList.get(currentChannelPosition).getDisplayName());
if (currentProgram == null) {
Log.d(TAG, "setChannelsData: currentProgram == null");
}
//暂时不设置被选中的event
//if (currentProgram != null) {
// currentProgram.setSelected(true);
//}
referenceEventPosition = programPosition;
//Log.e(TAG, " * * * EPG setEPGData() (referenceEventPosition) " + referenceEventPosition);
referenceEvent = currentProgram;
}
/**
* @param x 像素值(某个地方距epg起始地方的偏移值)
* @return 得到x地方的时刻,单位毫秒
*/
private long getTimeFrom(int x) {
return (x * mMillisPerPixel) + mTimeOffset;
}
/**
* @param position 可以理解成第几个节目
* @return 第position节目显示区域顶部位置像素值
*/
private int getTopFrom(int position) {
int y = position * (mChannelLayoutHeight + mChannelLayoutMargin) + mChannelLayoutMargin + mTimeBarHeight;
return y;
}
private int getXFrom(long time) {
return (int) ((time - mTimeOffset) / mMillisPerPixel) + mChannelLayoutMargin + mChannelLayoutWidth + mChannelLayoutMargin;
}
private boolean isEventStartTimeVisible(final long start) {
return (start >= mTimeLowerBoundary || start <= mTimeUpperBoundary);
}
/**
* 在节目对应的EPG信息列表中查找起始时间和结束时间在time之间的那个EPG
* @param channelPosition
* @param time
* @return
*/
private int getProgramPositionNew(int channelPosition, long time) {
List<Program> events = ProgramsDataManager.getInstance().getChannelProgramsByChannelId(serviceList.get(channelPosition).getId());
//Log.d("wujiang", "getProgramPositionNew 1: events size = " + events.size());
//Log.d("wujiang", "getProgramPositionNew 1: time = " + time);
if (events != null) {
for (int eventPos = 0; eventPos < events.size(); eventPos++) {
Program event = events.get(eventPos);
//Log.d("wujiang", "getProgramPositionNew 1: event getIntStartTime = " + event.getStartTimeUtcMillis());
//Log.d("wujiang", "getProgramPositionNew 1: event getIntStopTime = " + event.getEndTimeUtcMillis());
//Log.d("wujiang", "getProgramPositionNew 1: event title = " + event.getTitle());
if ((event.getStartTimeUtcMillis()) <= time && ((event.getEndTimeUtcMillis()) > time)) {
return eventPos;
}
}
}
return -1;
}
/**
* 在节目对应的EPG信息列表中查找时间在time和time1之间的那个EPG
* @param channelPosition
* @param time
* @param time1
* @return
*/
private int getProgramPositionNew(int channelPosition, long time, long time1) {
Log.d("wujiang", "getProgramPositionNew: channel id = " + serviceList.get(channelPosition).getId());
List<Program> events = ProgramsDataManager.getInstance().getChannelProgramsByChannelId(serviceList.get(channelPosition).getId());
if (events != null) {
//Log.d("wujiang", "getProgramPositionNew: events size = " + events.size());
for (int eventPos = 0; eventPos < events.size(); eventPos++) {
Program event = events.get(eventPos);
//DLog.d("wujiang", "getProgramPositionNew: event getIntStartTime = " + event.getIntStartTime());
//DLog.d("wujiang", "getProgramPositionNew: event getIntStopTime = " + event.getIntStopTime());
if (event.getEndTimeUtcMillis() > time && event.getStartTimeUtcMillis() < time1) {
return eventPos;
}
}
}
return -1;
}
public void moveToDestPosition(int position, int direction) {
//Log.d("wujiang", "moveToDestPosition: position = " + position);
if (currentProgram != null) {
//Log.d("wujiang", "moveToDestPosition: 1111111111");
isMoveRight = false;
isMoveLeft = false;
isAutoFocusChange = false;
long time = 0;
if (referenceEvent.getStartTimeUtcMillis() < mTimeOffset) {
time = mTimeOffset + 1;
} else if (isCurrent(referenceEvent) && referenceEvent.getStartTimeUtcMillis() < EPGUtils.roundOfTimeToLowerBound(mTvClock.currentTimeMillis())) {
time = EPGUtils.roundOfTimeToLowerBound(mTvClock.currentTimeMillis()) + 1;
} else {
time = EPGUtils.roundOfTimeToLowerBound(referenceEvent.getStartTimeUtcMillis()) + 1;
}
//gotoProgram(position, time);
//这里取代了gotoProgram,是因为挨着的两天如果有一天没EPG时,上下切换节目时有的时候会找不到event,所以就在mTimeLowerBoundary到mTimeUpperBoundary之间找
gotoProgramBetweenTimeNew(position, mTimeLowerBoundary, mTimeUpperBoundary);
invalidate();
int y = getTopFrom(position);
if (direction == UP_DIRECTION) {
int programAreaY = calculateProgramsHitArea().top + getScrollY();
if (y < programAreaY) {
scrollBy(0, y - programAreaY);
}
} else {
//总的channel个数大于MaxItemCountInOnePage个才允许去滑动
//Log.d("wujiang", "moveToDestPosition: position = " + position);
if (position >= MaxItemCountInOnePage) {
int programAreaY = calculateProgramsHitArea().bottom + getScrollY() - mChannelLayoutHeight;
if (y > programAreaY) {
scrollBy(0, y - programAreaY);
}
}
}
}
}
/**
* 获取当前event的上一个event
*
* @param channelPosition 当前channel的位置
* @param programmeBeanProgram 焦点所在的event
* @return true:找到了上一个event; false:没有找到上一个event
*/
private boolean gotoAheadProgramNew(int channelPosition, Program programmeBeanProgram) {
int current_index = ProgramsDataManager.getInstance().getPositionOfProgram(serviceList.get(channelPosition).getId(), programmeBeanProgram);
//Log.d("wujiang", "gotoAheadProgramNew: current_index = " + current_index);
//Log.d("wujiang", "gotoAheadProgramNew: current programme name = " + programmeBeanrogram.getTitle());
//Log.d("wujiang", "gotoAheadProgramNew: current programme start = " + programmeBeanrogram.getStart());
isMoveRight = false;
isMoveLeft = true;
if (current_index != -1) {
if (current_index == 0 || programmeBeanProgram.getStartTimeUtcMillis() <= mTimeOffset) {
Log.d(TAG, "gotoAheadProgramNew: It is the first event.");
return false;
}
ProgramsDataManager.getInstance().clearSelection();
Program event = ProgramsDataManager.getInstance().getProgramOfChannelByPosition(serviceList.get(channelPosition).getId(), current_index - 1);
if (event.getEndTimeUtcMillis() <= mTimeOffset) {
Log.d(TAG, "gotoAheadProgramNew: It is the lowest time event");
return false;
}
event.setSelected(true);
currentProgram = event;
//Log.d("wujiang", "gotoAheadProgramNew: currentProgramname = " + currentProgram.getTitle());
//Log.d("wujiang", "gotoAheadProgramNew: currentProgram start = " + currentProgram.getStart());
if (isMoveLeft || isMoveRight) {
referenceEvent = event;
} else {
referenceEventPosition = current_index - 1;
}
Log.d(TAG, "gotoAheadProgramNew: Find a event in epg data");
return true;
}
int program_count = ProgramsDataManager.getInstance().getProgramCountOfChannel(serviceList.get(channelPosition).getId());
if (program_count > 0) {
for (int i = program_count - 1; i >= 0; i--) {
Program event = ProgramsDataManager.getInstance().getProgramOfChannelByPosition(serviceList.get(channelPosition).getId(), i);
if (event.getEndTimeUtcMillis() <= mTimeLowerBoundary && event.getStartTimeUtcMillis() >= mTimeOffset) {
ProgramsDataManager.getInstance().clearSelection();
event.setSelected(true);
currentProgram = event;
//Log.d("wujiang", "gotoAheadProgramNew: currentProgramname = " + currentProgram.getTitle());
//Log.d("wujiang", "gotoAheadProgramNew: currentProgram start = " + currentProgram.getStart());
if (isMoveLeft || isMoveRight) {
referenceEvent = event;
} else {
referenceEventPosition = i;
}
return true;
}
}
}
Log.d(TAG, "gotoAheadProgramNew: Don't find any event");
return false;
}
/**
* 获取当前event的下一个event
*
* @param channelPosition 当前channel
* @param programmeBeanProgram 焦点所在的event
* @return true:找到了下一个event; false:没有找到下一个event
*/
private boolean gotoNextProgramNew(int channelPosition, Program programmeBeanProgram) {
int current_index = ProgramsDataManager.getInstance().getPositionOfProgram(serviceList.get(channelPosition).getId(), programmeBeanProgram);
//Log.d("wujiang", "gotoNextProgramNew: current_index = " + current_index);
//Log.d("wujiang", "gotoNextProgramNew: current programme name = " + programmeBeanrogram.getTitle());
//Log.d("wujiang", "gotoNextProgramNew: current programme start = " + programmeBeanrogram.getStartTimeUtcMillis());
isMoveRight = true;
isMoveLeft = false;
if (current_index != -1) {
if (current_index >= (ProgramsDataManager.getInstance().getProgramCountOfChannel(serviceList.get(channelPosition).getId()) - 1)) {
Log.d(TAG, "gotoNextProgramNew: It is the last event");
return false;
}
ProgramsDataManager.getInstance().clearSelection();
Program event = ProgramsDataManager.getInstance().getProgramOfChannelByPosition(serviceList.get(channelPosition).getId(), current_index + 1);
event.setSelected(true);
currentProgram = event;
//Log.d("wujiang", "gotoNextProgramNew: found next program = " + currentProgram.getTitle());
//Log.d("wujiang", "gotoNextProgramNew: next program start = " + currentProgram.getStartTimeUtcMillis());
if (isMoveLeft || isMoveRight) {
referenceEvent = event;
} else {
referenceEventPosition = current_index + 1;
}
Log.d(TAG, "gotoNextProgramNew: Find a event in epg data");
return true;
}
int program_count = ProgramsDataManager.getInstance().getProgramCountOfChannel(serviceList.get(channelPosition).getId());
Log.d(TAG, "gotoNextProgramNew: program_count = " + program_count);
if (program_count > 0) {
for (int i = 0; i < program_count; i++) {
Program event = ProgramsDataManager.getInstance().getProgramOfChannelByPosition(serviceList.get(channelPosition).getId(), i);
if (event.getStartTimeUtcMillis() >= mTimeUpperBoundary) {
ProgramsDataManager.getInstance().clearSelection();
event.setSelected(true);
currentProgram = event;
//Log.d("wujiang", "gotoNextProgramNew: currentProgramname = " + currentProgram.getTitle());
//Log.d("wujiang", "gotoNextProgramNew: currentProgram start = " + currentProgram.getStartTimeUtcMillis());
if (isMoveLeft || isMoveRight) {
referenceEvent = event;
} else {
referenceEventPosition = i;
}
return true;
}
}
}
Log.d(TAG, "gotoNextProgramNew: Don't find any event");
return false;
}
/**
* 去找特定的event
*
* @param channelPosition 被找的节目索引
* @param time 显示屏最左侧时刻
* @param time1 显示屏最右侧时刻
* @return 找到的event,没找到返回null
*/
private Program gotoProgramBetweenTimeNew(int channelPosition, long time, long time1) {
if (channelPosition > -1 && channelPosition < serviceList.size()) {
int programPosition = getProgramPositionNew(channelPosition, time, time1);
if (programPosition != -1) {
//Log.d("wujiang", "gotoProgramBetweenTimeNew: 11111111111");
ProgramsDataManager.getInstance().clearSelection();
Program event = ProgramsDataManager.getInstance().getProgramOfChannelByPosition(serviceList.get(channelPosition).getId(), programPosition);
event.setSelected(true);
currentProgram = event;
currentChannelPosition = channelPosition;
if (isMoveLeft || isMoveRight) {
referenceEvent = event;
} else {
referenceEventPosition = programPosition;
}
return event;
} else {
//Log.d("wujiang", "gotoProgramBetweenTimeNew: 22222222222222");
//没有EPG信息
ProgramsDataManager.getInstance().clearSelection();
Program event = new Program();
event.setTitle("No Information");
event.setStartTimeUtcMillis(mTimeLowerBoundary);
event.setEndTimeUtcMillis(mTimeUpperBoundary);
event.setSelected(true);
currentProgram = event;
currentChannelPosition = channelPosition;
//这里没有判断是否是左右切换
referenceEvent = event;
return event;
}
}
return null;
}
private Program gotoProgramNotSelectNew(int channelPosition, long time) {
Log.d(TAG, "gotoProgramNotSelectNew: call this fun, channelPosition = " + channelPosition);
if (channelPosition > -1 && channelPosition < serviceList.size()) {
int programPosition = getProgramPositionNew(channelPosition, time);
Log.d(TAG, "gotoProgramNotSelectNew: programPosition = " + programPosition);
if (programPosition != -1) {
ProgramsDataManager.getInstance().clearSelection();
Program event = ProgramsDataManager.getInstance().getProgramOfChannelByPosition(serviceList.get(channelPosition).getId(), programPosition);
event.setSelected(true);
currentProgram = event;
currentChannelPosition = channelPosition;
if (isMoveLeft || isMoveRight) {
referenceEvent = event;
} else {
referenceEventPosition = programPosition;
}
return event;
}
}
return null;
}
/**
*
* @return
* false: 再也找不到后续event了,即当前event是最后一个event
* true: 找到了后续的event
*/
public boolean moveRight() {
if (currentProgram != null) {
isAutoFocusChange = false;
if (!gotoNextProgramNew(currentChannelPosition, currentProgram)) {
return false;
}
/**
* 调用了上面的gotoNextProgram就不需要调用gotoProgram了
isMoveRight = true;
isMoveLeft = false;
gotoProgram(currentChannelPosition, currentProgram.getIntStopTime() + 1);
*/
invalidate();
int x = 0;
long dur = currentProgram.getEndTimeUtcMillis() - currentProgram.getStartTimeUtcMillis();
long currentUpperBound = EPGUtils.roundOfTimeToLowerBound(currentProgram.getStartTimeUtcMillis()) + HOURS_IN_VIEWPORT_MILLIS;
if (dur > HOURS_IN_VIEWPORT_MILLIS && !isEventStartTimeVisible(currentProgram.getStartTimeUtcMillis())) {
//Log.d("wujiang", "moveRight: 1111111111111111");
if (EPGUtils.roundOfTimeToLowerBound(currentProgram.getStartTimeUtcMillis()) != currentProgram.getStartTimeUtcMillis()) {
x = getXFrom(currentProgram.getStartTimeUtcMillis() + ((30 * 60 * 1000) - (currentProgram.getStartTimeUtcMillis() - EPGUtils.roundOfTimeToLowerBound(currentProgram.getStartTimeUtcMillis()))));
} else {
x = getXFrom(currentProgram.getStartTimeUtcMillis() + (HOURS_IN_VIEWPORT_MILLIS - 30 * 60 * 1000));
}
//x = getXFrom((currentProgram.getStart() - EPGUtils.roundOfTimeToLowerBound(currentProgram.getStart())) + HOURS_IN_VIEWPORT_MILLIS);
} else if (dur > HOURS_IN_VIEWPORT_MILLIS) {
//Log.d("wujiang", "moveRight: 22222222222");
if (EPGUtils.roundOfTimeToLowerBound(currentProgram.getStartTimeUtcMillis()) != currentProgram.getStartTimeUtcMillis()) {
x = getXFrom(currentProgram.getStartTimeUtcMillis() + ((60 * 60 * 1000) - ((currentProgram.getStartTimeUtcMillis() - EPGUtils.roundOfTimeToLowerBound(currentProgram.getStartTimeUtcMillis())))));
} else {
x = getXFrom(currentProgram.getStartTimeUtcMillis() + (HOURS_IN_VIEWPORT_MILLIS - 60 * 60 * 1000));
}
} else if (EPGUtils.roundOfTimeToLowerBound(currentProgram.getStartTimeUtcMillis()) != currentProgram.getStartTimeUtcMillis()) {
//Log.d("wujiang", "moveRight: 33333333333333");
x = getXFrom(currentProgram.getStartTimeUtcMillis() + ((30 * 60 * 1000) - (currentProgram.getStartTimeUtcMillis() - EPGUtils.roundOfTimeToLowerBound(currentProgram.getStartTimeUtcMillis()))));
} else {
//Log.d("wujiang", "moveRight: 444444444444444");
x = getXFrom(currentProgram.getStartTimeUtcMillis() + (30 * 60 * 1000));
}
//EPGUtils.printTime("MoveRight", mTimeUpperBoundary);
//int x = getXFrom(currentProgram.getEndTime());
//Log.d("wujiang", "moveRight: x = " + x);
//Log.d("wujiang", "moveRight: getScrollX() = " + getScrollX());
//Log.d("wujiang", "moveRight: getWidth() = " + getWidth());
int programAreaX = calculateProgramsHitArea().right + getScrollX();
//Log.d("wujiang", "moveRight: calculateProgramsHitArea().right = " + calculateProgramsHitArea().right);
if (x > programAreaX) {
scrollBy(x - programAreaX, 0);
}
Log.d(TAG, "moveRight: call end");
return true;
}
return false;
}
public boolean moveLeft() {
if (currentProgram != null) {
isAutoFocusChange = false;
/*if (currentProgram.getStartTimeUtcMillis() <= mTimeOffset) {
//Log.d("wujiang", "moveLeft: 1111111");
return false;
}
isMoveRight = false;
isMoveLeft = true;
gotoProgram(currentChannelPosition, currentProgram.getStartTimeUtcMillis() - 1);*/
if (!gotoAheadProgramNew(currentChannelPosition, currentProgram)) {
return false;
}
invalidate();
int x = 0;//getXFrom(currentProgram.getStartTime());
if (currentProgram.getStartTimeUtcMillis() < mTimeOffset) {
x = getXFrom(mTimeOffset);
} else {
x = getXFrom(EPGUtils.roundOfTimeToLowerBound(currentProgram.getStartTimeUtcMillis()));
}
int programAreaX = calculateProgramsHitArea().left + getScrollX();
if (x < programAreaX) {
scrollBy(x - programAreaX, 0);
}
} else {
Log.e(TAG, " @ MoveLeft @ I think CatchUp is enabled");
return false;
}
return true;
}
/**
* 显示上一天开始的epg信息
*/
public void moveToPrevDay() {
if (serviceList == null || serviceList.size() <= 0) {
return;
}
//当前页所在天的0点时刻
long currentPageDayStartTime = EPGUtils.getDayStartTime(getTimeLowerBoundary());
//当前页所在的天的上一天的0点时刻
long prevDayStartTime = currentPageDayStartTime - EPGUtils.DAY_OF_SECONDS * EPGUtils.ONE_THOUSAND;
//Log.d("wujiang", "prevDayStartTime = " + prevDayStartTime);
if (prevDayStartTime <= mTimeOffset) {
prevDayStartTime = mTimeOffset;
}
referenceEvent = gotoProgramBetweenTimeNew(currentChannelPosition, prevDayStartTime, prevDayStartTime + EPGUtils.DAY_OF_SECONDS * EPGUtils.ONE_THOUSAND);
int scrollX = getXFrom(prevDayStartTime);
scrollTo(scrollX, getScrollY());
}
/**
* 显示下一天开始的epg信息
*/
public void moveToNextDay() {
if (serviceList == null || serviceList.size() <= 0) {
return;
}
//当天的凌晨0点时间
long firstDayStartTime = EPGUtils.getDayStartTime(mTimeOffset);
//当前页EPG所属Day的0点时刻
long currentPageDayStartTime = EPGUtils.getDayStartTime(getTimeLowerBoundary());
//当前页晚上24点时刻
long currentPageEndTime = currentPageDayStartTime + EPGUtils.DAY_OF_SECONDS * EPGUtils.ONE_THOUSAND;
//Log.d("wujiang", "nextDayStartTime = " + currentPageEndTime);
if ((currentPageEndTime / 1000 - firstDayStartTime / 1000) >= 7 * EPGUtils.DAY_OF_SECONDS) {
PlayerUtil.showToast("It’s the last day!", 1000);
return;
}
long queryEndTime = currentPageEndTime + EPGUtils.DAY_OF_SECONDS * EPGUtils.ONE_THOUSAND;
//Log.d("wujiang", "queryEndTime = " + queryEndTime);
ArrayList<Long> queryChannelsArrayList = new ArrayList<>();
int firstVisibleChannelPosition = getFirstVisibleChannelPosition();
int lastVisibleChannelPosition = getLastVisibleChannelPosition();
for (int pos = firstVisibleChannelPosition; pos <= lastVisibleChannelPosition; pos++) {
Channel channel = serviceList.get(pos);
//Log.d("wujiang", "moveToNextDay: channel name = " + channel.getDisplayName());
queryChannelsArrayList.add(channel.getId());
}
referenceEvent = gotoProgramBetweenTimeNew(currentChannelPosition, currentPageEndTime, currentPageEndTime + EPGUtils.DAY_OF_SECONDS * EPGUtils.ONE_THOUSAND);
//Log.d("wujiang", "moveToNextDay: referenceEvent startTime = " + referenceEvent.getStartTimeUtcMillis());
//Log.d("wujiang", "moveToNextDay: referenceEvent endTime = " + referenceEvent.getEndTimeUtcMillis());
int scrollX = getXFrom(currentPageEndTime);
scrollTo(scrollX, getScrollY());
ProgramsDataManager.getInstance().queryChannelsProgramFromDatabase(firstDayStartTime, queryEndTime, queryChannelsArrayList, 0);
}
/**
* 显示当天开始的epg信息
*/
public void moveToToday() {
if (serviceList == null || serviceList.size() <= 0) {
return;
}
referenceEvent = gotoProgramBetweenTimeNew(currentChannelPosition, mTimeOffset, mTimeOffset + EPGUtils.DAY_OF_SECONDS * EPGUtils.ONE_THOUSAND);
int scrollX = getXFrom(mTimeOffset);
scrollTo(scrollX, getScrollY());
}
public long getTimeLowerBoundary() {
return mTimeLowerBoundary;
}
public long getTimeUpperBoundary() {
return mTimeUpperBoundary;
}
public long getTimeOffset () {
return mTimeOffset;
}
/**
* This will recalculate boundaries, maximal scroll and scroll to start position which is current time.
* To be used on device rotation etc since the device height and width will change.
*
* @param withAnimation
* @param default_position 默认位置,也就是第一行显示正在播放节目的EPG.
* @param needDrawSelect 是否要画焦点状态
*/
public void recalculateAndRedraw(boolean withAnimation, int default_position, boolean needDrawSelect) {
mNeedDrawSelectedProgram = needDrawSelect;
//Log.d("wujiang", "recalculateAndRedraw: default_position = " + default_position);
//Log.d("wujiang", "recalculateAndRedraw: mNeedDrawSelectedProgram = " + mNeedDrawSelectedProgram);
if (serviceList != null && serviceList.size() > 0) {
resetBoundaries();
calculateMaxVerticalScroll();
calculateMaxHorizontalScroll();
//注意:这里需要微调
//本意是只调用下getTopFrom(default_position)就行了的,但实际效果不是,于是经多次调试得出先减去mChannelLayoutHeight再减去19。
mScroller.startScroll(getScrollX(), getScrollY(), getXPositionStart() - getScrollX(), getTopFrom(default_position) - mChannelLayoutHeight - 19, withAnimation ? 0 : 0);
if (currentProgram == null) {
referenceEvent = gotoProgramBetweenTimeNew(currentChannelPosition, mTimeLowerBoundary, mTimeUpperBoundary);
} else {
if ((currentProgram.getStartTimeUtcMillis() > EPGUtils.getCurrentTimeInMillis()) || (currentProgram.getStartTimeUtcMillis() < EPGUtils.getCurrentTimeInMillis())) {
int eventPosition = getProgramPositionNew(currentChannelPosition, EPGUtils.getCurrentTimeInMillis());
Program event = ProgramsDataManager.getInstance().getProgramOfChannelByPosition(serviceList.get(currentChannelPosition).getId(), eventPosition);
if (mNeedDrawSelectedProgram == false) {
referenceEvent = gotoProgramNotSelectNew(currentChannelPosition, event.getStartTimeUtcMillis());
} else {
referenceEvent = gotoProgramBetweenTimeNew(currentChannelPosition, mTimeLowerBoundary, mTimeUpperBoundary);
}
}
}
} else {
Log.d(TAG, "recalculateAndRedraw: serviceList size = 0");
resetBoundaries();
}
redraw();
}
/**
* 查找到将要获取到焦点的program
* 判断position的理由:
* 当上下切换节目后,立马向右切换program,会看到焦点里面又回到了第一个program上。
* 即如果referenceEvent是一个真实存在的program,即不需要再去找即将要获取焦点的program。
*/
public void findBeSelectedProgram() {
if (referenceEvent == null) {
referenceEvent = gotoProgramBetweenTimeNew(currentChannelPosition, mTimeLowerBoundary, mTimeUpperBoundary);
return;
}
int position = ProgramsDataManager.getInstance().getPositionOfProgram(serviceList.get(currentChannelPosition).getId(), referenceEvent);
Log.d(TAG, "findBeSelectedProgram: position = " + position);
if (position == -1) {
referenceEvent = gotoProgramBetweenTimeNew(currentChannelPosition, mTimeLowerBoundary, mTimeUpperBoundary);
return;
} else {
/**
* 当找到了referenceEvent,如果这个referenceEvent的结束时间超过了当天的24点,那么它就被认为是可见的event。
* 当在这种event获取焦点时按绿色键请求下一天的epg后,需要用它的位置获取到该channel新的program list里对应的那个event(老的program list已经被clear了),
* 并设置它为选中状态。不这样做的话,当请求完下一天的epg后,焦点会变的没有。
**/
referenceEvent = ProgramsDataManager.getInstance().getProgramOfChannelByPosition(serviceList.get(currentChannelPosition).getId(), position);
referenceEvent.setSelected(true);
}
//Log.d("wujiang", "findBeSelectedProgram: referenceEvent start time = " + referenceEvent.getStartTimeUtcMillis());
//Log.d("wujiang", "findBeSelectedProgram: referenceEvent end time = " + referenceEvent.getEndTimeUtcMillis());
//Log.d("wujiang", "findBeSelectedProgram: referenceEvent title = " + referenceEvent.getTitle());
if (isEventVisible(referenceEvent.getStartTimeUtcMillis(), referenceEvent.getEndTimeUtcMillis())) {
Log.d(TAG, "findBeSelectedProgram: referenceEvent is visible");
return;
} else {
Log.d(TAG, "findBeSelectedProgram: referenceEvent is invisible");
referenceEvent = gotoProgramBetweenTimeNew(currentChannelPosition, mTimeLowerBoundary, mTimeUpperBoundary);
}
}
private boolean isCurrent(Program program) {
if (program == null) {
return false;
}
long now = mTvClock.currentTimeMillis();
return now >= (program.getStartTimeUtcMillis()) && now <= (program.getEndTimeUtcMillis());
}
}
//EpgView的部分资源文件
<dimen name="epg_line_space">1dp</dimen>
<dimen name="epg_event_space">1dp</dimen>
<dimen name="listviews_space">1dp</dimen>
<dimen name="epg_time_line_width">1dp</dimen>
<dimen name="zero">0dp</dimen>
<dimen name="epg_view_animator_width">2138dp</dimen>
<dimen name="epg_group_listview_width">109dp</dimen>
<dimen name="epg_channel_listview_width">162dp</dimen>
<dimen name="epg_view_width">688dp</dimen>
<dimen name="epg_view_large_width">797dp</dimen>
<dimen name="epg_event_text_size">@dimen/px_26.0</dimen>
<dimen name="epg_time_bar_text_size">@dimen/px_25.0</dimen>
<dimen name="epg_help_bar_height">71px</dimen>
<dimen name="dp_16">16dp</dimen>
大部分的重要的代码都贴上了,希望对你有帮助!