那么如何制作出这样的表格呢?
详情参照:
如何制作表格(1)——TableLayout
如何制作表格(2)——GridLayout
如何作表格(3)——GridView+RecyclerView
首先:选择什么样控件来制作头部的格子:
大家一看发现只有一行的表格,但是第一列的大小和其他列的大小是不一样的。大家肯定马上加想到了用TableLayout和GridLayout来制作头部的表格,当然如果不嫌麻烦可以直接用TextView堆加成一行。由于GridView生成的格子大小一致,所以不可以使用。(Recycler是可以制作的,但是总感觉有点大材小用了)。
个人选择GridLayout来制作该头部。(当然小伙伴也可以使用其他的控件来进行头部的制作,看看哪个更方便)
其次:设置每个格子的高度,和宽度。由于第一行比较特殊还需要分离出来。
本人是获取屏幕大小,然后分成15分,第一行占1份,其他行占2分来确定。
最后:分割线的确定
本人选择了,使用<View>的方式来添加了竖直分割线,并利用drawable中的<shape>来设置边框。(当然这不是最简便,和最有效的方法)
最后的效果:
添加头部:
<GridLayout android:id="@+id/main_grid_title" android:layout_width="match_parent" android:layout_height="@dimen/table_row_height" //设置表格的高度为50 android:background="@drawable/table_frame" //背景 android:rowCount="1" android:columnCount="15"> </GridLayout>columCount = "15" :其中有7列用来填充分割线的(这种做法,分割线和表格内容没有分开,容易混乱,不建议使用。本人懒癌发作了,请见谅。
背景:
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="@color/transparent"/> //中间背景色为透明。 <stroke android:color="@color/blue" android:width="1dp"/> </shape>
利用代码填充表格内容:
//表格的内容 private static final String [] TITLE_DATA = {"9月","周一","周二","周三","周四","周五","周六","周日"}; private GridLayout mGlClsTitle; //屏幕宽度的1/15 private int mTableDistance; protected void onCreateView(Bundle savedInstanceState) { setContentView(R.layout.activity_main); mGlClsTitle = getViewById(R.id.main_grid_title); mTableDistance = getScreenPixelWidth()/15; } //设置表格的头部 private void setUpClsTitle(){ for (int i=0; i<TITLE_DATA.length; ++i){ String content = TITLE_DATA[i]; //设置LayoutParams GridLayout.LayoutParams params = new GridLayout.LayoutParams(); //第一列的时候 if (i == 0){ params.width = mTableDistance;//宽度为总宽度的1/15 } else { //添加分割线 View divider = getLayoutInflater().inflate(R.layout.grid_title_form,mGlClsTitle,false); mGlClsTitle.addView(divider); params.width = mTableDistance * 2; } params.height = GridLayout.LayoutParams.MATCH_PARENT; TextView textView = new TextView(this); textView.setTextColor(getResources().getColor(R.color.blue)); textView.setText(content); textView.setGravity(Gravity.CENTER); mGlClsTitle.addView(textView,params); } }
首先:选择使用哪种控件来制作表格:
<ScrollView android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:scrollbars="none"> <GridLayout android:id="@+id/main_grid_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:rowCount="13" android:columnCount="8"> </GridLayout> </ScrollView>
private static final int GRID_ROW_COUNT = 12; private static final int GRID_COL_COUNT = 8; <pre code_snippet_id="1909809" snippet_file_name="blog_20161001_5_6741136" name="code" class="java" style="font-size: 18px;">//初始化课表显示的格子
<span style="font-weight: normal;">//初始化表格的距离 for (int i=1; i<GRID_COL_COUNT; ++i){ int row = 0; int col = i; GridLayout.LayoutParams params = new GridLayout.LayoutParams( GridLayout.spec(row),GridLayout.spec(col) ); params.width = mTableDistance*2; params.height = (int) getResources().getDimension(R.dimen.table_row_height); View view = new View(this); mGlClsContent.addView(view,params); }</span>
<color name="blue">#378BE0</color> <color name="white">#fff</color> <color name="transparent">#00000000</color> <color name="light_blue">#9960BFE5</color> <color name="light_green">#9968CA5E</color> <color name="light_pink">#99F49C97</color> <color name="hole_blue">#9993AAE2</color>
public class Course { //星期几:周一到周日 private int day; //第几节课:总共12节 private int clsNum; //每节课的长度 private int clsCount; //随机的颜色 private int color; //课程名 private String clsName; public int getClsNum() { return clsNum; } public void setClsNum(int clsNum) { this.clsNum = clsNum; } public String getClsName() { return clsName; } public void setClsName(String clsName) { this.clsName = clsName; } public int getColor() { return color; } public void setColor(int color) { this.color = color; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } public int getClsCount() { return clsCount; } public void setClsCount(int clsCount) { this.clsCount = clsCount; } @Override public String toString() { return "StuClass{" + "clsCount=" + clsCount + ", day=" + day + ", clsNum=" + clsNum + ", color=" + color + ", clsName='" + clsName + '\'' + '}'; } }
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:radius="8dp"/> //弯曲四个角 </shape>
private void showCls(){ for (int i = 0; i< mStuCourseList.size(); ++i){ Course course = mStuCourseList.get(i); int row = course.getClsNum(); int col = course.getDay(); int size = course.getClsCount(); //设定View在表格的哪行那列 GridLayout.LayoutParams params = new GridLayout.LayoutParams( GridLayout.spec(row,size), GridLayout.spec(col) ); //设置View的宽高 params.width = mTableDistance*2; params.height = (int) getResources().getDimension(R.dimen.table_row_height) * size; params.setGravity(Gravity.FILL); //通过代码改变<Shape>的背景颜色 GradientDrawable drawable = (GradientDrawable) getResources().getDrawable(R.drawable.cls_bg); drawable.setColor(getResources().getColor(course.getColor())); //设置View TextView textView = new TextView(this); textView.setTextColor(getResources().getColor(R.color.white)); textView.setText(course.getClsName()); textView.setGravity(Gravity.CENTER); textView.setBackground(drawable); //添加到表格中 mGlClsContent.addView(textView,params); } }
RadioButtonList1:可能显示值为unable to decode value。该内容可以从网站的源码中找到(单击右键,查看网络源代码)
最后查询出来的结果是"学生"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp"> <EditText android:id="@+id/login_et_username" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:hint="请输入账号"/> <EditText android:id="@+id/login_et_pwd" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:hint="请输入密码" android:inputType="textPassword"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/login_et_codes" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_marginRight="20dp" android:hint="请输入验证码"/> <ImageView android:id="@+id/login_iv_codes_img" android:layout_width="90dp" android:layout_height="40dp" android:scaleType="fitXY"/> </LinearLayout> <Button android:id="@+id/login_btn_login" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorAccent" android:text="@string/login" android:textColor="@color/white"/> </LinearLayout>
public class HttpConnection { private static final long TIME_OUT = 10000; private OkHttpClient mClient; private static HttpConnection sConnection; private final Map<String,List<Cookie>> mCookieMap = new HashMap<>(); private HttpConnection(){ mClient = new OkHttpClient.Builder() .connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS) .readTimeout(TIME_OUT,TimeUnit.MILLISECONDS) .cookieJar(new MyCookieJar()) .build(); } public static HttpConnection getInstance(){ if (sConnection == null){ sConnection = new HttpConnection(); } return sConnection; } /*存储Cookie的类*/ /** * 由于网站不能够持久化登陆,就直接将Cookie值放在HashMap中了 */ class MyCookieJar implements CookieJar{ @Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) { mCookieMap.put(url.host(),cookies); } @Override public List<Cookie> loadForRequest(HttpUrl url) { List<Cookie> cookieList = mCookieMap.get(url.host()); return cookieList == null ? new ArrayList<Cookie>() : cookieList; } } public void saveCookie(HttpUrl url,List<Cookie> cookies){ //如果url相同则替换 mClient.cookieJar().saveFromResponse(url,cookies); } public List<Cookie> getCookies(HttpUrl url){ //未判断Http是否合法 return mClient.cookieJar().loadForRequest(url); } /*默认使用异步加载*/ public void connectUrl(Request request, Callback callback){ Call call = mClient.newCall(request); call.enqueue(callback); } public interface HttpCallBack <T>{ void callback(T data); } }(3)、创建URLManager,管理网址
/** * Created by PC on 2016/9/25. * 存储需要用到的网址 */ public class URLManager { //登陆的首页 public static final String URL_BASE = "http://202.115.80.153"; //登陆的验证码 public static final String URL_CODES = "http://202.115.80.153/CheckCode.aspx?"; //登陆的判定提交地址 public static final String URL_LOGIN = "http://202.115.80.153/default2.aspx"; //登陆之后的跳转页面 public static final String URL_REFERER = "http://202.115.80.153/xs_main.aspx?xh=XH"; //课程表 public static final String URL_CLS = "http://202.115.80.153/xskbcx.aspx?xh=XH"; }
/** * Created by PC on 2016/9/25. * 登陆到学校的教务网 * 需要设置为单例模式 */ public class LoginService { public static final String TAG = "LoginService"; private final Handler mHandler = new Handler(Looper.getMainLooper()); private Context mContext; //设置登陆的Post参数 private String rudioButton = "学生"; private String state = "dDwyODE2NTM0OTg7Oz7QBx05W486R++11e1KrLTLz5ET2Q=="; private String button1 = ""; private String language = ""; private String hidPdrs = ""; private String hidsc = ""; private HttpConnection mConnection = HttpConnection.getInstance(); public LoginService(Context context){ mContext = context; } //获取验证码 /** * 获取验证码的时候,会返回给客户端一个Cookie * 作用:获取验证码 * @param httpCallBack */ public <T extends Bitmap> void getCodesImg(final HttpConnection.HttpCallBack<T> httpCallBack) throws IOException{ URL codeUrl = new URL(URLManager.URL_CODES); final Request request = new Request.Builder() .url(codeUrl) .build(); Callback callback = new Callback() { @Override public void onFailure(Call call, IOException e) { Log.d(TAG,"出错"); } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful()){ //用流优化当收到的数据比较大 byte [] bytes = response.body().bytes(); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); final Bitmap bitmap = BitmapFactory.decodeStream(bais); //切换线程环境 changeEnvironment(httpCallBack,bitmap); response.close(); } } }; mConnection.connectUrl(request,callback); } public <T> void login(final String username, String pwd, final String codes, final HttpConnection.HttpCallBack<T> httpCallBack) throws IOException{ URL url = new URL(URLManager.URL_LOGIN); FormBody formBody = new FormBody.Builder() .add("__VIEWSTATE",state) .add("txtUserName",username) .add("TextBox2",pwd) .add("txtSecretCode",codes) .add("RadioButtonList1",rudioButton) .add("Button1",button1) .add("lbLanguage",language) .add("hidPdrs",hidPdrs) .add("hidsc",hidsc) .build(); final Request request = new Request.Builder() .url(url) .post(formBody) .build(); Callback callback = new Callback() { @Override public void onFailure(Call call, IOException e) { Log.d(TAG,"错误"); } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful()){ boolean isLogin = false; //判断是否登陆成功 注释① if(CourseParse. parseIsLoginSucceed(response.body().string())){ isLogin = true; } else { //显示登陆失败。 isLogin = false; } changeEnvironment(httpCallBack,isLogin); response.close(); } } }; mConnection.connectUrl(request,callback); } private <T> void changeEnvironment(final HttpConnection.HttpCallBack httpCallBack, final T data){ mHandler.post(new Runnable() { @Override public void run() { httpCallBack.callback(data); } }); } }
public static boolean parseIsLoginSucceed(String data){ Document doc = Jsoup.parse(data); //查看登陆文档。 Element element = doc.getElementById("xhxm"); if (element != null){ return true; } return false; }
public class CourseService { public static final String TAG = "CourseService"; private final Handler mHandler = new Handler(); private HttpConnection mConnection = HttpConnection.getInstance(); public <T extends List<Course>> void getCourse(String username, final HttpConnection.HttpCallBack<T> callBack) throws IOException { //因为xh为学号,需要自己设置 String urlCls = URLManager.URL_CLS.replace("XH",username); //重点:因为该网站实现的是动态跳转(有没有发现跳转到课表页面,但是网址没有变化) //必须添加Referer到Http头,Referer指的是当前页面的网址,否则会拒绝访问 String referer = URLManager.URL_REFERER.replace("XH",username); Log.d(TAG,urlCls); URL url = new URL(urlCls); Request.Builder builder = new Request.Builder(); builder.addHeader("Referer",referer); builder.url(url); final Request request = builder.build(); Callback callback = new Callback() { @Override public void onFailure(Call call, IOException e) { //网络较差环境下的问题 e.printStackTrace(); } @Override public void onResponse(Call call, Response response) throws IOException { //登陆到课表,并解析课程表的资源 if (response.isSuccessful()){ //其实这里可以使用InputStream进行优化的。以后再说吧 final String clsDocument = response.body().string(); mHandler.post(new Runnable() { @Override public void run() { T stuClassList = (T) CourseParse.parsePersonal(clsDocument); callBack.callback(stuClassList); } }); response.close(); } } }; mConnection.connectUrl(request,callback); } }(7)解析课表数据,并转化为Course(在CourseParase类中)
/*解析个人课表*/ public static List<Course> parsePersonal(String data){ List<Course> courses = new ArrayList<>(); Document doc = Jsoup.parse(data); //首先获取Table Element table = doc.getElementById("Table1"); //然后获取table中的td节点 Elements trs = table.select("tr"); //移除不需要的参数,这里表示移除前两个数值。 trs.remove(0); trs.remove(0); //遍历td节点 for (int i=0; i<trs.size(); ++i){ Element tr = trs.get(i); //获取tr下的td节点,要求 Elements tds = tr.select("td[align]"); //遍历td节点 for(int j=0; j<tds.size(); ++j){ Element td = tds.get(j); String str = td.text(); //如果数值为空则不计算。 if (str.length() != 1){ //解析文本数据 str = parsePersonalCourse(str); Course course = new Course(); course.setClsName(str); course.setDay(j+1); course.setClsCount(Integer.valueOf(td.attr("rowspan"))); course.setClsNum(i+1); Random random = new Random(); int num = random.nextInt(COLOR.length); course.setColor(COLOR[num]); courses.add(course); } } } return courses; } private static String parsePersonalCourse(String text){ //正则表达式获取课名,和教室 Pattern courseNamePattern = Pattern.compile("^.+?(\\s{1})"); Matcher courseNameMatcher = courseNamePattern.matcher(text); courseNameMatcher.find(); String str = courseNameMatcher.group(0); Pattern courseLocPattern = Pattern.compile("\\s{1}(\\d+)"); Matcher courseLocMatcher = courseLocPattern.matcher(text); courseLocMatcher.find(); String data = courseLocMatcher.group(0); return str+"@"+data; }好了,到这里,超级课程表就出炉了~~~~~吼吼