由于毕业设计有一个功能模块是课程表,就想模仿一下超级课程表的界面,可是开始做的时候却没有一点头绪,百度google均无果,在CSDN和知乎上提问了也没人回答(估计是太简单了 大神不愿回答 尴尬 ),总之自己鼓捣了几天还是弄出来了,虽然实现的方法很挫。。。因为有好几个人都发私信问我怎么实现的,现在毕设做完了,所以我干脆就写到博客上吧,先上几张效果图:

当时看到超级课程表的界面时,第一个想法就是使用ListView来实现,好不容易把格子画出来了,课程信息不知道怎么放上去····,主要难点有:

1、 第一排的8个格子是固定的,下面的课表信息部分是可以滑动的,单用ListView无法实现,即下图。

2、 课程信息怎么附着在格子上,并且可以随着课表一起滚动。

放弃了ListView实现的想法,就只有另寻它路了,在CSDN上有一位朋友的回答给了我灵感,即每一个格子都用一个TextView实现。然后课程信息可以使用相对布局,根据课程的时间(节数和天数)算出他的偏移位置,比如星期二的第三、四节课就可以和周二下面的第一个格子保持左对齐和上对齐,并且向下偏移2个格子的距离。这个N个格子和课程信息都放到一个RelativeLayout中,然后再在外面嵌套一个ScrollViewLayout,就可以滚动了,总的布局还是RelayoutLayout,不多说了,直接上代码!

  1. <? xml version = "1.0" encoding = "utf-8" ?>
  2. < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
  3. android:layout_width = "fill_parent"
  4. android:layout_height = "fill_parent"
  5. android:background = "@drawable/c_bg" >
  6. <!-- 最左边空白的格子 -->
  7. < TextView android:id = "@+id/test_empty"
  8. android:layout_width = "wrap_content"
  9. android:layout_height = "wrap_content"
  10. style = "@style/courseTableText"
  11. android:text = "@string/empty"
  12. android:background = "@drawable/course_text_view_bg"
  13. />
  14. <!-- 星期一的格子 -->
  15. < TextView android:id = "@+id/test_monday_course"
  16. android:layout_width = "wrap_content"
  17. android:layout_height = "wrap_content"
  18. android:text = "@string/mon"
  19. style = "@style/courseTableText"
  20. android:layout_toRightOf = "@id/test_empty"
  21. android:background = "@drawable/course_text_view_bg"
  22. />
  23. <!-- 星期二的格子 -->
  24. < TextView android:id = "@+id/test_tuesday_course"
  25. android:layout_width = "wrap_content"
  26. android:layout_height = "wrap_content"
  27. android:text = "@string/tue"
  28. style = "@style/courseTableText"
  29. android:layout_toRightOf = "@id/test_monday_course"
  30. android:background = "@drawable/course_text_view_bg"
  31. />
  32. <!-- 星期三的格子 -->
  33. < TextView android:id = "@+id/test_wednesday_course"
  34. android:layout_width = "wrap_content"
  35. android:layout_height = "wrap_content"
  36. android:text = "@string/wen"
  37. style = "@style/courseTableText"
  38. android:layout_toRightOf = "@id/test_tuesday_course"
  39. android:background = "@drawable/course_text_view_bg"
  40. />
  41. <!-- 星期四的格子 -->
  42. < TextView android:id = "@+id/test_thursday_course"
  43. android:layout_width = "wrap_content"
  44. android:layout_height = "wrap_content"
  45. android:text = "@string/thu"
  46. style = "@style/courseTableText"
  47. android:layout_toRightOf = "@id/test_wednesday_course"
  48. android:background = "@drawable/course_text_view_bg"
  49. />
  50. <!-- 星期五的格子 -->
  51. < TextView android:id = "@+id/test_friday_course"
  52. android:layout_width = "wrap_content"
  53. android:layout_height = "wrap_content"
  54. android:text = "@string/fri"
  55. style = "@style/courseTableText"
  56. android:layout_toRightOf = "@id/test_thursday_course"
  57. android:background = "@drawable/course_text_view_bg"
  58. />
  59. <!-- 星期六的格子 -->
  60. < TextView android:id = "@+id/test_saturday_course"
  61. android:layout_width = "wrap_content"
  62. android:layout_height = "wrap_content"
  63. android:text = "@string/sta"
  64. style = "@style/courseTableText"
  65. android:layout_toRightOf = "@id/test_friday_course"
  66. android:background = "@drawable/course_text_view_bg"
  67. />
  68. <!-- 星期天的格子 -->
  69. < TextView android:id = "@+id/test_sunday_course"
  70. android:layout_width = "wrap_content"
  71. android:layout_height = "wrap_content"
  72. style = "@style/courseTableText"
  73. android:text = "@string/sun"
  74. android:layout_toRightOf = "@id/test_saturday_course"
  75. android:background = "@drawable/course_table_last_colum"
  76. />
  77. <!-- 课程表body部分 -->
  78. < ScrollView
  79. android:id = "@+id/scroll_body"
  80. android:layout_width = "fill_parent"
  81. android:layout_height = "wrap_content"
  82. android:layout_below = "@id/test_empty"
  83. android:scrollbars = "none"
  84. >
  85. <!-- 课程信息 -->
  86. < RelativeLayout
  87. android:layout_width = "fill_parent"
  88. android:layout_height = "wrap_content"
  89. android:id = "@+id/test_course_rl"
  90. >
  91. </ RelativeLayout >
  92. </ ScrollView >
  93. </ RelativeLayout >

2、用TextView表示一个格子,即有边框的TextView,整个课表是由N个TextView组成的,所以就没办法使用一张图当背景了。

当然现在我也想到了改进的方法,其实给课程信息做参照算出偏移位置的只需要一个格子就可以了,所以可以用一个透明的TextView放在星期一的第一个格子的位置作为参照,格子使用view画线实现,就可以使用背景了,也不用生成多个TextView了···这是我的想法,我还没用代码实现,有兴趣的朋友可以自己去试试。

超级课程表还有一个要点就是它的课程展示3D视图,不过那个可以用 Android 自带的gallery实现,网上有现成的代码,拿过来改改就可以了,下面上主要代码

1、CourseTableActivity

  1. package nd.leiyi.crims.activity;
  2. import java.lang.ref.WeakReference;
  3. import java.util.ArrayList;
  4. import java.util.Collections;
  5. import java.util.Comparator;
  6. import java.util.HashMap;
  7. import java.util.Iterator;
  8. import java.util.List;
  9. import java.util.Map;
  10. import nd.leiyi.crims.R;
  11. import nd.leiyi.crims.adapter.CourseInfoAdapter;
  12. import nd.leiyi.crims.appException.AppException;
  13. import nd.leiyi.crims.constant.UserInfo;
  14. import nd.leiyi.crims.db.CourseInfoDBManager;
  15. import nd.leiyi.crims.gallery3D.CourseInfoGallery;
  16. import nd.leiyi.crims.http.CourseInfoFetcher;
  17. import nd.leiyi.crims.model.CourseInfo;
  18. import nd.leiyi.crims.util.CourseSettingUtil;
  19. import android.app.Activity;
  20. import android.app.AlertDialog;
  21. import android.app.Dialog;
  22. import android.app.ProgressDialog;
  23. import android.content.Context;
  24. import android.content.Intent;
  25. import android.content.SharedPreferences;
  26. import android.content.SharedPreferences.Editor;
  27. import android.graphics.Color;
  28. import android.graphics.drawable.BitmapDrawable;
  29. import android.graphics.drawable.Drawable;
  30. import android.os.Bundle;
  31. import android.os.Handler;
  32. import android.os.Message;
  33. import android.util.DisplayMetrics;
  34. import android.util.Log;
  35. import android.view.Gravity;
  36. import android.view.LayoutInflater;
  37. import android.view.View;
  38. import android.view.View.OnClickListener;
  39. import android.view.ViewGroup.LayoutParams;
  40. import android.view.Window;
  41. import android.view.WindowManager;
  42. import android.widget.AdapterView;
  43. import android.widget.AdapterView.OnItemClickListener;
  44. import android.widget.ListView;
  45. import android.widget.PopupWindow;
  46. import android.widget.PopupWindow.OnDismissListener;
  47. import android.widget.Button;
  48. import android.widget.RelativeLayout;
  49. import android.widget.SimpleAdapter;
  50. import android.widget.TextView;
  51. import android.widget.Toast;
  52. public class CourseTableActivity extends Activity {
  53. /** 标题栏文字 */
  54. protected TextView textTitle;
  55. /** 第一个无内容的格子 */
  56. protected TextView empty;
  57. /** 星期一的格子 */
  58. protected TextView monColum;
  59. /** 星期二的格子 */
  60. protected TextView tueColum;
  61. /** 星期三的格子 */
  62. protected TextView wedColum;
  63. /** 星期四的格子 */
  64. protected TextView thrusColum;
  65. /** 星期五的格子 */
  66. protected TextView friColum;
  67. /** 星期六的格子 */
  68. protected TextView satColum;
  69. /** 星期日的格子 */
  70. protected TextView sunColum;
  71. /** 课程表body部分布局 */
  72. protected RelativeLayout course_table_layout;
  73. /** 选择周数弹出窗口 */
  74. protected PopupWindow weekListWindow;
  75. /** 显示周数的listview*/
  76. protected ListView weekListView;
  77. /** 选择周数弹出窗口的layout */
  78. protected View popupWindowLayout;
  79. /** 课程信息 **/
  80. protected Map<String, List<CourseInfo>> courseInfoMap;
  81. /** 保存显示课程信息的TextView **/
  82. protected List<TextView> courseTextViewList = new ArrayList<TextView>();
  83. /** 保存每个textview对应的课程信息 map,key为哪一天(如星期一则key为1) **/
  84. protected Map<Integer, List<CourseInfo>> textviewCourseInfoMap = new HashMap<Integer, List<CourseInfo>>();
  85. /** 课程格子平均宽度 **/
  86. protected int aveWidth;
  87. /** 屏幕宽度 **/
  88. protected int screenWidth;
  89. /** 格子高度 **/
  90. protected int gridHeight = 80 ;
  91. /** 最大课程节数 **/
  92. protected int maxCourseNum;
  93. protected Button goBackButton;
  94. protected ProgressDialog pDialog;
  95. protected Handler mhandler = new Handler();
  96. @Override
  97. protected void onCreate(Bundle savedInstanceState) {
  98. super .onCreate(savedInstanceState);
  99. //设置自定义标题栏布局
  100. requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
  101. setContentView(R.layout.course_table_layout);
  102. getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,R.layout.title);
  103. //设置标题栏周数
  104. textTitle = (TextView) this .findViewById(R.id.textTile);
  105. textTitle.setTextSize( 20 );
  106. textTitle.setPadding( 15 , 2 , 15 , 2 );
  107. //右边白色倒三角
  108. Drawable down = this .getResources().getDrawable(R.drawable.title_down);
  109. down.setBounds( 0 , 0 , down.getMinimumWidth(), down.getMinimumHeight());
  110. textTitle.setCompoundDrawables( null , null , down, null );
  111. textTitle.setCompoundDrawablePadding( 2 );
  112. //获得列头的控件
  113. empty = (TextView) this .findViewById(R.id.test_empty);
  114. monColum = (TextView) this .findViewById(R.id.test_monday_course);
  115. tueColum = (TextView) this .findViewById(R.id.test_tuesday_course);
  116. wedColum = (TextView) this .findViewById(R.id.test_wednesday_course);
  117. thrusColum = (TextView) this .findViewById(R.id.test_thursday_course);
  118. friColum = (TextView) this .findViewById(R.id.test_friday_course);
  119. satColum  = (TextView) this .findViewById(R.id.test_saturday_course);
  120. sunColum = (TextView) this .findViewById(R.id.test_sunday_course);
  121. //返回按钮
  122. goBackButton = (Button) this .findViewById(R.id.button_go_back);
  123. goBackButton.setOnClickListener( new OnClickListener() {
  124. @Override
  125. public void onClick(View arg0) {
  126. //改变返回按钮的背景,体现出被“按下出”的感觉
  127. goBackButton.setBackgroundDrawable(CourseTableActivity. this
  128. .getResources().getDrawable(R.drawable.arrow_left_down));
  129. // 恢复背景
  130. mhandler.postDelayed( new Runnable() {
  131. @Override
  132. public void run() {
  133. goBackButton.setBackgroundDrawable(CourseTableActivity. this
  134. .getResources().getDrawable(R.drawable.arrow_left));
  135. }
  136. }, 200 );
  137. mhandler.postDelayed( new Runnable() {
  138. @Override
  139. public void run() {
  140. finish();
  141. }
  142. }, 400 );
  143. }
  144. });
  145. // 列表布局文件
  146. course_table_layout = (RelativeLayout) this .findViewById(R.id.test_course_rl);
  147. DisplayMetrics dm = new DisplayMetrics();
  148. getWindowManager().getDefaultDisplay().getMetrics(dm);
  149. //屏幕宽度
  150. int width = dm.widthPixels;
  151. //平均宽度
  152. int aveWidth = width / 8 ;
  153. //给列头设置宽度
  154. empty.setWidth(aveWidth * 3 / 4 );
  155. monColum.setWidth(aveWidth * 33 / 32 + 1 );
  156. tueColum.setWidth(aveWidth * 33 / 32 + 1 );
  157. wedColum.setWidth(aveWidth * 33 / 32 + 1 );
  158. thrusColum.setWidth(aveWidth * 33 / 32 + 1 );
  159. friColum.setWidth(aveWidth * 33 / 32 + 1 );
  160. satColum.setWidth(aveWidth * 33 / 32 + 1 );
  161. sunColum.setWidth(aveWidth * 33 / 32 + 1 );
  162. this .screenWidth = width;
  163. this .aveWidth = aveWidth;
  164. //初始化body部分
  165. init();
  166. }
  167. /**
  168. * 初始化课程表body部分
  169. * @param aveWidth
  170. */
  171. protected void init(){
  172. //获取课表配置信息
  173. final SharedPreferences courseSettings = getSharedPreferences( "course_setting" , Activity.MODE_PRIVATE);
  174. //检测是否设置过学期
  175. if (courseSettings.getString( "currentTerm_" + UserInfo.currentUser.getStuNum(), null ) == null )
  176. {
  177. Toast.makeText(CourseTableActivity. this , "您尚未设置当前学期!快去设置吧!" , Toast.LENGTH_SHORT).show();
  178. return ;
  179. }
  180. //计算出当前的周数
  181. final String currentWeekStr = CourseSettingUtil.figureCurrentWeek(courseSettings);
  182. if (currentWeekStr.equals( "" ))
  183. {
  184. textTitle.setText( "全部" );
  185. }
  186. else
  187. {
  188. textTitle.setText( "第" + currentWeekStr + "周" );
  189. }
  190. //设置点击事件
  191. textTitle.setOnClickListener( new OnClickListener() {
  192. @Override
  193. public void onClick(View arg0) {
  194. //改变背景(体现出被"按下去"的感觉
  195. textTitle.setBackgroundDrawable(
  196. CourseTableActivity. this .getResources().getDrawable(R.drawable.title_text_bg));
  197. //显示弹出窗口
  198. showWeekListWindow(textTitle);
  199. }
  200. });
  201. //获取最大课程节数
  202. String maxCourseNumStr = courseSettings.getString( "maxCourseNum_" + UserInfo.currentUser.getStuNum(), "" );
  203. if (maxCourseNumStr.equals( "" ))
  204. {
  205. courseSettings.edit().putString( "maxCourseNum_" + UserInfo.currentUser.getStuNum(), "12" );
  206. maxCourseNum = 12 ;
  207. }
  208. else
  209. {
  210. maxCourseNum = Integer.parseInt(maxCourseNumStr);
  211. }
  212. DisplayMetrics dm = new DisplayMetrics();
  213. getWindowManager().getDefaultDisplay().getMetrics(dm);
  214. //屏幕高度
  215. int height = dm.heightPixels;
  216. gridHeight = height / maxCourseNum;
  217. //设置课表界面
  218. //动态生成12 * maxCourseNum个textview
  219. for ( int i = 1 ; i <= maxCourseNum; i ++){
  220. for ( int j = 1 ; j <= 8 ; j ++){
  221. TextView tx = new TextView(CourseTableActivity. this );
  222. tx.setId((i - 1 ) * 8 + j);
  223. //除了最后一列,都使用course_text_view_bg背景(最后一列没有右边框)
  224. if (j < 8 )
  225. tx.setBackgroundDrawable(CourseTableActivity. this .
  226. getResources().getDrawable(R.drawable.course_text_view_bg));
  227. else
  228. tx.setBackgroundDrawable(CourseTableActivity. this .
  229. getResources().getDrawable(R.drawable.course_table_last_colum));
  230. //相对布局参数
  231. RelativeLayout.LayoutParams rp = new RelativeLayout.LayoutParams(
  232. aveWidth * 33 / 32 + 1 ,
  233. gridHeight);
  234. //文字对齐方式
  235. tx.setGravity(Gravity.CENTER);
  236. //字体样式
  237. tx.setTextAppearance( this , R.style.courseTableText);
  238. //如果是第一列,需要设置课的序号(1 到 12)
  239. if (j == 1 )
  240. {
  241. tx.setText(String.valueOf(i));
  242. rp.width = aveWidth * 3 / 4 ;
  243. //设置他们的相对位置
  244. if (i == 1 )
  245. rp.addRule(RelativeLayout.BELOW, empty.getId());
  246. else
  247. rp.addRule(RelativeLayout.BELOW, (i - 1 ) * 8 );
  248. }
  249. else
  250. {
  251. rp.addRule(RelativeLayout.RIGHT_OF, (i - 1 ) * 8 + j - 1 );
  252. rp.addRule(RelativeLayout.ALIGN_TOP, (i - 1 ) * 8 + j - 1 );
  253. tx.setText( "" );
  254. }
  255. tx.setLayoutParams(rp);
  256. course_table_layout.addView(tx);
  257. }
  258. }
  259. pDialog = new ProgressDialog( this );
  260. pDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
  261. pDialog.setMessage( "正在加载信息。。。。" );
  262. pDialog.setIndeterminate( true );
  263. pDialog.setCanceledOnTouchOutside( false );
  264. pDialog.setCancelable( false );
  265. pDialog.show();
  266. //获取当前学期
  267. final String currentTerm = courseSettings.getString( "currentTerm_" + UserInfo.currentUser.getStuNum(), null );
  268. //查看这个学期的课程信息是否已经抓取过
  269. final boolean hasSetting = courseSettings.getBoolean( "Is_" + currentTerm + "_saved_" + UserInfo.currentUser.getStuNum(), false );
  270. /**
  271. 这里写你自己的获取课程信息的方法
  272. */
  273. new Thread() {
  274. @Override
  275. public void run() {
  276. CourseInfoDBManager dbManager = new CourseInfoDBManager(CourseTableActivity. this );
  277. //打开数据库
  278. dbManager.open();
  279. try {
  280. //入果还没抓取过该学期的课程信息,先抓取
  281. if (!hasSetting)
  282. {
  283. //抓取这个学期的课表信息
  284. List<CourseInfo> list = CourseInfoFetcher.fetchCourseInfo( "" , currentTerm, UserInfo.currentUser.getStuNum());
  285. //插入课程信息
  286. for (CourseInfo courseInfo : list)
  287. {
  288. dbManager.insertCourse(courseInfo, currentTerm);
  289. }
  290. //设置该学期的课程已经抓取过的标志
  291. Editor editor = courseSettings.edit();
  292. editor.putBoolean( "Is_" + currentTerm + "_saved_" + UserInfo.currentUser.getStuNum(), true );
  293. editor.commit();
  294. }
  295. //从数据库中读取课程信息,存放在courseInfoMap中,key为星期几,value是这一天的课程信息
  296. courseInfoMap  = dbManager.query(currentTerm);
  297. // 发送更新界面信息
  298. Message msg = new Message();
  299. if (courseInfoMap.isEmpty())
  300. {
  301. msg.what = - 2 ;
  302. courseInfoInitMessageHandler.sendMessage(msg);
  303. return ;
  304. }
  305. int currentWeek = - 1 ;
  306. if (!currentWeekStr.equals( "" ))
  307. {
  308. currentWeek = Integer.parseInt(currentWeekStr);
  309. }
  310. dbManager.close();
  311. InitMessageObj msgObj = new InitMessageObj(aveWidth, currentWeek, screenWidth, maxCourseNum);
  312. msg.obj = msgObj;
  313. courseInfoInitMessageHandler.sendMessage(msg);
  314. } catch (AppException e) {
  315. Message msg = new Message();
  316. msg.what = - 1 ;
  317. courseInfoInitMessageHandler.sendMessage(msg);
  318. Log.e( "courseInfo_fetch_exception" , e.toString());
  319. } finally {
  320. dbManager.close();
  321. }
  322. }
  323. }.start();
  324. }
  325. CourseInfoInitMessageHandler courseInfoInitMessageHandler = new CourseInfoInitMessageHandler( this );
  326. static class InitMessageObj{
  327. int aveWidth;
  328. int currentWeek;
  329. int screenWidth;
  330. int maxCourseNum;
  331. public InitMessageObj( int aveWidth, int currentWeek, int screenWidth, int maxCourseNum) {
  332. super ();
  333. this .aveWidth = aveWidth;
  334. this .currentWeek = currentWeek;
  335. this .screenWidth = screenWidth;
  336. this .maxCourseNum = maxCourseNum;
  337. }
  338. }
  339. //初始化课程表的messageHandler
  340. static class CourseInfoInitMessageHandler extends Handler{
  341. WeakReference<CourseTableActivity> mActivity;
  342. public CourseInfoInitMessageHandler(CourseTableActivity activity){
  343. mActivity = new WeakReference<CourseTableActivity>(activity);
  344. }
  345. @Override
  346. public void handleMessage(Message msg) {
  347. mActivity.get().pDialog.dismiss();
  348. //网络错误
  349. if (msg.what == - 1 )
  350. {
  351. Toast.makeText(mActivity.get(), "获取课程信息失败!请检查您的网络或者稍后再试" , Toast.LENGTH_SHORT).show();
  352. return ;
  353. }
  354. //没有课程信息
  355. if (msg.what == - 2 )
  356. {
  357. Toast.makeText(mActivity.get(), "教务管理系统中无该学期的课程信息···" , Toast.LENGTH_SHORT).show();
  358. return ;
  359. }
  360. //五种颜色的背景
  361. int [] background = {R.drawable.course_info_blue, R.drawable.course_info_green,
  362. R.drawable.course_info_red, R.drawable.course_info_red,
  363. R.drawable.course_info_yellow};
  364. //获取课程信息的map
  365. Map<String, List<CourseInfo>> courseInfoMap = mActivity.get().courseInfoMap;
  366. //一些传过来的参数
  367. final InitMessageObj msgObj = (InitMessageObj) msg.obj;
  368. //当前周数
  369. int currentWeek = msgObj.currentWeek;
  370. //最大课程节数
  371. int maxCourseNum = msgObj.maxCourseNum;
  372. for (Map.Entry<String, List<CourseInfo>> entry: courseInfoMap.entrySet())
  373. {
  374. //查找出最顶层的课程信息(顶层课程信息即显示在最上层的课程,最顶层的课程信息满足两个条件 1、当前周数在该课程的周数范围内 2、该课程的节数跨度最大
  375. CourseInfo upperCourse = null ;
  376. //list里保存的是一周内某 一天的课程
  377. final List<CourseInfo> list = new ArrayList<CourseInfo>(entry.getValue());
  378. //
  379. //按开始的时间(哪一节)进行排序
  380. Collections.sort(list, new Comparator<CourseInfo>(){
  381. @Override
  382. public int compare(CourseInfo arg0, CourseInfo arg1) {
  383. if (arg0.getBeginIndex() < arg1.getBeginIndex())
  384. return - 1 ;
  385. else
  386. return 1 ;
  387. }
  388. });
  389. int lastListSize;
  390. do {
  391. lastListSize = list.size();
  392. Iterator<CourseInfo> iter = list.iterator();
  393. //先查找出第一个在周数范围内的课
  394. while (iter.hasNext())
  395. {
  396. CourseInfo c = iter.next(); //
  397. if (((c.getBeginWeek() <= currentWeek && c.getEndWeek() >= currentWeek) || currentWeek == - 1 ) && c.getEndIndex() <= maxCourseNum)
  398. {
  399. //判断是单周还是双周的课
  400. if (c.getCourseType() == CourseInfo.ALL ||
  401. (c.getCourseType() == CourseInfo.EVEN && currentWeek % 2 == 0 ) ||
  402. (c.getCourseType() == CourseInfo.ODD && currentWeek % 2 != 0 ) )
  403. {
  404. //从list中移除该项,并设置这节课为顶层课
  405. iter.remove();
  406. upperCourse = c;
  407. break ;
  408. }
  409. }
  410. }
  411. if (upperCourse != null )
  412. {
  413. List<CourseInfo> courseInfoList = new ArrayList<CourseInfo>();
  414. courseInfoList.add(upperCourse);
  415. int index = 0 ;
  416. iter = list.iterator();
  417. //查找这一天有哪些课与刚刚查找出来的顶层课相交
  418. while (iter.hasNext())
  419. {
  420. CourseInfo c = iter.next();
  421. //先判断该课程与upperCourse是否相交,如果相交加入courseInfoList中
  422. if ((c.getBeginIndex() <= upperCourse.getBeginIndex()
  423. &&upperCourse.getBeginIndex() < c.getEndIndex())
  424. ||(upperCourse.getBeginIndex() <= c.getBeginIndex()
  425. && c.getBeginIndex() < upperCourse.getEndIndex()))
  426. {
  427. courseInfoList.add(c);
  428. iter.remove();
  429. //在判断哪个跨度大,跨度大的为顶层课程信息
  430. if ((c.getEndIndex() - c.getEndIndex()) > (upperCourse.getEndIndex() - upperCourse.getBeginIndex())
  431. && ((c.getBeginWeek() <= currentWeek && c.getEndWeek() >= currentWeek) || currentWeek == - 1 ))
  432. {
  433. upperCourse = c;
  434. index ++;
  435. }
  436. }
  437. }
  438. //记录顶层课程在courseInfoList中的索引位置
  439. final int upperCourseIndex = index;
  440. // 动态生成课程信息TextView
  441. TextView courseInfo = new TextView(mActivity.get());
  442. courseInfo.setId( 1000 + upperCourse.getDay() * 100 + upperCourse.getBeginIndex() * 10 + upperCourse.getId());
  443. int id = courseInfo.getId();
  444. mActivity.get().textviewCourseInfoMap.put(id, courseInfoList);
  445. courseInfo.setText(upperCourse.getCourseName() + "\n@" + upperCourse.getClassRoom());
  446. //该textview的高度根据其节数的跨度来设置
  447. RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(
  448. msgObj.aveWidth * 31 / 32 ,
  449. (mActivity.get().gridHeight - 5 ) * 2 + (upperCourse.getEndIndex() - upperCourse.getBeginIndex() - 1 ) * mActivity.get().gridHeight);
  450. //textview的位置由课程开始节数和上课的时间(day of week)确定
  451. rlp.topMargin = 5 + (upperCourse.getBeginIndex() - 1 ) * mActivity.get().gridHeight;
  452. rlp.leftMargin = 1 ;
  453. // 前面生成格子时的ID就是根据Day来设置的
  454. rlp.addRule(RelativeLayout.RIGHT_OF, upperCourse.getDay());
  455. //字体居中中
  456. courseInfo.setGravity(Gravity.CENTER);
  457. //选择一个颜色背景
  458. int colorIndex = ((upperCourse.getBeginIndex() - 1 ) * 8 + upperCourse.getDay()) % (background.length - 1 );
  459. courseInfo.setBackgroundResource(background[colorIndex]);
  460. courseInfo.setTextSize( 12 );
  461. courseInfo.setLayoutParams(rlp);
  462. courseInfo.setTextColor(Color.WHITE);
  463. //设置不透明度
  464. courseInfo.getBackground().setAlpha( 222 );
  465. // 设置监听事件
  466. courseInfo.setOnClickListener( new OnClickListener() {
  467. @Override
  468. public void onClick(View arg0) {
  469. Log.i( "text_view" , String.valueOf(arg0.getId()));
  470. Map<Integer, List<CourseInfo>> map = mActivity.get().textviewCourseInfoMap;
  471. final List<CourseInfo> tempList = map.get(arg0.getId());
  472. if (tempList.size() > 1 )
  473. {
  474. //如果有多个课程,则设置点击弹出gallery 3d 对话框
  475. LayoutInflater layoutInflater = (LayoutInflater) mActivity.get().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  476. View galleryView = layoutInflater.inflate(R.layout.course_info_gallery_layout, null );
  477. final Dialog coursePopupDialog = new AlertDialog.Builder(mActivity.get()).create();
  478. coursePopupDialog.setCanceledOnTouchOutside( true );
  479. coursePopupDialog.setCancelable( true );
  480. coursePopupDialog.show();
  481. WindowManager.LayoutParams params = coursePopupDialog.getWindow().getAttributes();
  482. params.width = LayoutParams.FILL_PARENT;
  483. coursePopupDialog.getWindow().setAttributes(params);
  484. CourseInfoAdapter adapter = new CourseInfoAdapter(mActivity.get(), tempList, msgObj.screenWidth, msgObj.currentWeek);
  485. CourseInfoGallery gallery = (CourseInfoGallery) galleryView.findViewById(R.id.course_info_gallery);
  486. gallery.setSpacing( 10 );
  487. gallery.setAdapter(adapter);
  488. gallery.setSelection(upperCourseIndex);
  489. gallery.setOnItemClickListener( new OnItemClickListener() {
  490. @Override
  491. public void onItemClick(
  492. AdapterView<?> arg0, View arg1,
  493. int arg2, long arg3) {
  494. CourseInfo courseInfo = tempList.get(arg2);
  495. Intent intent = new Intent();
  496. Bundle mBundle = new Bundle();
  497. mBundle.putSerializable( "courseInfo" , courseInfo);
  498. intent.putExtras(mBundle);
  499. intent.setClass(mActivity.get(), DetailCourseInfoActivity. class );
  500. mActivity.get().startActivity(intent);
  501. coursePopupDialog.dismiss();
  502. }
  503. });
  504. coursePopupDialog.setContentView(galleryView);
  505. }
  506. else
  507. {
  508. Intent intent = new Intent();
  509. Bundle mBundle = new Bundle();
  510. mBundle.putSerializable( "courseInfo" , tempList.get( 0 ));
  511. intent.putExtras(mBundle);
  512. intent.setClass(mActivity.get(), DetailCourseInfoActivity. class );
  513. mActivity.get().startActivity(intent);
  514. }
  515. }
  516. });
  517. mActivity.get().course_table_layout.addView(courseInfo);
  518. mActivity.get().courseTextViewList.add(courseInfo);
  519. upperCourse = null ;
  520. }
  521. } while (list.size() < lastListSize && list.size() != 0 );
  522. }
  523. super .handleMessage(msg);
  524. }
  525. }
  526. /**
  527. * 显示周数下拉列表悬浮窗
  528. * @param parent
  529. */
  530. private void showWeekListWindow(View parent){
  531. if (weekListWindow == null )
  532. {
  533. LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  534. //获取layout
  535. popupWindowLayout = layoutInflater.inflate(R.layout.week_list_layout, null );
  536. weekListView = (ListView) popupWindowLayout.findViewById(R.id.week_list_view_body);
  537. //禁用滚动条(貌似没用··)
  538. weekListView.setVerticalScrollBarEnabled( false );
  539. List<Map<String, Object>> weekList = new ArrayList<Map<String, Object>>();
  540. //默认25周
  541. for ( int i = 1 ; i <= 25 ; i ++)
  542. {
  543. Map<String, Object> rowData = new HashMap<String, Object>();
  544. rowData.put( "week_index" , "第" + i + "周" );
  545. weekList.add(rowData);
  546. }
  547. //设置listview的adpter
  548. SimpleAdapter listAdapter = new SimpleAdapter( this ,
  549. weekList, R.layout.week_list_item_layout,
  550. new String[]{ "week_index" },
  551. new int []{R.id.week_list_item});
  552. weekListView.setAdapter(listAdapter);
  553. weekListView.setOnItemClickListener( new OnItemClickListener() {
  554. @Override
  555. public void onItemClick(AdapterView<?> adpater, View arg1,
  556. int arg2, long arg3) {
  557. int index = 0 ;
  558. String indexStr = textTitle.getText().toString();
  559. indexStr = indexStr.replaceAll( "第" , "" ).replaceAll( "周" , "" );
  560. if (!indexStr.equals( "全部" ))
  561. index = Integer.parseInt(indexStr);
  562. textTitle.setText( "第" + (arg2 + 1 ) + "周" );
  563. weekListWindow.dismiss();
  564. if ((arg2 + 1 ) != index)
  565. {
  566. Log.i( "courseTableActivity" , "清空当前课程信息" );
  567. for (TextView tx : courseTextViewList)
  568. {
  569. course_table_layout.removeView(tx);
  570. }
  571. courseTextViewList.clear();
  572. //重新设置课程信息
  573. Message msg = new Message();
  574. InitMessageObj msgObj = new InitMessageObj(aveWidth, arg2 + 1 , screenWidth, maxCourseNum);
  575. msg.obj = msgObj;
  576. courseInfoInitMessageHandler.sendMessage(msg);
  577. }
  578. }
  579. });
  580. int width = textTitle.getWidth();
  581. //实例化一个popupwindow
  582. weekListWindow = new PopupWindow(popupWindowLayout, width + 100 , width + 120 );
  583. }
  584. weekListWindow.setFocusable( true );
  585. //设置点击外部可消失
  586. weekListWindow.setOutsideTouchable( true );
  587. weekListWindow.setBackgroundDrawable( new BitmapDrawable());
  588. //消失的时候恢复按钮的背景(消除"按下去"的样式)
  589. weekListWindow.setOnDismissListener( new OnDismissListener() {
  590. @Override
  591. public void onDismiss() {
  592. textTitle.setBackgroundDrawable( null );
  593. }
  594. });
  595. weekListWindow.showAsDropDown(parent, - 50 , 0 );
  596. }
  597. }
2、CourseInfo
  1. package nd.leiyi.crims.model;
  2. import java.io.Serializable;
  3. public class CourseInfo implements Serializable{
  4. /**
  5. *
  6. */
  7. private static final long serialVersionUID = 2074656067805712769L;
  8. /** id */
  9. private int id;
  10. /** 课程名称  */
  11. private String courseName;
  12. /** 上课教室 */
  13. private String classRoom;
  14. /** 老师  */
  15. private String teacher;
  16. /** 上课时间(哪一天)(周一--周日) */
  17. private int day;
  18. /** 上课时间(哪一节)开始(1--12) */
  19. private int beginIndex;
  20. /** 上课时间(哪一节)节数(1--12) */
  21. private int endIndex;
  22. /** 上课时间(哪一周) 开始 */
  23. private int beginWeek;
  24. /** 上课时间(哪一周) 结束 */
  25. private int endWeek;
  26. /** 课程类型(单周还是双周) **/
  27. private int courseType;
  28. public static final int ALL = 1 ;
  29. public static final int ODD = 2 ;
  30. public static final int EVEN = 3 ;
  31. public String getCourseName() {
  32. return courseName;
  33. }
  34. public void setCourseName(String courseName) {
  35. this .courseName = courseName;
  36. }
  37. public String getClassRoom() {
  38. return classRoom;
  39. }
  40. public void setClassRoom(String classRoom) {
  41. this .classRoom = classRoom;
  42. }
  43. public String getTeacher() {
  44. return teacher;
  45. }
  46. public void setTeacher(String teacher) {
  47. this .teacher = teacher;
  48. }
  49. public int getDay() {
  50. return day;
  51. }
  52. public void setDay( int day) {
  53. this .day = day;
  54. }
  55. public int getBeginIndex() {
  56. return beginIndex;
  57. }
  58. public void setBeginIndex( int beginIndex) {
  59. this .beginIndex = beginIndex;
  60. }
  61. public int getEndIndex() {
  62. return endIndex;
  63. }
  64. public void setEndIndex( int endIndex) {
  65. this .endIndex = endIndex;
  66. }
  67. public int getBeginWeek() {
  68. return beginWeek;
  69. }
  70. public void setBeginWeek( int beginWeek) {
  71. this .beginWeek = beginWeek;
  72. }
  73. public int getEndWeek() {
  74. return endWeek;
  75. }
  76. public void setEndWeek( int endWeek) {
  77. this .endWeek = endWeek;
  78. }
  79. public int getId() {
  80. return id;
  81. }
  82. public void setId( int id) {
  83. this .id = id;
  84. }
  85. public int getCourseType() {
  86. return courseType;
  87. }
  88. public void setCourseType( int courseType) {
  89. this .courseType = courseType;
  90. }
  91. }
3、CourseInfoGallery
  1. package nd.leiyi.crims.gallery3D;
  2. import android.content.Context;
  3. import android.graphics.Camera;
  4. import android.graphics.Matrix;
  5. import android.util.AttributeSet;
  6. import android.view.MotionEvent;
  7. import android.view.View;
  8. import android.view.animation.Transformation;
  9. import android.widget.Gallery;
  10. public class CourseInfoGallery extends Gallery {
  11. private Camera mCamera = new Camera();
  12. private int mMaxRotationAngle = 60 ;
  13. private int mMaxZoom = - 60 ;
  14. private int mCoveflowCenter;
  15. public CourseInfoGallery(Context context) {
  16. super (context);
  17. this .setStaticTransformationsEnabled( true );
  18. }
  19. public CourseInfoGallery(Context context, AttributeSet attrs) {
  20. super (context, attrs);
  21. this .setStaticTransformationsEnabled( true );
  22. }
  23. public CourseInfoGallery(Context context, AttributeSet attrs, int defStyle) {
  24. super (context, attrs, defStyle);
  25. this .setStaticTransformationsEnabled( true );
  26. }
  27. public int getMaxRotationAngle() {
  28. return mMaxRotationAngle;
  29. }
  30. public void setMaxRotationAngle( int maxRotationAngle) {
  31. mMaxRotationAngle = maxRotationAngle;
  32. }
  33. public int getMaxZoom() {
  34. return mMaxZoom;
  35. }
  36. public void setMaxZoom( int maxZoom) {
  37. mMaxZoom = maxZoom;
  38. }
  39. private int getCenterOfCoverflow() {
  40. return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2
  41. + getPaddingLeft();
  42. }
  43. private static int getCenterOfView(View view) {
  44. return view.getLeft() + view.getWidth() / 2 ;
  45. }
  46. protected boolean getChildStaticTransformation(View child, Transformation t) {
  47. final int childCenter = getCenterOfView(child);
  48. final int childWidth = child.getWidth();
  49. int rotationAngle = 0 ;
  50. t.clear();
  51. t.setTransformationType(Transformation.TYPE_MATRIX);
  52. if (childCenter == mCoveflowCenter) {
  53. transformImageBitmap(child, t, 0 );
  54. } else {
  55. rotationAngle = ( int ) ((( float ) (mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);
  56. if (Math.abs(rotationAngle) > mMaxRotationAngle) {
  57. rotationAngle = (rotationAngle < 0 ) ? -mMaxRotationAngle
  58. : mMaxRotationAngle;
  59. }
  60. transformImageBitmap(child, t, rotationAngle);
  61. }
  62. return true ;
  63. }
  64. protected void onSizeChanged( int w, int h, int oldw, int oldh) {
  65. mCoveflowCenter = getCenterOfCoverflow();
  66. super .onSizeChanged(w, h, oldw, oldh);
  67. }
  68. private void transformImageBitmap(View child, Transformation t,
  69. int rotationAngle) {
  70. mCamera.save();
  71. final Matrix imageMatrix = t.getMatrix();
  72. final int imageHeight = child.getLayoutParams().height;
  73. final int imageWidth = child.getLayoutParams().width;
  74. final int rotation = Math.abs(rotationAngle);
  75. mCamera.translate( 0 .0f, 0 .0f, 100 .0f);
  76. // As the angle of the view gets less, zoom in
  77. if (rotation < mMaxRotationAngle) {
  78. float zoomAmount = ( float ) (mMaxZoom + (rotation * 1.5 ));
  79. mCamera.translate( 0 .0f, 0 .0f, zoomAmount);
  80. }
  81. mCamera.rotateY(rotationAngle);
  82. mCamera.getMatrix(imageMatrix);
  83. imageMatrix.preTranslate(-(imageWidth / 2 ), -(imageHeight / 2 ));
  84. imageMatrix.postTranslate((imageWidth / 2 ), (imageHeight / 2 ));
  85. mCamera.restore();
  86. }
  87. @Override
  88. public boolean onInterceptTouchEvent(MotionEvent ev) {
  89. if (ev.getAction() == MotionEvent.ACTION_MOVE) {
  90. return true ;
  91. } else {
  92. return false ;
  93. }
  94. }
  95. }
4、CourseInfoAdapter
  1. package nd.leiyi.crims.adapter;
  2. import java.util.List;
  3. import nd.leiyi.crims.R;
  4. import nd.leiyi.crims.gallery3D.CourseInfoGallery;
  5. import nd.leiyi.crims.model.CourseInfo;
  6. import android.content.Context;
  7. import android.graphics.Color;
  8. import android.view.Gravity;
  9. import android.view.View;
  10. import android.view.ViewGroup;
  11. import android.widget.BaseAdapter;
  12. import android.widget.TextView;
  13. public class CourseInfoAdapter extends BaseAdapter {
  14. private Context context;
  15. private TextView[] courseTextViewList;
  16. private int screenWidth;
  17. private int currentWeek;
  18. public CourseInfoAdapter(Context context, List<CourseInfo> courseList, int width, int currentWeek) {
  19. super ();
  20. this .screenWidth = width;
  21. this .context = context;
  22. this .currentWeek = currentWeek;
  23. createGalleryWithCourseList(courseList);
  24. }
  25. private void createGalleryWithCourseList(List<CourseInfo> courseList){
  26. //五种颜色的背景
  27. int [] background = {R.drawable.course_info_blue, R.drawable.course_info_green,
  28. R.drawable.course_info_red, R.drawable.course_info_red,
  29. R.drawable.course_info_yellow};
  30. this .courseTextViewList = new TextView[courseList.size()];
  31. for ( int i = 0 ; i < courseList.size(); i ++)
  32. {
  33. final CourseInfo course = courseList.get(i);
  34. TextView textView = new TextView(context);
  35. textView.setText(course.getCourseName() + "@" + course.getClassRoom());
  36. textView.setLayoutParams( new CourseInfoGallery.LayoutParams((screenWidth / 6 ) * 3 , (screenWidth / 6 ) * 3 ));
  37. textView.setTextColor(Color.WHITE);
  38. textView.setGravity(Gravity.CENTER_VERTICAL);
  39. textView.setPadding( 10 , 0 , 0 , 0 );
  40. if (course.getBeginWeek() <= currentWeek && course.getEndWeek() >= currentWeek &&
  41. (course.getCourseType() == CourseInfo.ALL ||
  42. (course.getCourseType() == CourseInfo.EVEN && currentWeek % 2 == 0 ) ||
  43. (course.getCourseType() == CourseInfo.ODD && currentWeek % 2 != 0 )))
  44. {
  45. //选择一个颜色背景
  46. int colorIndex = ((course.getBeginIndex() - 1 ) * 8 + course.getDay()) % (background.length - 1 );
  47. textView.setBackgroundResource(background[colorIndex]);
  48. }
  49. else
  50. {
  51. textView.setBackgroundResource(R.drawable.course_info_light_grey);
  52. }
  53. textView.getBackground().setAlpha( 222 );
  54. //          textView.setOnClickListener(new OnClickListener() {
  55. //              @Override
  56. //              public void onClick(View arg0) {
  57. //                  // TODO Auto-generated method stub
  58. //                  Intent intent = new Intent();
  59. //                  Bundle mBundle = new Bundle();
  60. //                  mBundle.putSerializable("courseInfo", course);
  61. //                  intent.putExtras(mBundle);
  62. //                  intent.setClass(context, DetailCourseInfoActivity.class);
  63. //                  context.startActivity(intent);
  64. //              }
  65. //          });
  66. this .courseTextViewList[i] = textView;
  67. }
  68. }
  69. @Override
  70. public int getCount() {
  71. return courseTextViewList.length;
  72. }
  73. @Override
  74. public Object getItem( int index) {
  75. return courseTextViewList[index];
  76. }
  77. @Override
  78. public long getItemId( int arg0) {
  79. return arg0;
  80. }
  81. @Override
  82. public View getView( int position, View convertView, ViewGroup parent) {
  83. return courseTextViewList[position];
  84. }
  85. public float getScale( boolean focused, int offset) {
  86. return Math.max( 0 , 1 .0f / ( float ) Math.pow( 2 , Math.abs(offset)));
  87. }
  88. }
5、gallery-3d布局
  1. <? xml version = "1.0" encoding = "utf-8" ?>
  2. < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
  3. android:layout_width = "fill_parent"
  4. android:layout_height = "fill_parent"
  5. android:orientation = "vertical" >
  6. < nd.leiyi.crims.gallery3D.CourseInfoGallery
  7. android:id = "@+id/course_info_gallery"
  8. android:layout_width = "fill_parent"
  9. android:layout_height = "wrap_content"
  10. android:layout_centerInParent = "true"
  11. />
  12. </ LinearLayout >
6、gallery-3d-item
  1. <? xml version = "1.0" encoding = "utf-8" ?>
  2. < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
  3. android:layout_width = "match_parent"
  4. android:layout_height = "match_parent"
  5. android:orientation = "vertical" >
  6. < TextView
  7. android:id = "@+id/course_info_gallery_item"
  8. android:layout_width = "wrap_content"
  9. android:layout_height = "wrap_content"
  10. android:textColor = "#ffffff"
  11. android:gravity = "center_vertical" />
  12. </ LinearLayout >
7、course_text_view_bg (课程格子背景)
  1. <? xml version = "1.0" encoding = "UTF-8" ?>
  2. < layer-list xmlns:android = "http://schemas.android.com/apk/res/android" >
  3. < item >
  4. < shape >
  5. < solid android:color = "#FFFFFF" />
  6. < stroke
  7. android:width = "1dp"
  8. android:color = "#a8abad" />
  9. </ shape >
  10. </ item >
  11. < item
  12. android:right = "1dp"
  13. android:bottom = "1dp" >
  14. < shape >
  15. < solid android:color = "#FFFFFF" />
  16. < stroke
  17. android:width = "1dp"
  18. android:color = "#ffffff" />
  19. </ shape >
  20. </ item >
  21. </ layer-list >
8、course_table_last_colum(最后一列的背景,无边框)
  1. <? xml version = "1.0" encoding = "UTF-8" ?>
  2. < layer-list xmlns:android = "http://schemas.android.com/apk/res/android" >
  3. < item >
  4. < shape >
  5. < solid android:color = "#FFFFFF" />
  6. < stroke
  7. android:width = "1dp"
  8. android:color = "#a8abad" />
  9. </ shape >
  10. </ item >
  11. < item
  12. android:bottom = "1dp" >
  13. < shape >
  14. < solid android:color = "#FFFFFF" />
  15. < stroke
  16. android:width = "1dp"
  17. android:color = "#ffffff" />
  18. </ shape >
  19. </ item >
  20. </ layer-list >
还有一些布局文件就不贴了。代码太多了··,有兴趣的同学可以在github里下载我的工程···工程比较大,而且后台服务端程序我已经从云服务器上撤销了,所以跑不起来,我也不愿改代码了··

gitbub上的代码已删除

demo版: 超级课程表demo

超级课程表 》的 课表 界面 设计话不多说,直接看效果图下面我们来看《 超级课程表 界面 》这样的话,我们需要自定义三种视图:1.自定义 View方格背景 2.自定义 ViewGroup来装 课表 信息 3.自定义 TextView显示 课表 信息三者的关系是:自定义View提供背景,自定义ViewGroup提供装载 课表 视图 的容器,自定义TextView显示 课表 信息。首先我们先画背景!丛里往外进行设计1.自定
githu地址:https://github.com/longer96/CDTU 大一时针对我校开发的校园客户端,方便学生查 课表 、成绩、一卡通消费记录、失物招领等等。遵循MD设计原则,数据大多通过抓包获取,现开源(已屏蔽学校相关信息,怕被请回去喝茶)  Ps:第一次这么认真写文章,有不合理的地方希望大家提出 ...