Python:分页工具

简述

ps[2016.2.13]:更新一个问题,下面的源代码有误,具体往下看.
ps[2016.2.27]:将原来的函数组成一个类,完全兼容原来的使用方式.


# 错误代码*****************************
'''
htmls = htmls.append(
liahref % (
currentpage, url, lenindex, ipagesize, currentpage,
lenindex) if lenindex == noformatpage else liahref % (
pageclass,
url, lenindex, ipagesize, pageclass, lenindex))
'''
# 错误代码***********************************************************

# 正确代码*****************************
htmls.append(
liahref % (
currentpage, url, lenindex, ipagesize, currentpage,
lenindex) if lenindex == noformatpage else liahref % (
pageclass,
url, lenindex, ipagesize, pageclass, lenindex))
# 正确代码***********************************************************

这是一个伪代码,因分页需要数据库配合,所以这里只给出分页的代码.

分页效果图
image-2402

python Logo
image-2403

源码

代码里面有很详细的注释,不在过于解释了.

v1.0[2016.2.27]:


"""

分页程序. 2016.2.27 add
"""
class Pager(object):
"""
生成代码示例[使用了妹子UI的分页.参照:http://amazeui.org/widgets/pagination]:
page: 当前页数,从1开始.
pagesize: 每页里面多少数据,一般为10.
rowscount: 查询数据库中的总数(一共有多少条数据,不能添加查询条件,直接count即可).
url: 访问地址[注意:这里没有处理url本身带有参数的问题],这里自动添加了page和pagesize参数(!!丢弃了原来的所有参数,若有的话)
countshow=Flase:默认不显示(总XXX条,共XX页,当前X页),如需开启,请传递True.
"""

def repage(self, page, pagesize, rowscount, url,countshow=False):
# 页码
ipage = int(page)
# 原始page ,数值不会被改变
noformatpage = int(page) # 原始page
# 页码大小
ipagesize = int(pagesize)
# 总数量
irowscount = int(rowscount)
# 进行数值限定
if ipage <= 1: ipage = 1 # 进行分页查询时,需要跳过的数据. # 这个ipage和noformatpage是有区别的!!! # 例如,当前是第二页,那么下面这个ipage是10,而noformatpage是不变的2. ipage = (ipage - 1) * ipagesize # 给当前页添加一个css标记 currentpage = "am-active" # 当前 # 不添加标记 pageclass = "" # 正常 # 空包标记,用于替换. black = "" ''' 格式化数据. 主要作用: 大于/小于当前页5个的自动隐藏. 比如: 当前是第6页,那么只显示1-11页,并且第6页在中间. 示例如下: 1,2,3,4,5,[6],7,8,9,10,11 第8页的时候: 3,4,5,6,7,[8],9,10,11,12,13 以此类推... ''' def formatstyle(item, noformatpage): # 标记最大隐藏的.可配置. maxhidden = noformatpage + 5 # 标记最小隐藏的.可配置 minhidden = noformatpage - 5 # 为当前页添加css标记. if item == noformatpage: return liahref % (currentpage, black, url, item, ipagesize, currentpage, item) elif (minhidden) <= item <= (maxhidden): # 这个地方是控制显示的,只有符合当前页+5或者-5之内的才显示. return liahref % ( pageclass, black, url, item, ipagesize, pageclass, item) else: # 不满足条件的全都隐藏. return liahref % ( pageclass, hiddenstyle, url, item, ipagesize, pageclass, item) # 隐藏数据. hiddenstyle = "display:none;" ''' %1: 添加各种class,在这里就是:标记当前页. %2: 设置隐藏/显示(通过css控制) %3: url地址,不带参数的url地址. %4: 当前页码,从1开始. %5: 数据大小. %6: 标记当前页. %7: 显示的名称. ''' liahref = "

  • %s
  • "
    # 循环生成数据,并且使用formatstyle控制显示或隐藏.
    htmls = [formatstyle(item, noformatpage) for item in range(0, irowscount // ipagesize)]
    # 获取总页数
    countpage = len(htmls)
    # 处理余数的情况
    if irowscount % ipagesize != 0:
    # 最后一页.
    lenindex = len(htmls) + 1
    # 赋值给总页数
    countpage = lenindex
    # 新增到最后.
    print(pageclass)
    print(url)
    print(lenindex)
    print(ipagesize)
    print(pageclass)
    print(lenindex)
    # liahref % ("", "http://localhost:5000/admin/list",68,10, "", 68)
    htmls.append(liahref % (
    currentpage,black, url, lenindex, ipagesize, currentpage,
    lenindex) if lenindex == noformatpage else liahref % (
    pageclass,black, url, lenindex, ipagesize, pageclass, lenindex))
    # 正确代码***********************************************************
    # 参考上面的a,只是去掉了外层的li标记
    ahref = "%s"
    # 第一页和上一页
    firsthrefdisabled = "am-disabled"
    # 生成默认禁用数据
    prevhref = "上一页"
    firsthref = "第一页"
    # 只有当大于1的时候第一页和上一页才可以用.
    if noformatpage > 1:
    firsthrefdisabled = black
    prevhref = ahref % (url, (noformatpage - 1), ipagesize, currentpage, '上一页')
    firsthref = ahref % (url, 1, ipagesize, currentpage, '第一页') if 1 == noformatpage else ahref % (
    url, 1, ipagesize, pageclass, '第一页')
    # 新增到最前面
    htmls.insert(0,
    "

  • %s
  • %s
  • " % (
    firsthrefdisabled, firsthref, firsthrefdisabled,
    prevhref))
    # 第一页和上一页END.
    # 下一页和最末页
    lasthrefdisabled = "am-disabled"
    # 生成默认禁用数据
    nexthref = "下一页"
    lastindexhref = "最末页"
    # 只有当小于总数的时候下一页和最末页才可用.
    if noformatpage < (countpage): lasthrefdisabled = black nexthref = ahref % (url, (noformatpage + 1), ipagesize, currentpage, '下一页') lastindexhref = ahref % (url, countpage, ipagesize, currentpage, '最末页') htmls.append( "

  • %s
  • %s
  • " % (
    lasthrefdisabled, nexthref, lasthrefdisabled, lastindexhref))
    # 下一页和最末页END.
    # 显示最后的数据.
    if countshow:
    htmls.append("总%s条,共%s页,当前%s页" % (irowscount, (len(htmls) - 2), page))
    return ipage, ipagesize, ''.join(htmls)

    v0.9[建议使用最新版本.]:

    '''
    v0.9[建议使用最新版本.]
    这是一个分页的伪代码[使用了Flask(不过实际使用什么也无所谓)].
    效果可以看[Pagination.png]图片.
    涉及到数据库和返回给前台.
    这里只提供了一个分页的方法,注释很详细.
    prd.
    pruidong@gmail.com
    2015.12.30
    '''
    '''
    分页程序.
    生成代码示例[使用了妹子UI的分页.参照:http://amazeui.org/widgets/pagination]:

    总840条,共83页,当前6页
    page: 当前页数,从1开始.
    pagesize: 每页里面多少数据,一般为10.
    rowscount: 查询数据库中的总数(一共有多少条数据,不能添加查询条件,直接count即可).
    url: 访问地址[注意:这里没有处理url本身带有参数的问题],这里自动添加了page和pagesize参数(!!丢弃了原来的所有参数,若有的话)
    v0.9[建议使用最新版本.]
    '''
    def repage(page, pagesize, rowscount, url):
    # 页码
    ipage = int(page)
    # 原始page ,数值不会被改变
    noformatpage = int(page) # 原始page
    # 页码大小
    ipagesize = int(pagesize)
    # 总数量
    irowscount = int(rowscount)
    # 进行数值限定
    if ipage <= 1: ipage = 1 # 进行分页查询时,需要跳过的数据. # 这个ipage和noformatpage是有区别的!!! # 例如,当前是第二页,那么下面这个ipage是10,而noformatpage是不变的2. ipage = (ipage - 1) * ipagesize # 给当前页添加一个css标记 currentpage = "am-active" # 当前 # 不添加标记 pageclass = "" # 正常 # 空包标记,用于替换. black = "" ''' 格式化数据. 主要作用: 大于/小于当前页5个的自动隐藏. 比如: 当前是第6页,那么只显示1-11页,并且第6页在中间. 示例如下: 1,2,3,4,5,[6],7,8,9,10,11 第8页的时候: 3,4,5,6,7,[8],9,10,11,12,13 以此类推... ''' def formatstyle(item, noformatpage): # 标记最大隐藏的.可配置. maxhidden = noformatpage + 5 # 标记最小隐藏的.可配置 minhidden = noformatpage - 5 # 为当前页添加css标记. if item == noformatpage: return liahref % (currentpage, black, url, item, ipagesize, currentpage, item) elif (minhidden) <= item <= (maxhidden): # 这个地方是控制显示的,只有符合当前页+5或者-5之内的才显示. return liahref % ( pageclass, black, url, item, ipagesize, pageclass, item) else: # 不满足条件的全都隐藏. return liahref % ( pageclass, hiddenstyle, url, item, ipagesize, pageclass, item) # 隐藏数据. hiddenstyle = "display:none;" ''' %1: 添加各种class,在这里就是:标记当前页. %2: 设置隐藏/显示(通过css控制) %3: url地址,不带参数的url地址. %4: 当前页码,从1开始. %5: 数据大小. %6: 标记当前页. %7: 显示的名称. ''' liahref = "

  • %s.
  • "

    # 循环生成数据,并且使用formatstyle控制显示或隐藏.
    htmls = [formatstyle(item, noformatpage) for item in range(1, irowscount // ipagesize)]

    # 获取总页数
    countpage = len(htmls)
    # 处理余数的情况
    if irowscount % ipagesize != 0:
    # 最后一页.
    lenindex = len(htmls) + 1
    # 赋值给总页数
    countpage = lenindex
    # 新增到最后.
    # 错误代码*****************************
    '''
    htmls = htmls.append(
    '''
    # 错误代码***********************************************************

    # 正确代码*****************************
    htmls.append(liahref % (currentpage, url, lenindex, ipagesize, currentpage,lenindex) if lenindex == noformatpage else liahref % (pageclass,url, lenindex, ipagesize, pageclass, lenindex))
    # 正确代码***********************************************************
    #参考上面的a,只是去掉了外层的li标记
    ahref = "%s."

    # 第一页和上一页
    firsthrefdisabled = "am-disabled"
    # 生成默认禁用数据
    prevhref = "上一页"
    firsthref = "第一页"
    # 只有当大于1的时候第一页和上一页才可以用.
    if noformatpage > 1:
    firsthrefdisabled = black
    prevhref = ahref % (url, (noformatpage - 1), ipagesize, currentpage, '上一页')
    firsthref = ahref % (url, 1, ipagesize, currentpage, '第一页') if 1 == noformatpage else ahref % (
    url, 1, ipagesize, pageclass, '第一页')
    # 新增到最前面
    htmls.insert(0,
    "

  • %s
  • %s
  • " % (
    firsthrefdisabled, firsthref, firsthrefdisabled,
    prevhref))
    # 第一页和上一页END.
    # 下一页和最末页
    lasthrefdisabled = "am-disabled"
    # 生成默认禁用数据
    nexthref = "下一页"
    lastindexhref = "最末页"
    # 只有当小于总数的时候下一页和最末页才可用.
    if noformatpage < (countpage): lasthrefdisabled = black nexthref = ahref % (url, (noformatpage + 1), ipagesize, currentpage, '下一页') lastindexhref = ahref % (url, countpage, ipagesize, currentpage, '最末页') htmls.append( "

  • %s
  • %s
  • " % (
    lasthrefdisabled, nexthref, lasthrefdisabled, lastindexhref))
    # 下一页和最末页END.

    # 显示最后的数据.
    htmls.append("总%s条,共%s页,当前%s页" % (irowscount, (len(htmls) - 2), page))
    return ipage, ipagesize, ''.join(htmls)
    @app.route('/indexs', methods=['POSTS', 'GET'])
    def seetext():
    page = request.args.get('page', 1)
    pagesize = request.args.get('pagesize', 10)
    see = SeeText()
    # 获取总数--不要带条件!!!
    rowscount = see.count()
    # 参数请参考方法说明.
    pageinfo = repage(page=page, pagesize=pagesize, rowscount=rowscount, url=request.base_url)
    # 查询数据
    data = see.findAll(page[0], pagesize)
    # 转换一下,第二项是分页的数据.
    pageinfo = ''.join(pageinfo[2])
    # 返回数据给前台
    return render_template("/index/index.html", seetextdata=data, pageInfo=pageinfo)

    2015:逆风飞翔

    开篇

    你的人生永远不会辜负你的。那些转错的弯,那些走错的路,那些流下的泪水,那些滴下的汗水,那些留下的伤痕,全都让你成为独一无二的自己。by 朱学恒

    上面这段话,我曾用在很多地方.

    对于即将过去的这一年,也很适用.因此,写在最亮的地方.

    其实这一年说起来并非一帆风顺,但无论如何,都已经走过来了.想起这一年,像在慢速放一个电影,一张张画面划过眼前.熟悉,却又感叹时光匆匆.有人说,无论何事,咬咬牙总会过去.何尝不是这样呢.任何一切,都躲不过时间的流逝.小四有一段话:

    很多我们以为一辈子都不会忘记的事情,就在我们念念不忘的日子里,被我们遗忘了 。–《左手倒影 右手年华》

    若不回忆,岂不就忘记了?

    站在三岔路口

    当站在大街上,围满人的棋桌前,看的人会比真正下棋的人着急。总会思考,怎么不走那,怎么不走那..等等.当你真正坐在那个地方,你也会感受到来自周围”热切的目光”.有一句话是”观棋不语真君子”.自己做到就好.扯远了.2015发生了很多事.去了一些地方,换了一份工作.

    其实在换工作之前,有过很多纠结.面临的问题,总是一个接着一个.简单总结如下:

    选择
    image-2387

    • 人际关系:其实对于新公司所面临的挑战,最大的莫过于人际关系,可能在原来公司已经辛辛苦苦经营了很久的人际关系.一换工作,就消失殆尽了.同样也存在一些风险,也可能无法融入一个新的团队等等.
    • 工作层面:每个人性格都是不同的,但还好,我觉得我很幸运.遇到的很多同事,基本都比较聊得来.
    • 技术层面:每个公司所面临的事情不尽相同,所使用到的技术肯定也千差万别.可能无法使用最喜欢//最熟悉的语言,而要从头开始接触一门或者几门新的语言.虽然在公司中接触一门或者几门语言会是一个好的学习机会(因为往往都带着目标去学习),但同样会出现一些小问题.

    大概就是以上几点.每一次选择都意味着未来的方向,站在一个三岔路口或者十字路口,所面临的问题都是不一样的.我相信每一个人都会有自己不同的选择,对于他人或许很重要,也可能不重要.毕竟没有人会一直跟在身后,除了影子.

    Code……

    2015依旧写了一些代码.不多不少,数下来才发现:真的没几个啊!(内心几乎是崩溃的,咋才写了这么些)..

    1. 在线服务:提供了三个东西(每天一张图//一大堆段子//一个网站的离线chm[抱歉已经许久没更新了..]),其实是很简单的一个东西,是业余练手和一些乐趣.在完成这个小东西的时候,会发现写代码的那种快乐[不必思考太多,拿起就写,想到就开始写].并且依据此服务,发布了一个工具集(已经开源:地址).在线服务地址..PS:这几个均使用Python脚本完成.
    2. Android客户端[地址]:从2014开始自学Android,刚学的时候,做了几个简单的拿不出手的APP.只好按下,继续发力,在今年的年末的前几天,终于发布了一个APP(还有很多功能和细节有待优化),学习进度最快的那段时间,恰好是拼命写代码的那几天.面对一个新东西是有点痛苦的,这种痛苦建立在思维上,Android打破了固有思维(令我万万没想到的是:Android使用XML布局[当然也可以用Java代码进行布局,但是官方推荐使用XML]).就因为思维差异,导致学习路线陡增,学习时间也就无限增长了.也就这时候,忽然想起来之前一个前辈说的:之前做C++,后来Web发展,去学习了jQuery.难度可想而知.往前的每一步,无论大小,都是进步.偶尔害怕踏出第一步,因其中的不确定.战胜自己,才可以勇敢前行.
    3. 其余的技术部分就是一些乱七八糟的不值得说的了,也没啥亮点.

    编程语言
    image-2388

    2015也接触不少新东西:Python3.5发布,AngularJS2.0快要发布,Android 6.0发布等等等等(目前已经从Python2+切换到Python3+,大部分问题已解决).每一次变革,都意味着新东西的到来.另外我还想说,这几年我一直在接触PHP,从买到的第一本《PHP与MySQL程序设计(第四版)》到现在,它依旧是崭新的.而目前PHP7都已经发布了.实在愧疚,感觉PHP是学不会了.希望后面有时间可以多了解一下PHP.

    对于其它方面的技术就了解的越来越少了,依稀记得有过这样一个约定:从某天起,我要把系统更换成Linux(一直使用Fedora,从Fedora18->>到目前的Fedora23).事实是,到现在依旧还没使用上.原因很多,我想技术分散,学习力减退,时间紧张这些可能都是借口.慢慢我在发现,我好像只能做一个Linux的”虚拟机使用者”.为此,深感遗憾.回想当初,刚接触Linux,先是去看了Ubuntu(一个很优秀的Linux发行版,那时候好像还是Ubuntu12),结果没看明白官网的安装介绍(当时不知道有翻译这个神器..)..然后就找到了Fedora的官网(那时候就已经有中文了..赞一个).一步一步跟着学习,在学习群里有一句知名的话:如果没有重装过几遍Linux,肯定就是没用过Linux的.没错,如你上面看到的,我重装了很多遍Linux.说实话,每次都很心疼,因为里面都是我的数据啊,学习数据各种资料各种工具(一开始重装,不知道备份,然后就弄丢了).那时候也喜欢折腾,各种折腾.后来慢慢就不太敢大折腾了,毕竟装了那么些工具,也写了一些代码,重装简直是花样作死啊.当时啃过很多遍《鸟哥的Linux私房菜》,很遗憾,就是没看完(大多数时候,就看一半多就没看后面的了).想起来,发现很多时候,书只看了一半就没有继续看下去了.原因和借口都很多.只是希望有时间能好好看完已经看过一半的那些书.还是希望有机会可以直接换到Linux系统,接触更多的Linux知识(毕竟她是如此这般迷人).

    除了PHP还有另外一个是没有想明白,也没有时间去想明白的——没错,是NodeJS.

    等有时间好好研究下PHP和NodeJS.免得以后遇到初级问题,还需要去请教别人.可能会有疑问,为啥想了解这么多?刚开始接触程序的时候,想学的东西很多:Java,PHP,C++,JavaScript,Linux,DataBase(里面区分了很多)等等这些,也收集了很多书,但大部分都没有看过.其中的一些不在最开始想学的里面,但也还是接触了,在列表里面的,却很少有时间去接触了.

    其实,我们都在梦想的路上,越来越远.

    站在家门口,盼望远方

    前几天回家,特地拍了一张照.不知道我所在的城市,在哪个方向.但我所知道的是,家在哪个方向.

    现在所在的城市,跟大多数城市一样,夜晚灯火通明.外面一点都不寂寞,没有伸手不见五指的黑暗.而家则截然不同,关灯后伸手不见五指,四处安静的能听见心跳声.习惯了喧闹,忽然觉得那份安静很难得.之前回去的时候,偶尔会失眠,因为实在不习惯那太安静的环境——没有光线,没有喧闹,没有车的喇叭声.后来回去的次数渐渐多了也就习惯了,只是总觉得,在这反复的切换中,仿佛丢失了什么.认真的想,却又想不起来.

    家
    image-2389

    家是没有雾霾的那种地方,偶然拍一张,竟然会发现不一样的美.像是很久没看见那样的景色,熟悉却又陌生.最让我感到震撼的是:当走在路上,打招呼的人很多,人们不是神色匆匆,也不是一脸愁容,也没有低头看着手机.我似乎刻意忘记了,在不远的地方,有一群看着我长大的人.他们能随口喊出我的名字,能随口说出我小时候的趣事.这样的发现,让我觉得惊讶——难道这不是自然而然的吗?那是我的家,这不是必然的吗?在城市的生活,似乎忘记了还有那样的一个地方.

    去年从更远的地方回来,回到离家坐车5小时的地方.这是一种接近,在有一年的十一,忽然有点想家.电话中熟悉的声音,让我在挂断电话之后,泣不成声.当即决定,靠近,慢慢靠近.在离家更近的地方,仿佛能感觉到家人的抚摸,亲昵的话语.在很多时候,会觉得很近,仿佛就在眼前,就在脑海里.当然了,还有家乡从小吃到大的美食,各种辣味的食品,那可都是小时候最喜欢的零食.在前几年去外面的时候,曾去超市买过和小时候类似的零食,一口咬下去才发现那味道,跟小时候的差别太大了.我想,即使回到当初上学的地方,也找不回来小时候吃零食的那种快乐.但家的味道一直存在.

    “当我站在家门口眺望你所在的方向,你能听到我内心的期盼吗?”——来自家的声音.

    逆水行舟

    2016看起来还有很多事情要去完成.为什么是逆水行舟?因为对前途的不可知,对未来的迷惘.

    面对困难,可能不会在使用之前的方式去解决.
    面对技术问题,可能不会去群里问.
    面对选择,可能会有更多顾虑.

    我想这便是成长吧,《大话西游》里说,”成长不一定会得到,但一定会失去”.

    那么,2016:

    • 做该去做的事情.
    • 面对困难,咬咬牙,总会过去的.
    • 累了就回头看看,看完之后,立刻前行.别留恋曾经,因为留恋也没有用.
    • 遇到技术问题,先去看官方文档.因为可能官方文档里稍微写了一下(有很多问题确实在官方文档里找不到答案),然后再去找.
    • 你所经历的痛苦,在人群中都太渺小了.根本不值得一提.
    • 总有人在过着你想要的生活.
    • 其实上面大部分都是鸡汤,没错.鸡汤就是这么激励人.

    废话发表完毕,2015无论如何是即将过去了.希望在2016可以心安.加油!

    展望2016

    看上面一段.

    写在2015年末.献给2015,和即将到来的2016.

    无论要去哪,先战胜自己,迈出勇敢的第一步!

    你的人生,不会辜负你的.

    PS:prd 于 2015.12.21 [CD]

    Android:第一个Android APP发布

    第一个..

    开源了APP的所有代码[访问地址].

    初试牛刀,学了这么久的Android.总算在磕磕绊绊中完成了第一个APP(虽然问题还有一大堆)…

    只记得是从去年开始学习的Android(自学),期间学习进度比较缓慢,但也还是”捣鼓”出了几个不叫APP的APP.现在发布的这个,拖拉拖拉的做了4个月,其实折算下来,时间应该是不长的.没办法,期间有各种问题.大部分时间都花在解决大问题上了.

    其实写了这么久,多少还是有点感情的.我有一个不知道是不是不太好的习惯,有时候习惯在代码里面码一些注释,就是写完那个功能或者那个模块的感受……

    下面也会贴一些其中的注释.更重要的是,我开源了这个APP[访问地址],如果不愿意去看开源的源码,也要去那边的文件夹才能下载的…(因为哥的服务器实在架不住大楼量啊).还需要说明的一个问题是:因服务器的原因,在代码里面所有跟服务器交互的地方都被去掉了,这也是逼不得已,希望能谅解一下.但我保证,其余部分的代码是完整的(跟发布版的一毛一样的).

    很遗憾,这个APP没能在任何市场上架,想必这也是一个不太美好的地方了.后来想想不上就不上吧,直接发布也挺好,反正也是要开源的.(源码地址在上面找)写这个东西的时候,其实已经没多少兴奋劲了,因为都已经打包一段时间了,才把源码公开出来(也是希望新手可以学习下,虽然我也是新手).

    啰嗦了一大堆,想起后面还有年终总结要写.好吧……停止啰嗦,下面发干货(对了这个APP叫:凉夜).

    大概做了什么

    1. 看看(文字内容,采集自网络):
    详细:自有国外服务器,提供数据支撑,文字内容同样基于该服务器提供
    (对于访问速度是最大的性能瓶颈,希望后期有能力可以改善)。当前更新频率为3天更新一次。
    在Android客户端提供了缓存及下拉刷新操作。在分享方面,目前集成了微信、易信、QQ这几个分享。

    2. 有图(图片,每天定时更新):
    详细:图片源自某门户每天更新的图片,后台服务器每天零时左右定时进行更新。
    客户端对于服务器端有相应延迟(避免提前请求导致的加载错误)。
    该项同样提供文字方式的分享(在分享方式上有差别),并提供保存本地及即时设置桌面功能。

    3. 关于(关于APP、设置等操作):
    详细:可在客户端开启[每天自动设置桌面功能(使用两个Service相互监听,
    保障可正确更新,若开启,该功能会延迟更新.)]、提交反馈、关于等。

    Android LOGO
    image-2382

    代码里面的啰嗦

    下面的都是来自代码里面的.可以在[地址]里面的MainActivity.java里面找到.


    /*
    * 今天开始重写之前的"闲聊",只是现在改名字了--凉夜。
    初次的意思是,在每一个凉的夜,都需要一些快乐或者温暖。
    刚写还是遇到一些困难,也挺头疼的。
    加油!
    prd - 2015.8.15 2:20
    今天调整了很久ListView的显示,加上泛型也用不了导致解析JSON数据出了很大问题,只能暂时找个办法先解决了。2015.8.16 3:30
    今天在一个群的朋友的帮助下,总算解决了Fragment加载图片的问题,真心觉得很开心。也谢谢那个朋友。因为这个问题实在困扰了好几天啊。。。。。。
    2015.8.22 1:10
    这可能是到目前为止,单日增加功能最多的一次了。
    本次共实现以下功能:
    1.为自动设置桌面增加两个Service(保证Service一直存在);
    2.为Image增加一个从底部弹出的框;
    3.将NetworkUtils从new,改为单例.
    看起来功能并不多,但是代码量真心不小.
    2015.8.29 23:40
    =====================================================================================
    这次更新了很多东西,已更新如下:
    1.完成ActionBar右侧只在[看图]中显示.
    2.在[我]菜单下,新增一个无限量滚动广告图.
    3.修复在[我->有话要说]里面输入完之后,点击空白处关闭虚拟键盘.
    4.-->重大修改-->修改工具类,获取JSON数据的方法(工具类不在处理,具体的对象转换),代码结构更加清晰.
    5.修复ActionBar的图标大小问题.
    6.修复下载图片的路径,并实时刷新系统图片媒体库.
    待优化以下问题:
    1.多次点击ActionBar中的图标按钮,会导致内存溢出.
    2.实现一个留言反馈功能(注意过滤非法字符,可以考虑新增一个图片选择功能..)
    3.实现一个关于界面.
    4.对APP整体颜色进行优化.
    5.对相应代码进行优化或简化.
    6.开源.
    2015.9.4 1:40
    ===================================================================
    今天优化了一下界面颜色,有以下改进:
    1.修改[我]界面中的图标颜色.
    2.修正[反馈][关于]中文字不居中的问题.
    3.优化所有颜色,从COLOR文件获取.
    4.优化[看图]底部弹出框颜色.
    5.优化[自动设置桌面]弹窗的颜色.
    6.[看]新增一个复制功能.
    2015.9.8 22:29
    2015.9.10 实现了微信的分享,接下来可能需要实现微博,易信的分享功能.
    并且优化了[我]下面的轮播展示.
    2015.9.13 为工具类添加更加详细的注释.并针对代码进行部分优化.
    2015.8.15 2:20 开始开发.
    2015.12.1 0:30 完成开发.
    不再开发新的功能,后续几天将进入统一测试.
    预计12.5左右发布第一个正式版本.
    从8.15开始第一行代码,拖拖拉拉接近3个多月,三大功能模块,最占时间莫过于[分享]部分.
    无论如何,这都是一个开始.从刚开始的菜鸟,到如今的菜鸟,虽然不知到进步多少,但只能说,每一次更新
    都是一个大的进步.对于下一个版本,将着重优化服务器端,及客户端逻辑代码.
    之前的[闲聊]算是一个失败的典型,而当前这个则是完全站立在失败之上.
    加油,不为什么!
    ->:你的人生永远不会辜负你的。那些转错的弯,那些走错的路,那些流下的泪水,那些滴下的汗水,那些留下的伤痕,全都让你成为独一无二的自己。by 朱学恒
    * */

    一个小网络工具类

    别着急,还要发布个Android的网络工具类,使用了Volley.封装了一些简单的操作.源码里面说的很清楚,就不重复解释了.


    import android.app.ActivityManager;
    import android.app.WallpaperManager;
    import android.content.*;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.media.MediaScannerConnection;
    import android.net.ConnectivityManager;
    import android.net.NetworkInfo;
    import android.net.Uri;
    import android.os.Build;
    import android.os.Environment;
    import android.view.Gravity;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.TextView;
    import android.widget.Toast;
    import com.android.volley.*;
    import com.android.volley.toolbox.*;
    import com.w1520.liangye.app.R;

    import java.io.*;
    import java.util.List;
    import java.util.Map;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    /**
    *

    工具类

    *

    *

    此类使用Volley实现网络操作.

    *

    *

    当前类封装了:文字,图片的加载,Toast的各种实现,以及

    *

    保存图片信息到本地,复制文本数据,转换ImageView等操作.

    About
    *

    *

    这个类不可以被继承或重写,并且使用单例模式保证无论任何时候,

    *

    内存中仅存在一个当前类的实例.

    *

    *

    注意:当前类的加载数据,不适合下载,或者需要操作大量数据.

    *

    *

    * Created by puruidong on 8/14/15.
    */
    public final class NetworkUtils {

    //网络请求队列实例.
    private static RequestQueue queue;
    //上下文实例.
    private Context context;
    //当前类实例.默认为null
    private static NetworkUtils network = null;

    /**
    * 创建一个实例时,内部初始化.
    * 外部无需关心此构造函数的实现.
    *
    * @param context
    */
    private NetworkUtils(Context context) {
    this.context = context.getApplicationContext();
    if (queue == null) {
    queue = Volley.newRequestQueue(context);
    }
    }

    /**
    * 获取一个当前类的实例.
    * 注意:当前类的实例仅能通过此方式获得.
    * 以保证在内存中仅存在唯一一个当前类的实例.
    *
    * @param context 上下文实例.
    * @return 当前类的实例.
    */
    public static NetworkUtils getInstance(Context context) {
    if (network == null) {
    network = new NetworkUtils(context);
    }
    return network;
    }

    /**
    * 检查网络连接是否可用.
    * 不区分是3G还是WIFI.
    *
    * @return 若网络可用返回true, 反之返回false.
    */
    public boolean isOnline() {
    ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    return (networkInfo != null && networkInfo.isConnected());
    }

    /*-------------------------------------[GET]从网络获取文字数据---------------------------------------------*/

    /**
    * 获取文本数据.
    *

    * 若无法访问网络,将关闭加载提示框,
    * 并给出无法访问网络的提示.
    *
    * @param url 获取数据的url,此方法不检测url的正确性.
    * @param volleyResult 数据加载成功后的回调接口,用于传递数据
    * @param dialog 数据加载提示框.
    */
    public void getTextData(String url, final VolleyResultData volleyResult, CustomProgressDialog dialog) {
    if (!isOnline()) {
    showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
    if(null!=dialog) {
    dialog.hide();
    }
    } else {
    StringRequest strArray = new StringRequest(url, new Response.Listener() {
    @Override
    public void onResponse(String response) {
    volleyResult.onSuccess(response);
    }
    }, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
    showToast("啊哦!~暂时加载失败了,要不稍候在试试?..呜..", Toast.LENGTH_SHORT);
    }
    }
    );
    queue.add(strArray);
    }
    }

    /*-------------------------------------[GET]从网络获取文字数据---------------------------------------------*/

    /*-------------------------------------[POST]提交请求,从网络获取数据---------------------------------------------*/

    /**
    * POST方式提交普通参数数据.
    * --------------------------------------
    * 若无法访问网络,将关闭加载提示框,
    * 并给出无法访问网络的提示.
    *
    * @param url 获取数据的url,此方法不检测url的正确性.
    * @param params POST方式提交数据的参数.* :@see {@link android.support.v4.util.ArrayMap}
    * @param volleyResult 数据加载成功后的回调接口,用于传递数据
    * @param dialog 数据加载提示框.
    */
    public void postData(String url, final Map params, final VolleyResultData volleyResult, CustomProgressDialog dialog) {
    if (!isOnline()) {
    showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
    if(null!=dialog) {
    dialog.hide();
    }
    } else {
    StringRequest postRequest = new StringRequest(Request.Method.POST, url,
    new Response.Listener() {
    @Override
    public void onResponse(String response) {
    volleyResult.onSuccess(response);
    }
    }, null) {
    @Override
    protected Map getParams() {
    //在这里设置需要post的参数
    return params;
    }
    };
    queue.add(postRequest);
    }
    }

    /**
    * 使用Volley请求文本数据,
    * 实现此接口自行处理相关String数据.
    * 即可获取数据.
    */
    public interface VolleyResultData {
    void onSuccess(String response);
    }

    /*-------------------------------------[POST]提交请求,从网络获取文字数据---------------------------------------------*/

    /*-------------------------------------从网络获取图片数据---------------------------------------------*/

    /**
    * 通过ImageRequest加载并设置网络图片.
    * 注意:方法不对url的有效性进行检测.
    * 若无法访问网络,将关闭加载提示框,
    * 并给出无法访问网络的提示.
    *

    * 此方法使用了默认实现的缓存.
    *
    * @param url 图片url
    * @param view 图片控件所在的View对象
    * @param imgViewId ImageView或其它图片展示控件的Id
    * @param dialog 图片加载提示框对象.
    * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByImageRequest(String, onImageLoaderListener, CustomProgressDialog)}
    */
    public void getImageByImageRequest(String url, final View view, final int imgViewId, CustomProgressDialog dialog) {
    if (!isOnline()) {
    showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
    if(null!=dialog) {
    dialog.hide();
    }
    } else {

    ImageRequest request = new ImageRequest(url, new Response.Listener() {
    @Override
    public void onResponse(Bitmap response) {
    //显示加载成功的图片.
    ImageView img = (ImageView) view.findViewById(imgViewId);
    img.setImageBitmap(response);
    }
    }, 0, 0, ImageView.ScaleType.CENTER_INSIDE, Bitmap.Config.RGB_565, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
    //显示加载失败的图片.
    showToast("啊哦!~暂时加载失败了,要不稍候在试试?..呜..", Toast.LENGTH_SHORT);
    ImageView img = (ImageView) view.findViewById(imgViewId);
    img.setImageResource(R.mipmap.image_loader_error);
    }
    });
    queue.add(request);
    }
    }

    /**
    * 通过ImageRequest加载并传递值给回调接口.
    * 若无法访问网络,将关闭加载提示框,
    * 并给出无法访问网络的提示.
    * 注意:方法不对url的有效性进行检测.
    *

    * 此方法使用了默认实现的缓存.
    *
    * @param url 图片url地址.
    * @param loaderlistener 接收Bitmap的回调接口.若请求响应为空,回调接口返回空.
    * @param dialog 图片加载提示框对象.
    * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByImageRequest(String, View, int, CustomProgressDialog)}
    */
    public void getImageByImageRequest(String url, final onImageLoaderListener loaderlistener, CustomProgressDialog dialog) {
    if (!isOnline()) {
    showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
    if (null != dialog) {
    if(null!=dialog) {
    dialog.hide();
    }
    }
    } else {
    ImageRequest request = new ImageRequest(url, new Response.Listener() {
    @Override
    public void onResponse(Bitmap response) {
    //显示加载成功的图片.
    loaderlistener.onSuccessImage(response);
    }
    }, 0, 0, null, null, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
    //显示加载失败的图片.
    //showToast("啊哦!~暂时加载失败了,要不稍候在试试?..呜..", Toast.LENGTH_SHORT);
    //error.printStackTrace();
    }
    });
    queue.add(request);
    }
    }

    /**
    * 通过NetworkImageView加载
    * 并设置网络图片,若无法访问网络,将关闭加载提示框,
    * 并给出无法访问网络的提示.此方法会给予一个默认图片及
    * 一个加载失败时候的图片.注意:方法不对url的有效性进行检测.
    *

    * 注意:此方法已经默认实现了一个图片缓存.具体可参考:
    * {@link com.w1520.liangye.utils.BitmapCache}
    *
    * @param url 图片url地址.
    * @param view 图片控件所在的View对象activitd
    * @param netWorkImgViewId ImageView或其它图片展示控件的Id
    * @param dialog 图片加载提示框对象.
    * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByImageLoader(String, View, int, CustomProgressDialog)}
    */
    public void getImageByNetworkImageView(String url, final View view, final int netWorkImgViewId, CustomProgressDialog dialog) {
    if (!isOnline()) {
    showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
    if(null!=dialog) {
    dialog.hide();
    }
    return ;
    }
    ImageLoader loader = new ImageLoader(queue, new BitmapCache());
    NetworkImageView networkImageView = (NetworkImageView) view.findViewById(netWorkImgViewId);
    networkImageView.setDefaultImageResId(R.mipmap.image_loading);
    networkImageView.setErrorImageResId(R.mipmap.image_loader_error);
    networkImageView.setImageUrl(url, loader);
    }

    /**
    * 通过ImageLoader加载并设置网络图片
    * 注意:方法不对url的有效性进行检测.
    * 若无法访问网络,将关闭加载提示框,
    * 并给出无法访问网络的提示.
    *

    * 此方法会设置一个默认加载图片,及一个加载失败时
    * 显示的图片.
    *

    * 注意:此方法已经默认实现了一个图片缓存.具体可参考:
    * {@link com.w1520.liangye.utils.BitmapCache}
    *
    * @param url 图片url
    * @param view 图片控件所在的View对象
    * @param imgViewId ImageView或其它图片展示控件的Id
    * @param dialog 图片加载提示框对象.
    * @return 返回一个响应图片的资源.
    * @see {@link com.w1520.liangye.utils.NetworkUtils#getImageByNetworkImageView(String, View, int, CustomProgressDialog)}
    */
    public View getImageByImageLoader(String url, View view, int imgViewId, CustomProgressDialog dialog) {
    if (!isOnline()) {
    showToast("目前无法访问网络,请稍候在试", Toast.LENGTH_SHORT);
    if(null!=dialog) {
    dialog.hide();
    }
    }
    ImageLoader loader = new ImageLoader(queue, new BitmapCache());
    ImageView imageView = (ImageView) view.findViewById(imgViewId);
    ImageLoader.ImageListener listener = ImageLoader.getImageListener(imageView, R.mipmap.image_loading, R.mipmap.image_loader_error);
    loader.get(url, listener);
    return imageView;
    }

    /**
    * 获取网络响应的图片实现此接口,重写接口中的方法即可获得该图片资源.
    * 若网络数据返回为null,则接口中的方法将返回null.
    */
    public interface onImageLoaderListener {
    void onSuccessImage(Bitmap bitmap);
    }

    /*-------------------------------------从网络获取图片数据---------------------------------------------*/

    /**
    * 快速输出Toast,默认显示时长为 {@link android.widget.Toast#LENGTH_SHORT}.
    * 不支持布局自定义和显示时长自定义.
    *
    * @param message 要显示的信息.
    * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(View view, String message, int duration)}
    * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message, int duration)}
    */
    public void showToast(String message) {
    Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
    }

    /**
    * 显示Toast,可以使用自定义的布局,自定义显示时长.
    *
    * @param view 一个针对Toast的自定义布局.
    * @param message 要显示的信息.
    * @param duration 显示时长,请使用Toast中的常量进行设置.
    * 可选值:{@link android.widget.Toast#LENGTH_SHORT}
    * {@link android.widget.Toast#LENGTH_LONG}
    * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message, int duration)}
    * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message)}
    */
    public void showToast(View view, String message, int duration) {
    Toast toast = new Toast(context);
    toast.setView(view);
    toast.setDuration(duration);
    toast.setText(message);
    toast.show();
    }

    /**
    * 显示带有图片,居中的Toast.
    *
    * @param message 要显示的信息.
    * @param duration 显示时长,请使用Toast中的常量进行设置.
    * 可选值:{@link android.widget.Toast#LENGTH_SHORT}
    * {@link android.widget.Toast#LENGTH_LONG}
    * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(View view, String message, int duration)}
    * @see {@link com.w1520.liangye.utils.NetworkUtils#showToast(String message)}
    */
    public void showToast(String message, int duration) {
    LayoutInflater inflater = LayoutInflater.from(context);
    View toast_view = inflater.inflate(R.layout.custom_toast_center, null);
    ((TextView) toast_view.findViewById(R.id.toast_message)).setText(message);
    Toast toast = new Toast(context);
    toast.setView(toast_view);
    toast.setGravity(Gravity.CENTER, 0, 0);
    toast.setDuration(duration);
    toast.show();
    }

    /**
    * 判断进程是否处于正在运行状态.
    *
    * @return 正在运行返回true, 反之返回false.
    */
    public boolean isProessRunning(String proessName) {
    boolean isRunning = false;
    ActivityManager am = (ActivityManager) context
    .getSystemService(Context.ACTIVITY_SERVICE);
    List lists = am.getRunningAppProcesses();
    for (ActivityManager.RunningAppProcessInfo info : lists) {
    if (info.processName.equals(proessName)) {
    isRunning = true;
    }
    }
    return isRunning;
    }

    /**
    * 将一张图片(Bitmap资源)设置为手机桌面壁纸.
    *

    * 需要权限:{@code
    * }
    *
    * @param bitmap 要设置为桌面的资源.
    * @throws IOException 资源不存在或资源损坏导致的IO异常.
    */
    public void setWallPaper(Bitmap bitmap) throws IOException {
    WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
    wallpaperManager.setBitmap(bitmap);
    }

    /**
    * 获取SD卡或者内置存储空间可以保存资源的路径.
    * 此处未实现对存储空间是否充足进行判断.
    *
    * @return 返回保存数据的路径, 有SD卡则是SD上的路径, 反之内置存储空间上的路径.
    */
    private String getSDPath() {
    boolean hasSDCard = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
    if (hasSDCard) {
    return Environment.getExternalStorageDirectory().toString() + "/Pictures/liangye";
    } else
    return "/data/data/com.w1520.liangye/liangye";
    }

    /**
    * 判断当前系统LEVEL
    * Android在4.4之后不允许第三方发起系统广播.
    * 所以需要判断版本.
    *
    * @return 当前版本是否大于等于19[4.4].
    */
    private boolean hasKitkat() {
    return Build.VERSION.SDK_INT >= 19;
    }

    /**
    * 刷新媒体库
    *
    * @param filePath
    * @param context
    */
    private void scanPhotos(String filePath) {
    Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    Uri uri = Uri.fromFile(new File(filePath));
    intent.setData(uri);
    context.sendBroadcast(intent);
    }

    /**
    * 刷新媒体库.
    *
    * @param currFilePath
    */
    private void refreshPicture(String currFilePath) {
    /* ContentValues values = new ContentValues();
    values.put(MediaStore.Images.Media.DATA,currFilePath);
    values.put(MediaStore.Images.Media.MIME_TYPE,"image/jpeg");
    context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values);*/

    if (hasKitkat()) {
    MediaScannerConnection.scanFile(context,
    new String[]{currFilePath}, new String[]{"image/*"},
    new MediaScannerConnection.OnScanCompletedListener() {
    public void onScanCompleted(String path, Uri uri) {
    context.sendBroadcast(new Intent(android.hardware.Camera.ACTION_NEW_PICTURE, uri));
    context.sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", uri));
    }
    });
    scanPhotos(currFilePath);
    } else {
    context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + currFilePath)));
    }
    }

    /**
    * 转换ImageView为Bitmap.
    *
    * @param view 需要转换的View,通常是ImageView或其它类似的类.
    * @return 转换后的Bitmap.
    */
    public Bitmap convertViewToBitmap(View view) {
    view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
    view.buildDrawingCache();
    Bitmap bitmap = view.getDrawingCache();
    return bitmap;
    }

    /**
    *

    保存图片到本地
    *

    *

    需要权限:

    * {@code
    *
    *
    * }
    *
    * @param imgView 要保存的ImageView对象.
    * @throws IOException 写文件时导致的异常.
    * @throws FileNotFoundException 文件未找到时,或路径不存在时抛出此异常.
    * @see {@link com.w1520.liangye.utils.NetworkUtils#saveImages(Bitmap bitmap)}
    */
    public void saveImages(ImageView imgView) {
    Bitmap bitmap = convertViewToBitmap(imgView);
    String strPath = getSDPath();
    DateUtils dateutils = DateUtils.getInstance();
    String strFileName = dateutils.getCurrentTimeById() + ".jpg";
    FileOutputStream fos = null;
    try {
    File destDir = new File(strPath);
    if (!destDir.exists()) {
    destDir.mkdirs();
    }
    final String filePaths = strPath + "/" + strFileName;
    File imageFile = new File(filePaths);
    imageFile.createNewFile();
    fos = new FileOutputStream(imageFile);
    bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
    fos.flush();
    showToast("已成功保存到相册", Toast.LENGTH_SHORT);
    refreshPicture(filePaths);
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    try {
    fos.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    /**
    * 保存图片到本地
    *

    *

    需要权限:

    {@code
    *
    * }
    *
    * @param bitmap 要保存的图片.
    * @throws IOException 写文件时导致的异常.
    * @throws FileNotFoundException 文件未找到时,或路径不存在时抛出此异常.
    * @see {@link com.w1520.liangye.utils.NetworkUtils#saveImages(ImageView imgView)}
    */
    public void saveImages(Bitmap bitmap) {
    String strPath = getSDPath();
    DateUtils dateutils = DateUtils.getInstance();
    String strFileName = dateutils.getCurrentTimeById() + ".jpg";
    FileOutputStream fos = null;
    try {
    File destDir = new File(strPath);
    if (!destDir.exists()) {
    destDir.mkdirs();
    }
    final String filePaths = strPath + "/" + strFileName;
    File imageFile = new File(filePaths);
    imageFile.createNewFile();
    fos = new FileOutputStream(imageFile);
    bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
    fos.flush();
    showToast("已成功保存到相册", Toast.LENGTH_SHORT);
    refreshPicture(filePaths);
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    try {
    fos.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    /**
    * 图片按比例大小压缩方法
    *
    * @param image
    * @return
    * @see {@link http://104zz.iteye.com/blog/1694762 }
    */
    public Bitmap comp(Bitmap image) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    if (baos.toByteArray().length / 1024 > 1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出
    baos.reset();//重置baos即清空baos
    image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中
    }
    ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
    BitmapFactory.Options newOpts = new BitmapFactory.Options();
    //开始读入图片,此时把options.inJustDecodeBounds 设回true了
    newOpts.inJustDecodeBounds = true;
    Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
    newOpts.inJustDecodeBounds = false;
    int w = newOpts.outWidth;
    int h = newOpts.outHeight;
    //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
    float hh = 800f;//这里设置高度为800f
    float ww = 480f;//这里设置宽度为480f
    //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
    int be = 1;//be=1表示不缩放
    if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放
    be = (int) (newOpts.outWidth / ww);
    } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放
    be = (int) (newOpts.outHeight / hh);
    }
    if (be <= 0) be = 1; newOpts.inSampleSize = be;//设置缩放比例 //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了 isBm = new ByteArrayInputStream(baos.toByteArray()); bitmap = BitmapFactory.decodeStream(isBm, null, newOpts); return compressImage(bitmap);//压缩好比例大小后再进行质量压缩 } /** * 质量压缩 * * @param image * @return */ private Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 int options = 100; while (baos.toByteArray().length / 1024 > 100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
    baos.reset();//重置baos即清空baos
    image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
    options -= 10;//每次都减少10
    }
    ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
    Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
    return bitmap;
    }

    /**
    * 复制文本
    *
    * @param label 标记.
    * @param data 要被复制的文本.
    */
    public void copyText(String label, String data) {
    ClipboardManager clipManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
    ClipData clip = ClipData.newPlainText(label, data);
    clipManager.setPrimaryClip(clip);
    showToast("复制成功", Toast.LENGTH_SHORT);
    }

    /**
    * 判断email格式是否正确
    *
    * @param email
    * @return
    */
    public boolean isEmail(String email) {
    String str = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$";
    Pattern p = Pattern.compile(str);
    Matcher m = p.matcher(email);
    return m.matches();
    }

    }

    可能是因为代码的原因,好久都没”写”这么长的文了…偶然有点不习惯……

    嗯,没错–你的人生永远不会辜负你的。那些转错的弯,那些走错的路,那些流下的泪水,那些滴下的汗水,那些留下的伤痕,全都让你成为独一无二的自己。by 朱学恒

    Python:操作MongoDB工具实例

    前提

    Python 连接MongoDB.
    目前实现:
    增删改查(大部分已经实例运行过).
    对于聚合函数的实现并没有细化(没有提供相关示例).
    官方提供了更多的方法,目前只集成了常用的部分函数(其余可以去官方网站参考).

    python Logo
    image-2366

    源码

    代码已经托管在Github[访问].

    PS:2015.12.19 更新//修复数据库表创建的问题//修复数据库集合的使用问题,下面的源码已经替换为最新.
    PS:2015.12.28 更新//修复查询分页的问题.
    PS:2016.1.6 更新//优化查询方式,简化原有代码结构.此处使用了find()方法.原有方式不再使用.但兼容原有数据.并增加一项功能:设定返回列,或排除返回列.
    (文件名:DaoUtil.py)


    #!/usr/bin/env python3
    # !-*-coding=utf-8-*-

    """

    Python 连接MongoDB.

    目前实现:
    增删改查(大部分已经实例运行过).
    对于聚合函数的实现并没有细化(没有提供相关示例).
    官方提供了更多的方法,目前只集成了常用的部分函数(其余可以去官方网站参考).

    依赖:
    Python3+
    MongoDB3+
    pymongo3+

    参考:
    MongoDB官方文档:
    https://docs.mongodb.org/getting-started/python/

    pymongo下载提示页面:
    https://pypi.python.org/pypi/pymongo/

    相关运行信息:
    运行平台:Linux
    MongoDB版本:3.03
    Python版本:Python 3.4.3
    pymongo(Python连接MongoDB的驱动)版本:3.2

    author : prd
    version : 0.9 2015.12.11
    2015.12.18 修复几个问题:
    修改原有的获取数据库名称方式不正确.
    修改原有获取数据库集合方式的问题.
    2015.12.28 修复一个问题:
    在查询时,进行分页之前的方式,无法进行正常分页.
    之前的错误原因(现在已经修复):满足其一条件将不再往下执行,现在已修改方式.
    举例:
    错误的:
    if page:
    pass
    if pageSize:
    pass
    if page and pageSize:
    pass
    # 若传递page和pageSize也无法正常获取数据.因为判断完第一个将不再继续执行.

    正确的:
    if page and pageSize:
    pass
    if page:
    pass
    if pageSize:
    pass
    2016.1.6 更新:
    优化查询方式,简化原有代码结构.此处使用了find()方法.原有方式不再使用.但兼容原有数据.
    并增加一项功能:设定返回列,或排除返回列.
    更多细节:http://api.mongodb.org/python/current/api/pymongo/collection.html?highlight=find#pymongo.collection.Collection.find
    2016.1.10 v2.0.0 更新:
    为更新操作,新增参数(可选,若文档不存在时,是否进行新增).
    查询时的参数:dataLimit,修改默认为0(之前默认为None会导致错误!).

    email : pruidong#gmail.com

    """
    import pymongo

    class PyDaoUtil(object):
    dbname = None
    collection = None

    def __init__(self, dbname, collection):
    self.dbname = dbname
    self.collection = collection

    """ 返回MongoDB客户端对象. """

    def getClient(self):
    return pymongo.MongoClient("localhost", 27017)

    """ 返回MongoDB的一个数据库 """

    def getDB(self):
    client = self.getClient()
    # 修改原有的获取数据库名称方式不正确. TODO 2015.12.18
    dbdatabase = client['%s' % (self.dbname)]
    return dbdatabase

    """
    添加数据

    可新增单条或者多条.

    单条:json对象,
    多条:json数组.

    """

    def insertData(self, bsonData):
    if self.dbname:
    db = self.getDB()
    collections = self.collection
    if isinstance(bsonData, list):
    # 修改原有获取数据库集合方式的问题. TODO 2015.12.18 get_collection(collections)
    result = db.get_collection(collections).insert_many(bsonData)
    return result.inserted_ids
    return db.get_collection(collections).insert_one(bsonData).inserted_id
    else:
    return None

    """
    删除数据!!!!

    关键字参数:
    oneDeleteFilter->单条删除
    manyDeleteFilter->多条删除

    """

    def deleteData(self, **kwargs):
    if self.dbname:
    collections = self.collection
    db = self.getDB()

    def deleteOne(self, oneDeleteFilter=None): # 单个删除
    result = db.get_collection(collections).delete_one(oneDeleteFilter)
    return result.deleted_count

    def deleteMany(self, manyDeleteFilter=None): # 全部删除
    result = db.get_collection(collections).delete_many(manyDeleteFilter)
    return result.deleted_count

    onedel = kwargs.get("oneDeleteFilter", "")
    manydel = kwargs.get("manyDeleteFilter", "")
    if onedel:
    return deleteOne(self, **kwargs)
    elif manydel:
    return deleteMany(self, **kwargs)

    """
    更新数据

    oldData:过滤原有数据,

    关键字参数:

    oneUpdate->单条更新
    oneUpsert->True:如果文档不存在,执行插入.
    (默认)False:如果文档不存在不执行执行操作.
    manyUpdate->多条更新(如果过滤条件结果存在多条)
    manUpsert->True:如果文档不存在,执行插入.
    (默认)False:如果文档不存在不执行执行操作.

    """

    def updateData(self, oldData=None, **kwargs):
    if self.dbname:
    collections = self.collection
    db = self.getDB()

    def updateOne(self, oneOldData=None, oneUpdate=None,oneUpsert=False): # 单个更新
    result = db.get_collection(collections).update_one(filter=oneOldData,update=oneUpdate,upsert=oneUpsert)
    return result.matched_count

    def updateMany(self, manyOldData, manyUpdate=None,manUpsert=False): # 全部更新
    result = db.get_collection(collections).update_many(filter=manyOldData,update=manyUpdate,upsert=manUpsert)
    return result.matched_count

    if oldData:
    oneup = kwargs.get("oneUpdate", "")
    manyup = kwargs.get("manyUpdate", "")
    if oneup:
    return updateOne(self, oldData, **kwargs)
    elif manyup:
    return updateMany(self, oldData, **kwargs)

    """
    查询数据

    关键字参数:

    dataLimit - > 限定最多返回多少条.
    dataSkip - > 跳过多少条.
    dataQuery - > 查询限定条件
    dataSortQuery -> 排序条件
    dataProjection -> 返回指定的列或者排除指定的列

    ----------------------------------------------------------------

    oneDataQuery -> 查询单条条件 -- 弃用!!!!

    """

    def findAll(self, **kwargs):
    if self.dbname:
    collections = self.collection
    db = self.getDB()

    def findAllDataQuery(self, dataLimit=0, dataSkip=0, dataQuery=None, dataSortQuery=None,
    dataProjection=None):
    '''
    TODO :
    2016.1.6 更新:
    优化查询方式,简化原有代码结构.此处使用了find()方法.原有方式不再使用.但兼容原有数据.
    并增加一项功能:设定返回列,或排除返回列.
    更多细节:http://api.mongodb.org/python/current/api/pymongo/collection.html?highlight=find#pymongo.collection.Collection.find
    '''
    return db.get_collection(collections).find(filter=dataQuery, projection=dataProjection, skip=dataSkip,
    limit=dataLimit, sort=dataSortQuery)

    return findAllDataQuery(self, **kwargs)

    """
    聚合函数.

    具体参考:
    https://docs.mongodb.org/manual/meta/aggregation-quick-reference/

    """

    def aggregation(self, aggreg):
    if self.dbname:
    collections = self.collection
    db = self.getDB()
    return db.get_collection(collections).aggregate(aggreg)

    """
    统计数据库中的数量,

    关键字参数参考:
    http://api.mongodb.org/python/current/api
    /pymongo/collection.html
    ?_ga=1.21101243.1811534224.1449817089#pymongo.collection.Collection.count

    """

    def countData(self, countQuery=None, **kwargs):
    if self.dbname:
    collections = self.collection
    db = self.getDB()
    if countQuery and kwargs:
    return db.get_collection(collections).count(countQuery, kwargs)
    elif countQuery:
    return db.get_collection(collections).count(countQuery)
    elif kwargs:
    return db.get_collection(collections).count(filter=None, **kwargs)
    else:
    return db.get_collection(collections).count()

    """ 删除集合中所有数据!!!!谨慎调用!!!! """

    def dropAllData(self, dataPassword=None):
    if self.dbname:
    collections = self.collection
    db = self.getDB()
    if dataPassword and isinstance(dataPassword, list):
    db.get_collection(collections).drop()

    if __name__ == '__main__':
    # dao = PyDaoUtil("test", "my_collection")
    # # 新增数据.
    # dao.insertData(
    # {
    # "title": "MongoDB Overview",
    # "description": "MongoDB is no sql database",
    # "by": "百度一下你就知道",
    # "url": "http://www.baidu.com",
    # "tags": [
    # "mongodb",
    # "database",
    # "NoSQL"
    # ],
    # "likes": 100
    # })#单个新增.

    # print(dao.insertData([{"a": "67"}, {"a": "67"}, {"x": "5"}])) # 批量新增.

    # 新增数据.END.

    # 查询数据

    # print(dao.findAll())
    # print(dao.findAll(dataLimit=2, dataSkip=1))
    # print(dao.findAll(dataSkip=1))
    # print(dao.findAll(dataLimit=2))
    # print(dao.findAll(oneDataQuery={"x": "67"}))
    # print(dao.findAll(dataSortQuery=[("x", "55")]))
    # print(dao.findAll(dataQuery={"x": "55"}, dataSortQuery=[("x", "55")]))

    # 查询数据END.

    # 删除数据

    # print(dao.deleteData(manyDeleteFilter={"a": "10000"}))
    # print(dao.deleteData(oneDeleteFilter={"a": "10000"}))

    # 删除数据END.

    # 更新数据

    # print(dao.insertData([{"a": "67"}, {"a": "67"}, {"x": "5"}])) # 批量新增.
    # print(dao.updateData(oldData={"x": "5"}, oneUpdate={'$set': {"x": "300"}}))
    # print(dao.updateData(oldData={"a": "67"}, manyUpdate={'$set': {"a": "10000"}}))

    # 更新数据END.

    # 清除所有数据[不限定条件,直接删除]
    """

    警告:会删除所有数据,请谨慎调用!!!!!!!!!!!!!!!!!!

    """
    # 清除所有数据END.
    # dao.dropAllData(dataPassword=[123])
    # 查询显示所有数据.
    # print(dao.insertData([{"测试": "测试"}, {"a": "67"}, {"x": "5"}]))

    # print(dao.findAll())
    # 获得数据库中一共有多少条数据.
    # print(dao.countData())
    # print(dao.countData(countQuery={"a":"10000"}))

    Python:关键字参数的使用

    起源

    下午在写一个Python连接MongoDB的工具(使用pymongo 3.2).并且使用Python 3.4.但是遇到一个问题,就是调用的时候,发现报错.

    报错的程序如下:


    def findAll(self, **kwargs):
    print(kwargs)
    limit = kwargs.get('limit', None)
    skip = kwargs.get('skip', None)
    query = kwargs.get('query', None)
    sortQuery = kwargs.get('sortQuery', None)
    if self.dbname:
    collections = self.collection
    db = self.getDB()
    print(limit, skip)
    resultData = None

    def findPage(self, limit, skip): # 分页查询数据
    print(limit, skip)
    if isinstance(limit, int) and isinstance(skip, int):
    resultData = db.collections.find().limit(limit).skip(skip)
    else:
    def findAllData(self, query, sortQuery): # 查询数据
    print(self.dbname + "----" + self.collection)
    resultData = db.collections.find(query).sort(sortQuery)

    return resultData

    调用:


    print(dao.findAll(limit=2, skip=1))
    print(dao.findAll(query={"x": "55"}))
    print(dao.findAll(query={"x": "55"}, sortQuery={"x": "55"}))

    报错:

    Traceback (most recent call last):
    File “DaoUtil.py”, line 84, in
    print([item for item in dao.findAll(limit=2, skip=1)])
    TypeError: ‘NoneType’ object is not iterable

    写这段程序的时候,参照了网上一个例子,但是忽略了一个重要的部分{也是最重要的部分}.所以就报错了.

    python Logo
    image-2360

    修改后的程序

    下面的程序是最新的,可以运行的!


    """ 查询数据 """

    def findAll(self, **kwargs):
    # print(kwargs)
    if self.dbname:
    collections = self.collection
    db = self.getDB()

    def findPage(self, dataLimit=None, dataSkip=None): # 分页查询数据
    if dataLimit and isinstance(dataLimit, int): # limit 最多只显示多少.
    return db.collections.find().limit(dataLimit)

    if dataSkip and isinstance(dataSkip, int): # 跳过多少条
    return db.collections.find().limit(10).skip(dataSkip)

    if isinstance(dataLimit, int) and isinstance(dataSkip, int):
    return db.collections.find().limit(dataLimit).skip(dataSkip)

    def findAllData(self, dataQuery=None, dataSortQuery=None): # 查询数据
    if dataQuery:
    return db.collections.find(dataQuery)
    else:
    return db.collections.find()

    if dataSortQuery:
    return db.collections.find().sort(dataSortQuery)
    if dataQuery and dataSortQuery:
    return db.collections.find(dataQuery).sort(dataSortQuery)

    if len(kwargs) == 2:
    limit = kwargs.get('dataLimit', "")
    skip = kwargs.get('dataSkip', "")
    query = kwargs.get('dataQuery', "")
    sortQuery = kwargs.get('dataSortQuery', "")
    if limit or skip:
    return findPage(self, **kwargs)
    elif query or sortQuery:
    return findAllData(self, **kwargs)
    else:
    return findAllData(self, "", "")

    调用如下:


    print([item for item in dao.findAll()])
    print([item for item in dao.findAll(dataLimit=2, dataSkip=1)])
    print([item for item in dao.findAll( dataSkip=1)])
    print([item for item in dao.findAll(dataLimit=2)])
    print([item for item in dao.findAll(dataQuery={"x": "55"})])
    print([item for item in dao.findAll(dataSortQuery=[("x", "55")])])
    print([item for item in dao.findAll(dataQuery={"x": "55"}, dataSortQuery=[("x", "55")])])

    输出MongoDB里面的数据.

    最重要,工具目前暂未完成,剩余部分暂时不发了.等写完了再发整个工具.(不会安装MongoDB?自己搜索下.)

    PS:工具已经完整发布(地址).