Android 上传PHP xUtils Bug修复分析过程

 更新时间:2016年10月2日 16:23  点击:1498
下面我们来看一篇在Android 上传PHP xUtils Bug修复分析过程,这个bug也是非常的好利用了,各位朋友可以和小编一起来看看的哦。

起因

作为全职PHPer偶尔需要客串下Androider,最近公司的一个项目需要Android的客户端(主要图片特效处理及其上传),自己就客串下Androider.

之前有过Android开发经验所以做这个挺顺手的,几乎所有东西直接github中拿过来改改就用,不过在处理图片上传的时候选择了xUtils这个

开源工具类,用起来确实比较好用,挺方便的,例如如下代码就可以实现上传:

RequestParams params = newRequestParams();
params.addBodyParameter("file", file);
HttpUtils httpUtils = newHttpUtils();
httpUtils.send(HttpRequest.HttpMethod.POST, UPLOAD_URL, params, newRequestCallBack<String>() {
@Override
//上传失败处理方法
publicvoidonFailure(HttpException arg0, String msg) {
 alert(msg);
 }
@Override
//上传进度处理
publicvoidonLoading(longtotal,longcurrent,
booleanisUploading) {
if(isUploading) {
 Log.i(LOG_NAME, "upload:"+ current +"/"+ total);
 }
 }
@Override
//上传成功处理
publicvoidonSuccess(ResponseInfo<String> responseInfo) {
 alert(responseInfo.result);
 Log.i(LOG_NAME, responseInfo.result);
 }
});
可以看到用起来比较方便,如果自己写还是比较麻烦的。不过最让人头疼的不是使用方法,而是作为接收端为PHP的话是接收不到上传的文件,最后经证实

不仅仅是PHP C# 也有问题, 网上搜素了下不少人都遇到问题不过没有解决方案,看来只能自己动手解决了

问题分析

既然要解决问题,那么就需要分析bug可能出现的地方,既然是HTTP上传那么我们得知道在PHP 在HTTP协议中文件是怎么处理上传的,直接在官方文档

就可以找到

PHP 能够接受任何来自符合 RFC-1867 标准的浏览器(包括 Netscape Navigator 3 及更高版本,打了补丁的 Microsoft Internet Explorer 3 或者更高版本)上传的文件。

这个是PHP官方文档中给出的解释, PHP在处理上传的时候遵循的是RFC-1867标准,那么我们接下来看看什么是RFC-1867。

这里我给出一个RFC-1867的说明文档地址 RFC-1867 说明 ,太长了就不放在这里了只拿核心重点内容过来看看:

# The client might send back the following data use POST method:

Content-type: multipart/form-data, boundary=AaB03x

--AaB03x
content-disposition: form-data; name="field1"

Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain

 ... contents of file1.txt ...
--AaB03x--
这里通俗点讲,RFC-1867通过HTTP协议传输特定的格式来实现上传文件,比如我们上传一个文件名字叫做hello.txt的文本到服务端,那么发起请求的HTTP格式应该就如下:

POST /up.phpHTTP/1.1
Host:creturn.com
Content-Length:294
Content-type:multipart/form-data, boundary=AaB03x

--AaB03x
content-disposition: form-data; name="file"; filename="hello.txt"
Content-Type: text/plain

hello
--AaB03x--
这里HTTP头部协议没有写完整只是为了说明问题写的格式。在这些描述信息中

第一个Content-type(http头部描述信息)值multipart/form-data 是告诉服务器此次发送的数据是上传文件数据, boundary是告诉服务器文件数据之间分割标示符是 AaB03x
在HTTP body数据中以—AaB03x 分割多个文件,每个分隔符下面的描述信息是作为上传文件的描述信息
发送结束后需要以分隔符加“—”符号进行标示
如果这三点没有问题那么就能正确上传,当然次实例中肯定不成功因为HTTP头部协议描述信息简短切不争取(比如长度)

解决过程

既然上面已经对问题进行分析了,同样也知道了只要发送过程是按照RFC-1867的标准进行发送那么至少PHP是能够接收到上传的文件,那么接下来我们要解决的就是如果判断

或者查看xUtils发送文件过程中是否遵循了RFC-1867标准

那么如何查看xUtils是否发送了正确的数据格式? 有两种方案,一个是利用代理工具抓包,另外一个方案就是直接抓包

这里就说说直接抓包,代理抓包可以google一大堆。

pc 上面建立无线热点分享给手机,这样所有的数据都通过电脑走,不会用pc分享热点的google一大堆,或者为了偷懒买个传说中的mini WIFI都行

抓包工具window推荐smartSniff, linux或者os x直接就tcpdump也行

先写个简单的app装到手机上这里给出上传代码:

String UPLOAD_URL = "http://www.creturn.com/up.php";
File file = newFile(Environment.getExternalStorageDirectory() ,"hello.txt");
RequestParams params = newRequestParams();
params.addBodyParameter("file", file);
HttpUtils httpUtils = newHttpUtils();
httpUtils.send(HttpRequest.HttpMethod.POST, UPLOAD_URL, params, newRequestCallBack<String>() {
@Override
//上传失败处理方法
publicvoidonFailure(HttpException arg0, String msg) {
 alert(msg);
 }
@Override
//上传进度处理
publicvoidonLoading(longtotal,longcurrent,
booleanisUploading) {
if(isUploading) {
 Log.i(LOG_NAME, "upload:"+ current +"/"+ total);
 }
 }
@Override
//上传成功处理
publicvoidonSuccess(ResponseInfo<String> responseInfo) {
 alert(responseInfo.result);
 Log.i(LOG_NAME, responseInfo.result);
 }
});
上面代码作用是把sdcard根目录的hello.txt文件上传到UPLOAD_URL, 所以在sdcard根目录放一个hello.txt文件里面内容随便写点

php服务端这边就直接打印上传的文件信息就行,代码很简单:

<?php
print_r($_FILES);
?>
如果上传成功就会反馈上传文件的信息,写好app装到手机连接好wifi然后在pc上面抓包,我这里用的是smart sniffer

抓包

打开smart sniffer
选择菜单 Options -> Capture Options
选择你分享wifi的网卡
确定点击开始抓包
在手机app上操作上传文件时候就可以看到抓包工具中已经有相应的http数据,抓包工具会对所有流量抓取所以如果有其他包干扰还可以

设置相应的过滤规则,这里就不阐述google就能找到

看看我们抓到的包内容:


图种可以看到我们上传的HTTP包信息,不过很明显反馈的信息提示是没有上传成功的。

那么接下来我们怎么去分析这个?怎么去排查问题?很多人应该能够想到,要是有个正确参照物不就很容易分析出问题出处?

那么我们在建立一个html文件用浏览器同样上传sdcard里面的hello.txt文件,html内容如下:

<html>
<body>

<formaction="http://www.creturn.com/up.php"method="post"enctype="multipart/form-data">
<labelfor="file">Filename:</label>
<inputtype="file"name="file"id="file"/>
<br/>
<inputtype="submit"name="submit"value="Submit"/>
</form>

</body>
</html>
用同样的方法抓包看看正确的包内容是什么样的:

POST /up.phpHTTP/1.1
Host:www.creturn.com
Connection:keep-alive
Content-Length:294
Cache-Control:max-age=0
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin:http://222.73.234.196
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryIexdOW8e2EZyciDK
Referer:http://222.73.234.196/up.html
DontTrackMeHere:gzip,deflate,sdch
Accept-Language:zh-CN,zh;q=0.8

------WebKitFormBoundaryIexdOW8e2EZyciDK
Content-Disposition: form-data; name="file"; filename="hello.txt"
Content-Type: text/plain

hello upload
------WebKitFormBoundaryIexdOW8e2EZyciDK
Content-Disposition: form-data; name="submit"

Submit
------WebKitFormBoundaryIexdOW8e2EZyciDK--
可以看到处理HTTP头部描述信息和包体里面多了一个Submit,几乎一样

之前说过上传过程中的几个重点,然后对比下我们发现Content-Type描述信息多了一个charset字符编码描述信息

那么要做测试的话肯定就要把不同的地方去掉,然后对包进行回放看看是否成功

包回放指的是包数据包重新发送一次

回放数据包有两种方法,一种直接修改xUtils源码重新上传。这里说一个简单的方法window自带的telnet ,用telnet 链接服务器80端口

手动发送数据

注意: windows7默认没有安装需要在控制面板->程序和功能->打开或者关闭windows功能中开启

如何进行手动发送?按照我们之前的想法去掉charset描述信息然后手动发送,那么先去掉charset信息后的包内容放入记事本:

POST /up.phpHTTP/1.1
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.73.11 (KHTML, like Gecko) Version/7.0.1 Safari/537.73.11
Content-Length:233
Content-Type:multipart/form-data; boundary=6wYgRevA02R_Uy4EJP31EcIJtsBlZtRv
Host:wwwcreturn.com
Connection:Keep-Alive
DontTrackMeHere:gzip

--6wYgRevA02R_Uy4EJP31EcIJtsBlZtRv
Content-Disposition: form-data; name="file"; filename="hello.txt"
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary

hello upload

--6wYgRevA02R_Uy4EJP31EcIJtsBlZtRv--
打开cmd 然后输入telnet www.creturn.com 80 然后黏贴进去看看效果


为了印证我们的才行可以把charset加上去和去掉的进行对比看看是不是加了之后就收不到上传文件的信息。

其实根据HTTP协议来讲理论上加不加charset应该不会影响上传,但结果这个问题确实是由于charset引起的。

接下来就简单了找到根源解决就行,在源码里面进行搜索 boundary ,找到地方根据作者写的方法注释掉其中添加charset的代码:

protectedStringgenerateContentType(
finalString boundary,
finalCharset charset) {
 StringBuilder buffer = newStringBuilder();
 buffer.append("multipart/"+ multipartSubtype +"; boundary=");
 buffer.append(boundary);
//这里就是需要注释掉的代码
/*if (charset != null) {
 buffer.append("; charset=");
 buffer.append(charset.name());
 }*/
returnbuffer.toString();
}

首行缩进在TextView中要如何来实现呢,TextView在安卓开发中用到非常的多了,在这里我们就一起来看看吧,希望文章能够对各位有帮助的哦。

在文字排版的时候经常要设置首行缩进,使用过word的都会知道,那么在Android中当需要设置首行缩进的时候该肿么办呢,我总结了两种方式:

第一种:傻瓜式,空格充当(8个空格占两个汉字的大小)。


textView.setText("        设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进");
第二种:转义字符。

textView.setText("\u3000\u3000" + "设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进设置首行缩进");
 
TextView属性

属性名称 描述
android:autoLink 设置是否当文本为URL链接/email/电话号码/map时,文本显示为可点击的链接。可选值(none/web/email/phone/map/all)
android:autoText 如果设置,将自动执行输入值的拼写纠正。此处无效果,在显示输入法并输入的时候起作用。
android:bufferType 指定getText()方式取得的文本类别。选项editable 类似于StringBuilder可追加字符,
也就是说getText后可调用append方法设置文本内容。spannable 则可在给定的字符区域使用样式,参见这里1、这里2。

android:capitalize 设置英文字母大写类型。此处无效果,需要弹出输入法才能看得到,参见EditView此属性说明。
android:cursorVisible 设定光标为显示/隐藏,默认显示。
android:digits 设置允许输入哪些字符。如“1234567890.+-*/%\n()”
android:drawableBottom 在text的下方输出一个drawable,如图片。如果指定一个颜色的话会把text的背景设为该颜色,并且同时和background使用时覆盖后者。
android:drawableLeft 在text的左边输出一个drawable,如图片。
android:drawablePadding 设置text与drawable(图片)的间隔,与drawableLeft、drawableRight、drawableTop、drawableBottom一起使用,可设置为负数,单独使用没有效果。
android:drawableRight 在text的右边输出一个drawable,如图片。
android:drawableTop 在text的正上方输出一个drawable,如图片。
android:editable 设置是否可编辑。这里无效果,参见EditView。
android:editorExtras 设置文本的额外的输入数据。在EditView再讨论。
android:ellipsize 设置当文字过长时,该控件该如何显示。有如下值设置:”start”—–省略号显示在开头;”end”——省略号显示在结尾;”middle”—-省略号显示在中间;”marquee” ——以跑马灯的方式显示(动画横向移动)

android:freezesText 设置保存文本的内容以及光标的位置。参见:这里。

android:gravity 设置文本位置,如设置成“center”,文本将居中显示。
android:hint Text为空时显示的文字提示信息,可通过textColorHint设置提示信息的颜色。此属性在EditView中使用,但是这里也可以用。
android:imeOptions 附加功能,设置右下角IME动作与编辑框相关的动作,如actionDone右下角将显示一个“完成”,而不设置默认是一个回车符号。这个在EditView中再详细说明,此处无用。
android:imeActionId 设置IME动作ID。在EditView再做说明,可以先看这篇帖子:这里。

android:imeActionLabel 设置IME动作标签。在EditView再做说明。
android:includeFontPadding 设置文本是否包含顶部和底部额外空白,默认为true。
android:inputMethod 为文本指定输入法,需要完全限定名(完整的包名)。例如:com.google.android.inputmethod.pinyin,但是这里报错找不到。
android:inputType 设置文本的类型,用于帮助输入法显示合适的键盘类型。在EditView中再详细说明,这里无效果。
android:marqueeRepeatLimit 在ellipsize指定marquee的情况下,设置重复滚动的次数,当设置为marquee_forever时表示无限次。
android:ems 设置TextView的宽度为N个字符的宽度。这里测试为一个汉字字符宽度,如图:
android:maxEms 设置TextView的宽度为最长为N个字符的宽度。与ems同时使用时覆盖ems选项。
android:minEms 设置TextView的宽度为最短为N个字符的宽度。与ems同时使用时覆盖ems选项。
android:maxLength 限制显示的文本长度,超出部分不显示。
android:lines 设置文本的行数,设置两行就显示两行,即使第二行没有数据。
android:maxLines 设置文本的最大显示行数,与width或者layout_width结合使用,超出部分自动换行,超出行数将不显示。
android:minLines 设置文本的最小行数,与lines类似。
android:linksClickable 设置链接是否点击连接,即使设置了autoLink。
android:lineSpacingExtra 设置行间距。
android:lineSpacingMultiplier 设置行间距的倍数。如”1.2”
android:numeric 如果被设置,该TextView有一个数字输入法。此处无用,设置后唯一效果是TextView有点击效果,此属性在EdtiView将详细说明。
android:password 以小点”.”显示文本
android:phoneNumber 设置为电话号码的输入方式。
android:privateImeOptions 设置输入法选项,此处无用,在EditText将进一步讨论。
android:scrollHorizontally 设置文本超出TextView的宽度的情况下,是否出现横拉条。
android:selectAllOnFocus 如果文本是可选择的,让他获取焦点而不是将光标移动为文本的开始位置或者末尾位置。TextView中设置后无效果。
android:shadowColor 指定文本阴影的颜色,需要与shadowRadius一起使用。效果: 
android:shadowDx 设置阴影横向坐标开始位置。
android:shadowDy 设置阴影纵向坐标开始位置。
android:shadowRadius 设置阴影的半径。设置为0.1就变成字体的颜色了,一般设置为3.0的效果比较好。
android:singleLine 设置单行显示。如果和layout_width一起使用,当文本不能全部显示时,后面用“…”来表示。如android:text="test_ singleLine " android:singleLine="true" android:layout_width="20dp"将只显示“t…”。如果不设置singleLine或者设置为false,文本将自动换行
android:text 设置显示文本.
android:textAppearance 设置文字外观。如“?android:attr/textAppearanceLargeInverse
”这里引用的是系统自带的一个外观,?表示系统是否有这种外观,否则使用默认的外观。可设置的值如下:textAppearanceButton/textAppearanceInverse/textAppearanceLarge/textAppearanceLargeInverse/textAppearanceMedium/textAppearanceMediumInverse/textAppearanceSmall/textAppearanceSmallInverse
android:textColor 设置文本颜色
android:textColorHighlight 被选中文字的底色,默认为蓝色
android:textColorHint 设置提示信息文字的颜色,默认为灰色。与hint一起使用。
android:textColorLink 文字链接的颜色.
android:textScaleX 设置文字之间间隔,默认为1.0f。分别设置0.5f/1.0f/1.5f/2.0f效果如下:
 
android:textSize 设置文字大小,推荐度量单位”sp”,如”15sp”
android:textStyle 设置字形[bold(粗体) 0, italic(斜体) 1, bolditalic(又粗又斜) 2] 可以设置一个或多个,用“|”隔开
android:typeface 设置文本字体,必须是以下常量值之一:normal 0, sans 1, serif 2, monospace(等宽字体) 3]
android:height 设置文本区域的高度,支持度量单位:px(像素)/dp/sp/in/mm(毫米)
android:maxHeight 设置文本区域的最大高度
android:minHeight 设置文本区域的最小高度
android:width 设置文本区域的宽度,支持度量单位:px(像素)/dp/sp/in/mm(毫米),与layout_width的区别看这里。

android:maxWidth 设置文本区域的最大宽度
android:minWidth 设置文本区域的最小宽度

 

ListView在android中用到的是很多了但ListView嵌套GridView就出问题了,今天我们就一起来看一篇关于ListView嵌套GridView问题详解吧。

开发中,遇到一个问题,是ListView嵌套GridView,需要点击整个ListView的Item进行跳转。但是在点击GridView区域时无法进行页面的跳转。这是因为GridView获得了焦点。导致点击无法跳转。

解决方法就是:

1.在Item最外层加上

android:descendantFocusability="blocksDescendants"

2.在Adapter中添加


holder.mGridView.setClickable(false);
holder.mGridView.setPressed(false);
holder.mGridView.setEnables(false);

这样就解决了.

下面我们来看一篇关于Android自定义布局的日历的例子,希望这个例子能够帮助到各位朋友,有兴趣的同学可以进来看看吧。


系统自带的日历虽然好用,但是不符合某些项目的需求,所以需要自定义布局,看网上许多自定义View完成自定义日历的,

自认水平不够,无法驾驭,所以采用gridview来实现。


CalendarUtils:(获取当前年月对应的数据数组)

package kyle.com.zujian;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
 
/**
 * Created by kyle on 2016/8/12 0012.
 * 获取某年某月的日期,界面上第一天为周日
 * 通过传的年和月,获取对应年月的日期和第一天是周几
 */
public class CalendarUtils {
    private static int FIRST_DAY_OF_WEEK;//第一天
    private static Calendar calendar;//日历
 
 
    public static List<DateEntity> getDate(int year, int month) {
        if (year <= 0 || month < 1 || month > 12) {
            return new ArrayList<>();//当年和月不符合规则时,返回空list
        }
        List<DateEntity> dates = new ArrayList<>();
        calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);//设置年
        calendar.set(Calendar.MONTH, month - 1);//设置月
        calendar.set(Calendar.DAY_OF_MONTH, 1);//设置第一天
        FIRST_DAY_OF_WEEK = calendar.get(Calendar.DAY_OF_WEEK) - 1;//获得第一天是周几 0:周日 6周六
        for (int i = 0; i < FIRST_DAY_OF_WEEK; i++) {//假设是周日则跳过,假设不是周日,就添加对应数目的空白item
            DateEntity entity = new DateEntity();
            dates.add(entity);
        }
        int count = checkDays(year, month);//获取当前年月的天数
        for (int i = 0; i < count; i++) {
            DateEntity entity2 = new DateEntity(year, month, i + 1);
            dates.add(entity2);
        }
        return dates;
    }
 
    /***
     * 判断当前年月有多少天
     *
     * @param year
     * @param month
     * @return
     */
    private static int checkDays(int year, int month) {
        if (year < 1 || month > 12 || month < 1) {
            return 0;
        }
        switch (month) {
            case 2:
                if (year % 4 == 0) {
                    return 29;
                } else {
                    return 28;
                }
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                return 31;
            case 4:
            case 6:
            case 9:
            case 11:
                return 30;
        }
        return 0;
    }
}

实体类:DateEntity

package kyle.com.zujian;
 
/**
 * Created by Administrator on 2016/8/9 0009.
 */
public class DateEntity {
    private int year;
    private int month;
    private int day;
 
    @Override
    public String toString() {
        return "DateEntity{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
 
    public DateEntity(){
 
}
    public DateEntity( int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
 
 
    public int getYear() {
        return year;
    }
 
    public void setYear(int year) {
        this.year = year;
    }
 
    public int getMonth() {
        return month;
    }
 
    public void setMonth(int month) {
        this.month = month;
    }
 
    public int getDay() {
        return day;
    }
 
    public void setDay(int day) {
        this.day = day;
    }
}

CalendarGridView:(自定义的gridview,把代码转移到这里面,方便调用.)

package kyle.com.zujian;
 
import android.content.Context;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
 
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
 
/**
 * Created by kyle on 2016/8/15 0015.
 * 自定义日历gridview
 */
public class CalendarGridView extends GridView {
 
    private Context mContext;
    private List<DateEntity> mList = new ArrayList<>();
    private MyAdapter mAdapter;
    private Calendar calendar;
    /***
     * 今天
     */
    private int NOW_YEAR;//当前年
    private int NOW_MONTH;//当前月
    private int NOW_DAY;//当前日
 
    public CalendarGridView(Context context) {
        super(context, null);
        mContext = context;
    }
 
    public CalendarGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        calendar = Calendar.getInstance();//获取日历实例
        NOW_YEAR = calendar.get(Calendar.YEAR);//当前年
        NOW_MONTH = calendar.get(Calendar.MONTH) + 1;//当前月
        NOW_DAY = calendar.get(Calendar.DAY_OF_MONTH);//当前日
        mAdapter = new MyAdapter();//实例化和设置适配器
        setAdapter(mAdapter);
    }
 
    public void init(int year, int month) {
        if (year < 1 || month < 1 || month > 12) {
            Toast.makeText(mContext, "年份或月份错误!", Toast.LENGTH_SHORT).show();
            return;
        }
        mList.clear();
        for (DateEntity entity : CalendarUtils.getDate(year, month)) {
            mList.add(entity);
        }
        mAdapter.notifyDataSetChanged();
    }
 
    private class MyAdapter extends BaseAdapter {
 
        @Override
        public int getCount() {
            return mList.size();
        }
 
        @Override
        public Object getItem(int position) {
            return mList.get(position);
        }
 
        @Override
        public long getItemId(int position) {
            return position;
        }
 
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            MyHolder holder;
            if (convertView == null) {
                convertView = View.inflate(mContext, R.layout.item_grid, null);
                holder = new MyHolder();
                holder.llMain = (LinearLayout) convertView.findViewById(R.id.ll_main);
                holder.tvDate = (TextView) convertView.findViewById(R.id.tv_date);
                holder.tvCount = (TextView) convertView.findViewById(R.id.tv_count);
                convertView.setTag(holder);
            } else {
                holder = (MyHolder) convertView.getTag();
            }
            int year = mList.get(position).getYear();
            int month = mList.get(position).getMonth();
            int day = mList.get(position).getDay();
            if (year == NOW_YEAR && month == NOW_MONTH && day == NOW_DAY) {
                holder.llMain.setBackgroundResource(R.color.red);
                holder.tvDate.setTextColor(ContextCompat.getColor(mContext, R.color.white));
                holder.tvCount.setTextColor(ContextCompat.getColor(mContext, R.color.white));
            } else {
                holder.llMain.setBackgroundResource(R.color.white);
                holder.tvDate.setTextColor(ContextCompat.getColor(mContext, R.color.black));
                holder.tvCount.setTextColor(ContextCompat.getColor(mContext, R.color.black));
            }
            if (day != 0) {
                holder.tvDate.setText(mList.get(position).getDay() + "");
            } else {
                holder.tvDate.setText("");
            }
            return convertView;
        }
    }
 
    private class MyHolder {
        private LinearLayout llMain;
        private TextView tvDate;
        private TextView tvCount;
    }
}

在Activity里面这样调用:

CalendarGridView mGv= (CalendarGridView) findViewById(R.id.gv);
mGv.init(2016,8);
 

当然,布局和实体类都可以自己去根据需求写,而如果想要真正的日历功能,则可以使用viewpager+fragment实现了

[!--infotagslink--]

相关文章

  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • php中Multipart/form-data漏洞补丁修复

    Multipart/form-data是文件上传或数据提交时会用到了,在php中Multipart/form-data是有安全bug的,下面我们来看看如何修复Multipart/form-data的bug吧. 今天在乌云...2016-11-25
  • Php文件上传类class.upload.php用法示例

    本文章来人大家介绍一个php文件上传类的使用方法,期望此实例对各位php入门者会有不小帮助哦。 简介 Class.upload.php是用于管理上传文件的php文件上传类, 它可以帮...2016-11-25
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • PHP文件上传一些小收获

    又码了一个周末的代码,这次在做一些关于文件上传的东西。(PHP UPLOAD)小有收获项目是一个BT种子列表,用户有权限上传自己的种子,然后配合BT TRACK服务器把种子的信息写出来...2016-11-25
  • jQuery实现简单的文件上传进度条效果

    本文实例讲述了jQuery实现文件上传进度条效果的代码。分享给大家供大家参考。具体如下: 运行效果截图如下:具体代码如下:<!DOCTYPE html><html><head><meta charset="utf-8"><title>upload</title><link rel="stylesheet...2015-11-24
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • php文件上传你必须知道的几点

    本篇文章主要说明的是与php文件上传的相关配置的知识点。PHP文件上传功能配置主要涉及php.ini配置文件中的upload_tmp_dir、upload_max_filesize、post_max_size等选项,下面一一说明。打开php.ini配置文件找到File Upl...2015-10-21
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • js实现上传图片及时预览

    这篇文章主要为大家详细介绍了js实现上传图片及时预览的相关资料,具有一定的参考价值,感兴趣的朋友可以参考一下...2016-05-09
  • EXCEL数据上传到SQL SERVER中的简单实现方法

    EXCEL数据上传到SQL SERVER中的方法需要注意到三点!注意点一:要把EXCEL数据上传到SQL SERVER中必须提前把EXCEL传到服务器上.做法: 在ASP.NET环境中,添加一个FileUpload上传控件后台代码的E.X: 复制代码 代码如下: if...2013-09-23
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • Android设置TextView竖着显示实例

    TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
  • DWVA上传漏洞挖掘的测试例子

    DVWA (Dam Vulnerable Web Application)DVWA是用PHP+Mysql编写的一套用于常规WEB漏洞教学和检测的WEB脆弱性测试程序。包含了SQL注入、XSS、盲注等常见的一些安全漏洞...2016-11-25
  • android.os.BinderProxy cannot be cast to com解决办法

    本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20
  • PHP swfupload图片上传的实例代码

    PHP代码如下:复制代码 代码如下:if (isset($_FILES["Filedata"]) || !is_uploaded_file($_FILES["Filedata"]["tmp_name"]) || $_FILES["Filedata"]["error"] != 0) { $upload_file = $_FILES['Filedata']; $fil...2013-10-04