超级课程表-java转向android

/ 2评 / 0

写在文章开始之前

老规矩,介绍之前先上图片。

timetable

可以看到,效果还是不错的哈,因为java写的代码可以直接放到Android上运行,所以我在android端只是做了一下UI上的数据展示而已,真正的获取课表的代码都是我在前面两篇博客里面扒拉出来的。所以只想了解原理的只需要看看这两篇博客而已。

Java登录方正教务系统

Java登录方正教务管理系统并获取课表

Android登录界面

登录界面的布局参考了一下这个博客使用的是相对布局,因为相对布局一个View可以覆盖在另一个View上面,就像图片中的用户名和密码框,最左边的文字就是一个TextView然后下面覆盖了一个EditView,然后设置一下EditView的左内边距就可实现这个效果了,至于EditText的样式,使用的是shape.xml文件,定义一个shape样式。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.jay.loginmyschool.MainActivity$PlaceholderFragment" >

    <ImageView
        android:id="@+id/img"
        android:layout_width="90dp"
        android:layout_height="90dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="35sp"
        android:src="@drawable/ic_launcher" />

    <EditText
        android:id="@+id/id"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_below="@id/img"
        android:layout_marginLeft="50dp"
        android:layout_marginRight="50dp"
        android:layout_marginTop="15dp"
        android:background="@drawable/layout_username"
        android:inputType="number"
        android:maxLength="8"
        android:paddingLeft="50dip"
        android:singleLine="true" />

    <TextView
        android:id="@+id/tip_id"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_alignLeft="@id/id"
        android:layout_alignTop="@id/id"
        android:layout_marginLeft="5dp"
        android:gravity="center_vertical"
        android:text="@string/user_name"
        android:textColor="#999999"
        android:textSize="20sp" />

    <EditText
        android:id="@+id/password"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_below="@id/id"
        android:layout_marginLeft="50dp"
        android:layout_marginRight="50dp"
        android:background="@drawable/layout_password"
        android:digits="@string/digits"
        android:inputType="textPassword"
        android:maxLength="16"
        android:paddingLeft="50dip" />

    <TextView
        android:id="@+id/tip_password"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_alignLeft="@id/password"
        android:layout_alignTop="@id/password"
        android:layout_marginLeft="5dp"
        android:gravity="center_vertical"
        android:text="@string/password"
        android:textColor="#999999"
        android:textSize="20sp" />

    <LinearLayout
        android:id="@+id/radiobutton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@id/password"
        android:layout_below="@id/password"
        android:layout_marginTop="5dp"
        android:orientation="horizontal" >

        <CheckBox
            android:id="@+id/saveme"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/save_me" />

        <CheckBox
            android:id="@+id/login_self"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/login_self" />
    </LinearLayout>

    <Button
        android:id="@+id/login"
        android:layout_width="match_parent"
        android:layout_height="42sp"
        android:layout_below="@id/radiobutton"
        android:layout_marginLeft="50dp"
        android:layout_marginRight="50dp"
        android:layout_marginTop="15dp"
        android:background="@drawable/selector_login"
        android:text="@string/login"
        android:textColor="#ffffff"
        android:textSize="20sp" />

</RelativeLayout>

详细的可以查看源码里面的login.xml文件。

Android的登陆操作

由于Android中不允许将耗时操作放在主线程里面,所以登陆以及解析网页的操作都是开启新线程的,由于中间会要求用户输入验证码,所以将获取课表分为两个部分,一个是获取验证码,获取验证码以后等待用户输入,输入完成以后在启动一个线程获取课表,这两个操作分别对应GetBitmap.java和GetTimeTable.java这两个文件,代码大部分都是在前面两篇博客里面复制过来的,就不在赘述了。

至于线程与主线程的通信采用的是handler,在android中,为了防止内存泄露,谷歌推荐我们将handler定义为静态,这里我定义了一个handler,用来传递消息,每一次启动新线程就将这个handler传递过去,然后新线程再通过这个handler通知主线程执行操作。

private static class MyHandler extends Handler {
	private WeakReference<MainActivity> ac = null;

	public MyHandler(MainActivity ac) {
		this.ac = new WeakReference<MainActivity>(ac);
	}

	@Override
	public void handleMessage(Message msg) {
		super.handleMessage(msg);
		MainActivity ac = this.ac.get();
		switch (msg.what) {
		case GetBitmap.OK: {
			ac.DismissProgressDlg();
			ac.getLoginButton().setEnabled(true);
			ac.setLoginDate((LoginBean) msg.getData().getSerializable(
					"logindate"));
			ac.showInputCheckDlg((Bitmap) msg.obj);
		}
			break;
		case GetBitmap.ERROR: {
			ac.DismissProgressDlg();
			ac.getLoginButton().setEnabled(true);
			Toast.makeText(ac, "获取验证码失败!", Toast.LENGTH_SHORT).show();
		}
			break;
		case GetTimeTable.LOGIN_ERROR: {
			Bundle data = msg.getData();
			Toast.makeText(ac, data.getString("error"), Toast.LENGTH_SHORT)
					.show();
			ac.DismissProgressDlg();
		}
			break;
		case GetTimeTable.LOGIN_OK: {
			// Bundle data = msg.getData();
			// Toast.makeText(ac, "登录成功", Toast.LENGTH_SHORT).show();
			@SuppressWarnings("unchecked")
			ArrayList<CourseBean> timeTable = (ArrayList<CourseBean>) msg.obj;
			ac.DismissProgressDlg();
			Intent intent = new Intent(ac, ShowTimeTable.class);
			intent.putExtra("KB", timeTable);
			ac.startActivity(intent);
			ac.finish();
		}
			break;
		}
	}
}

AlertDialog的使用

在登录的过程中,由于网络操作是耗时的,所以我们应该给与用户提示,在图片中可以看到,一旦开始网络请求操作,就会弹出一个等待对话框,这里的等待框使用的是自定义布局的AlertDialog,包括验证码的输入框也是采用的AlertDialog,关于自定义AlertDialog布局,百度已经讲得超级清楚了,如果不想百度,请点这里如果有错误与遗漏,欢迎大家指正

两个AlertDialog的布局也是很简单的,等待对话框是一个ProgressBar和一个TextView,验证码输入框是一个EditView加上一个ImageView和Button,大家可以参看源码里面的布局,这里就不在贴出了。

课表的解析

不同学校的课表页面的源码是不同的,不过只要会使用正则表达式,然后耐心点,应该可以搞定。关于正则表达式主要是Pattern类和Matcher类,详细用法可以参考API或者网上其他博客。

/**
 * 解析含有课表的网页
 * 
 * @param html
 */
private List<CourseBean> resolveKB(String html) {
	Pattern pattern1 = Pattern.compile("width=\"7%\">.*?/td>");
	Matcher matcher1 = pattern1.matcher(html);
	StringBuffer sb = new StringBuffer();
	int begin = 0;
	int end = 0;
	while (matcher1.find()) {
		begin = matcher1.start();
		end = matcher1.end();
		String str = html.substring(begin + 11, end);
		sb.append(str);
	}

	pattern1 = Pattern.compile("rowspan=\"2\">.*?/td>");
	matcher1 = pattern1.matcher(html);

	while (matcher1.find()) {
		begin = matcher1.start();
		end = matcher1.end();
		String str = html.substring(begin + 12, end);
		sb.append(str);
	}

	String str = sb.toString().replaceAll("<font.*?</font>", "");

	String[] str1 = str.split("</td>");
	List<CourseBean> course = new ArrayList<CourseBean>();
	for (String s : str1) {
		String[] k = s.split("(<br>)+");

		for (int i = 0; i < k.length && k.length > 1;) {
			String course_name = k[i++];
			String course_type = k[i++];
			String course_week = k[i++];

			String course_teacher = k[i++];
			String course_address = k[i++];

			CourseBean c = new CourseBean(course_name, course_address,
					course_teacher, course_type, course_week);
			course.add(c);
		}
	}
	return course;
}

课表的展示

课表的展示使用的是ViewPager,这是supportV4提供的一个控件,一个ViewPager里面包含了7个ListView,在登录成功以后,登录界面会通过intent将解析出来并格式化好的课表发送给课表展示页面,然后由ViewPager负责显示。下面贴出的代码是获取包含指定星期几的ListView,ListViewAdapter 为自己继承自BasedAdapter的适配器类,大家可以参看随后贴出的源码。

/*
 * 根据星期几来将所有课程分开,然后节数排序,创建好一个View, 然后返回给ViewPager
 * 
 * 课程时间格式 很明显,按照前面第二个字符可以判断星期几,按照第一个数字可以判断节数大小
 * 
 * 周五第1,2节{第8-9周}]
 */
private View getViewByDate(char date) {
	ListView list = new ListView(this);
	// 将data中所有课程信息分类
	ArrayList<CourseBean> list_data = new ArrayList<CourseBean>();

	for (int i = 0; i < data.size(); i++) {
		CourseBean course = data.get(i);
		Log.v("x", course.getWeek() + "");
		if(course.getWeek() == date){
			list_data.add(course);
		}
	}

	ListViewAdapter listViewAdapter = new ListViewAdapter(this, list_data);
	// 设置适配器
	list.setAdapter(listViewAdapter);
	// 设置点击时间监听
	list.setOnItemClickListener(new ListViewItemClickListener());
	return list;
}

点击课表项弹出课程详细信息

这个也是使用的AlertDialog,布局也很简单,一个纵向的ListView,里面包含了5个TextView,TextView左边的图片是使用的下面两个属性,分别是设置TextView左边的图片,以及图片与文字的间距。

android:drawableLeft="@drawable/name"

android:drawablePadding="5dp"

最后再说一句

由于涉及到的知识点太多,而且本人水平有限,只能介绍大概思路,具体的实现可以参看源码,也许其中有很多遗漏甚至错误,希望大家指出。

源码下载:360云盘 访问密码 74d5

  1. 博主,360云盘挂了,能用其他方式分享一下吗?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注