仿QQ android 实战(学习 android 先来个QQ)
实战 QQ demo (学习 android 先来个QQ)
源码
服务器端下载:http://download.csdn.net/download/knight_black_bob/9822551
android eclipse 版:http://download.csdn.net/download/knight_black_bob/9822553
android stdio 版本:http://download.csdn.net/download/knight_black_bob/9822556
ps :该demo 中的 .9.png 为盗图,在as中不可使,在eclispe 中可以
先看效果图
android 框架
xlistview swipe badgeview slidemenu scrollerview
volley
xutils3.0
eventbus
mob-sharesdk
baidupush
java
webservice+restful
bccs-api(百度推送)
数据库设计
用户表
CREATE TABLE `t_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `nick_name` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `mail` varchar(255) DEFAULT NULL, `telphone` varchar(255) DEFAULT NULL, `photo` varchar(255) DEFAULT NULL, `insert_time` timestamp NULL DEFAULT NULL, `last_update_time` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1403271249 DEFAULT CHARSET=utf8
用户关系表
CREATE TABLE `t_friendship` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_src` bigint(20) DEFAULT NULL, `parent_id` bigint(20) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, `user_id` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8
图床表
CREATE TABLE `picrepository` ( `id` int(10) NOT NULL AUTO_INCREMENT, `origin_name` varchar(255) DEFAULT NULL, `path` varchar(255) DEFAULT NULL, `user_id` int(10) DEFAULT NULL, `link` varchar(255) DEFAULT NULL, `insert_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `last_update_time` timestamp NULL DEFAULT NULL, `description` varchar(255) DEFAULT NULL, `nextfix` varchar(255) DEFAULT NULL, `pic_size` int(10) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=10002 DEFAULT CHARSET=utf8
朋友圈
CREATE TABLE `t_feed` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_from` bigint(20) DEFAULT NULL, `user_icon` varchar(255) DEFAULT NULL, `user_nickname` varchar(30) DEFAULT NULL, `clicknum` int(10) DEFAULT NULL, `location` varchar(200) DEFAULT NULL, `content` varchar(500) DEFAULT NULL, `insert_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `last_update_time` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8
朋友圈评论
CREATE TABLE `t_feed_comment` ( `id` int(11) NOT NULL AUTO_INCREMENT, `feed_id` int(11) DEFAULT NULL, `user_a_id` bigint(20) DEFAULT NULL, `user_a_nickname` varchar(255) DEFAULT NULL, `user_b_id` bigint(20) DEFAULT NULL, `user_b_nickname` varchar(255) DEFAULT NULL, `parent_id` int(11) DEFAULT '0', `content` varchar(255) DEFAULT NULL, `insert_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `last_update_time` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
朋友圈图片
CREATE TABLE `t_feed_pic` ( `id` int(11) NOT NULL AUTO_INCREMENT, `feed_id` int(11) DEFAULT NULL, `pic_name` varchar(255) DEFAULT NULL, `pic_link` varchar(255) DEFAULT NULL, `insert_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `last_update_time` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8
朋友圈点赞
CREATE TABLE `t_feed_zan` ( `id` int(11) NOT NULL AUTO_INCREMENT, `feed_id` int(11) DEFAULT NULL, `user_id` bigint(20) DEFAULT NULL, `user_nickname` varchar(255) DEFAULT NULL, `insert_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `last_update_time` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
聊天记录
CREATE TABLE `t_chatmessage` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `content` varchar(255) DEFAULT NULL, `user_src` bigint(20) DEFAULT NULL, `user_from` bigint(20) DEFAULT NULL, `user_to` bigint(20) DEFAULT NULL, `insert_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `last_update_time` timestamp NULL DEFAULT NULL, `new_flag` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
java 图床设计接口
@Path(value = "/showPic") @Produces({ MediaType.APPLICATION_XML}) @Consumes({ MediaType.APPLICATION_XML}) @Component public class ShowPicRestService { // http://192.168.1.102:8080/RepositoryPic/WebService/rest/showPic/users/1401155710/pics/1.png @GET @Produces("image/*") @Path("/users/{userId}/pics/{picName}") public Response getInputStream(@PathParam("userId")int userId,@PathParam("picName")String picName, @Context HttpServletRequest request) throws Exception { HttpSession session = request.getSession(); ServletContext application = session.getServletContext(); String serverRealPath = application.getRealPath("/") ; String path = (serverRealPath +"img/"+userId+".png"); File file = new File(path); return Response.ok(file,"image/jpg").build(); } @GET @Produces("image/*") @Path("/feed/{picSaveName}") public Response getInputStream(@PathParam("picSaveName")String picSaveName, @Context HttpServletRequest request) throws Exception { HttpSession session = request.getSession(); ServletContext application = session.getServletContext(); String serverRealPath = application.getRealPath("/") ; String path = (serverRealPath +"img/feed/"+picSaveName+".png"); File file = new File(path); return Response.ok(file,"image/jpg").build(); } }
好友关系
@Path(value = "/friendShip") @Produces({ MediaType.APPLICATION_JSON}) @Consumes({ MediaType.APPLICATION_JSON}) @Component public class FriendShipRestService { @Resource FriendShipSoapService friendShipSoapService; @Resource UserSoapService userSoapService; @GET @Produces({ MediaType.APPLICATION_JSON}) @Path("/friendShips/{userId}") public Response getFriendShips(@PathParam("userId") Long userId) { List<FriendShip> fses = friendShipSoapService.getAllByUserId(userId); List<User> userList = new ArrayList<User>(); for (FriendShip fs:fses) { if (fs.getUserId() != 0) { userList.add(userSoapService.get(fs.getUserId())); } } QueryResultJson result=new QueryResultJson(400,"error",null); if (null != fses && fses.size()>0 ) { result= new QueryResultJson(); result.retcode = 200; result.retmsg ="success"; FriendShipUserList list = new FriendShipUserList(fses,userList); result.retdata = list; } return Response .ok(result) .build(); } public static class FriendShipUserList{ public List<FriendShip> friendShipList; public List<User> userList; public FriendShipUserList(List<FriendShip> fses, List<User> userList) { this.friendShipList = fses; this.userList = userList; } } }
朋友圈 详细
@Path(value = "/feedAll") @Produces({ MediaType.APPLICATION_JSON}) @Consumes({ MediaType.APPLICATION_JSON}) @Component public class FeedAllRestService { @Resource FeedAllService feedAllService; @Resource FeedCommentDao feedCommentDao; @Resource FeedZanDao feedZanDao; @Resource FeedDao feedDao; @Resource FeedPicDao feedPicDao; @GET @Produces({ MediaType.APPLICATION_JSON}) @Path("/feedAlls/user/{userId}/start/{start}") public Response getFriendShips(@PathParam("userId") Long userId ,@PathParam("start") int start ) { List<FeedAll> list = feedAllService.getAlls(userId, start, Constants.defaultPageNum); QueryResultJson result=new QueryResultJson(400,"error",null); if (null != list && list.size()>0 ) { result= new QueryResultJson(); result.retcode = 200; result.retmsg ="success"; result.retdata = list; } else{ result= new QueryResultJson(); result.retcode = 400; result.retmsg ="no data"; result.retdata = list; } return Response .ok(result) .build(); } @POST @Consumes({ MediaType.APPLICATION_JSON}) @Path("/feedCommemtAdd") public Response feedCommemtAdd(@RequestBody FeedComment feedc ) { int saveFeedComment = feedCommentDao.saveFeedComment(feedc); QueryResultJson result = new QueryResultJson(400, "error", null); if (saveFeedComment == 1) { result = new QueryResultJson(); result.retcode = 200; result.retmsg = "success"; } return Response.ok(result).build(); } @POST @Consumes({ MediaType.APPLICATION_JSON}) @Path("/feedZanAdd") public Response feedZanAdd(@RequestBody FeedZan feedz ) { FeedZan feedZan = feedZanDao.getFeedZan(feedz.getFeedId(), feedz.getUserId()); if (feedZan != null) { feedZanDao.deleteFeedZan(feedz.getFeedId(), feedz.getUserId()); } else{ feedZanDao.saveFeedZan(feedz); } QueryResultJson result = new QueryResultJson(400, "error", null); result = new QueryResultJson(); result.retcode = 200; result.retmsg = "success"; return Response.ok(result).build(); } @POST @Consumes("multipart/form-data") @Path("/createFeed") public Response createFeed( List<Attachment>attachments,@Context HttpServletRequest request ) { long userFrom = 0; String userNickname = null,userIcon = null,location = null,content = null; List<FileKv> fileList = new ArrayList<FileKv>(); for (Attachment attach : attachments) { DataHandler dh = attach.getDataHandler(); System.out.println(attach.getContentType().toString()); // text/plain;charset=UTF-8 if (attach.getContentType().toString().contains("text/plain")) { try { System.out.println(dh.getName()); System.out.println(writeToString(dh.getInputStream())); if ("userFrom".equals(dh.getName())) { userFrom = Long.valueOf( writeToString(dh.getInputStream())); }else if("userNickname".equals(dh.getName())) { userNickname = writeToString(dh.getInputStream()) ; }else if("userIcon".equals(dh.getName())) { userIcon = writeToString(dh.getInputStream()) ; }else if("location".equals(dh.getName())) { location = writeToString(dh.getInputStream()) ; }else if("content".equals(dh.getName())) { content = writeToString(dh.getInputStream()) ; } } catch (Exception e) { e.printStackTrace(); } } else if(attach.getContentType().toString().contains("application/octet-stream")){ String path = request.getRealPath("/"); String picSaveName = DatetimeUtil.getUniqueTimestamp()+""; try { writeToFile(dh.getInputStream(), path +"img/feed/"+ picSaveName+".png"); fileList.add(new FileKv(dh.getName(),picSaveName)); } catch (IOException e) { e.printStackTrace(); } }else{ } } Feed feed = new Feed(); feed.setUserFrom(userFrom); feed.setUserNickname( userNickname ); feed.setUserIcon( userIcon ); feed.setLocation( location ); feed.setContent( content ); feed.setClicknum(0); feed.setInsertTime(new Date()); feed.setLastUpdateTime(new Date()); int feedId = feedDao.saveFeedReturnFeedId(feed); for (FileKv kv : fileList) { feedPicDao.saveFeedPic(new FeedPic(feedId, kv.fileName, Constants.BASEIP+"/RepositoryPic/WebService/rest/showPic/feed/"+kv.fileSaveName, new Date(), new Date() )); } QueryResultJson result = new QueryResultJson(400, "error", null); result = new QueryResultJson(); result.retcode = 200; result.retmsg = "success"; return Response.ok(result).build(); } private void writeToFile(InputStream ins, String path) { try { OutputStream out = new FileOutputStream(new File(path)); int read = 0; byte[] bytes = new byte[1024]; while ((read = ins.read(bytes)) != -1) { out.write(bytes, 0, read); } out.flush(); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private String writeToString(InputStream ins) throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] b = new byte[1024]; int i = -1; while ((i = ins.read(b)) != -1) { out.write(b, 0, i); } ins.close(); return new String(out.toByteArray(), "UTF-8"); } public class FileKv{ public String fileName; public String fileSaveName; public FileKv(String fileName, String fileSaveName) { this.fileName = fileName; this.fileSaveName = fileSaveName; } } }
java 百度推送后台 推送 (用户 标签绑定)
@Path(value = "/pushService") @Produces({ MediaType.APPLICATION_JSON}) @Consumes({ MediaType.APPLICATION_JSON}) @Component public class BaidupushRestService { // http://192.168.1.102:8080/RepositoryPic/WebService/rest/pushService/addDevicesToTag @GET @Consumes({ MediaType.APPLICATION_JSON}) @Path("/addDevicesToTag") public Response addDevicesToTag( @QueryParam("tag") String tag ,@QueryParam("channelId") String channelId) { System.out.println("============="+tag+"-------------------"+channelId); QueryResultJson result=new QueryResultJson(400,"error",null); if ( !StringUtils.isEmpty(tag) &&!StringUtils.isEmpty(channelId)) { boolean addDevicesToTag =true; try { boolean queryTag = BaiduPushServerApi.queryTag(tag); boolean createTag = true; if(!queryTag){ createTag = BaiduPushServerApi.createTag(tag); } if (createTag) { addDevicesToTag = BaiduPushServerApi.AddDevicesToTag(tag, channelId); } } catch (Exception e) { e.printStackTrace(); } result = new QueryResultJson(200,"success",addDevicesToTag); } return Response.ok(result).build(); } // http://192.168.1.102:8080/RepositoryPic/WebService/rest/pushService/sendMessageToTag @POST @Consumes({ MediaType.APPLICATION_JSON}) @Path("/sendMessageToTag") public Response sendMessageToTag(@RequestBody TagMessage tagMessage ) { System.out.println("====="+tagMessage.toString()); QueryResultJson result=new QueryResultJson(400,"error",null); if ( tagMessage != null) { BaiduPushServerApi.sendAndroidMessageFromTag( tagMessage.title,tagMessage.content, tagMessage.userFrom, tagMessage.userTo); result = new QueryResultJson(200,"success",true); } return Response.ok(result).build(); } } class TagMessage{ public String title; public String content; public long userFrom; public long userTo; @Override public String toString() { return "TagMessage [title=" + title + ", content=" + content + ", userFrom=" + userFrom + ", userTo=" + userTo + "]"; } }
android 依赖 jar 和 框架依赖
android 百度推送 接收端
package com.curiousby.fitnessandappointment.quote.baidupush; import java.util.Date; import java.util.List; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import android.text.TextUtils; import android.util.Log; import com.baidu.android.pushservice.PushMessageReceiver; import com.curiousby.fitnessandappointment.MyApplication; import com.curiousby.fitnessandappointment.entity.ChatMessageEntity; import com.curiousby.fitnessandappointment.entity.CommonMessages; import com.curiousby.fitnessandappointment.entity.UserEntity; import com.curiousby.fitnessandappointment.request.db.ChatMessageDB; import com.curiousby.fitnessandappointment.request.db.CommonMsgDB; import com.curiousby.fitnessandappointment.request.db.UserDB; import com.curiousby.fitnessandappointment.request.dbmanager.ChatMessageDBManager; import com.curiousby.fitnessandappointment.request.dbmanager.CommonMessagesDBManager; import com.curiousby.fitnessandappointment.utils.JsonParser; import com.curiousby.fitnessandappointment.utils.PrefInfoUtils; import com.curiousby.fitnessandappointment.utils.RequestUtil; public class BaiduPushReceiver extends PushMessageReceiver{ private UserEntity user ; private UserDB userDB; private ChatMessageDB chatMessageDB; private CommonMsgDB commonMsgDB; /** TAG to Log */ public static final String TAG = BaiduPushReceiver.class .getSimpleName(); /** * 调用PushManager.startWork后,sdk将对push * server发起绑定请求,这个过程是异步的。绑定请求的结果通过onBind返回。 如果您需要用单播推送,需要把这里获取的channel * id和user id上传到应用server中,再调用server接口用channel id和user id给单个手机或者用户推送。 * * @param context * BroadcastReceiver的执行Context * @param errorCode * 绑定接口返回值,0 - 成功 * @param appid * 应用id。errorCode非0时为null * @param userId * 应用user id。errorCode非0时为null * @param channelId * 应用channel id。errorCode非0时为null * @param requestId * 向服务端发起的请求id。在追查问题时有用; * @return none */ @Override public void onBind(Context context, int errorCode, String appid, String userId, String channelId, String requestId) { String responseString = "onBind errorCode=" + errorCode + " appid=" + appid + " userId=" + userId + " channelId=" + channelId + " requestId=" + requestId; System.out.println("baoyou -=========== "+responseString); Log.d(TAG, responseString); final String myChannelId = channelId; if (errorCode == 0) { // 绑定成功 if (channelId != null) { String userInfo = PrefInfoUtils.getUserInfo(MyApplication.newInstance()); user = JsonParser.parseDateJson(userInfo, UserEntity.class); Log.d(TAG, "========"+user.getId()+"========"+ channelId); try { //BaiduPushServerApi.AddDevicesToTag( user.getId()+"", channelId); RequestUtil.addDeviceToTag(user.getId()+"", myChannelId); } catch (Exception e) { e.printStackTrace(); } } } // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑 // updateContent(context, responseString); } /** * 接收透传消息的函数。 * * @param context * 上下文 * @param message * 推送的消息 * @param customContentString * 自定义内容,为空或者json字符串 */ @Override public void onMessage(Context context, String message, String customContentString) { String messageString = "透传消息 message=\"" + message + "\" customContentString=" + customContentString; Log.d(TAG, messageString); // Toast.makeText(MyApplication.newInstance(), messageString , Toast.LENGTH_SHORT).show(); if(!TextUtils.isEmpty(message)){ JSONObject msg = null; try { msg = new JSONObject(message); String title = null; String content = null; long userFrom = 0; long userTo = 0; if (!msg.isNull("title")) { title = msg.getString("title"); } if (!msg.isNull("content")) { content = msg.getString("content"); } if (!msg.isNull("userFrom")) { userFrom = msg.getLong("userFrom"); } if (!msg.isNull("userTo")) { userTo = msg.getLong("userTo"); } String userInfo = PrefInfoUtils.getUserInfo(MyApplication.newInstance()); user = JsonParser.parseDateJson(userInfo, UserEntity.class); chatMessageDB = new ChatMessageDB(MyApplication.newInstance(), user.getId()+""); ChatMessageEntity lastEntity = chatMessageDB.getLastEntity(); long id = 0; if (lastEntity!= null ) { id = lastEntity.getId() + 1; } if (user.getId() == userTo) { userDB = new UserDB(MyApplication.newInstance(), user.getId()+""); commonMsgDB = new CommonMsgDB(MyApplication.newInstance(), user.getId()+""); UserEntity entityById = userDB.getEntityById(userFrom+""); CommonMessages commonMessages = new CommonMessages(userFrom, entityById.getNickName(), new Date(), content, entityById.getPhoto(),1); commonMsgDB.saveOrUpdate(commonMessages ); CommonMessagesDBManager.recieveOneMessage(); ChatMessageEntity item = new ChatMessageEntity(id, content, user.getId(), userFrom, userTo, new Date(), new Date(), 0); chatMessageDB.saveOrUpdate(item); ChatMessageDBManager.recieveChatMessage(item); // Toast.makeText(MyApplication.newInstance(), user.getId()+"----"+userFrom +"---"+userTo , Toast.LENGTH_LONG ).show(); } } catch (JSONException e) { e.printStackTrace(); } } // 自定义内容获取方式,mykey和myvalue对应透传消息推送时自定义内容中设置的键和值 if (!TextUtils.isEmpty(customContentString)) { JSONObject customJson = null; try { customJson = new JSONObject(customContentString); String myvalue = null; if (!customJson.isNull("tag")) { myvalue = customJson.getString("mykey"); } } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑 // updateContent(context, messageString); } /** * 接收通知点击的函数。 * * @param context * 上下文 * @param title * 推送的通知的标题 * @param description * 推送的通知的描述 * @param customContentString * 自定义内容,为空或者json字符串 */ @Override public void onNotificationClicked(Context context, String title, String description, String customContentString) { String notifyString = "通知点击 title=\"" + title + "\" description=\"" + description + "\" customContent=" + customContentString; Log.d(TAG, notifyString); // 自定义内容获取方式,mykey和myvalue对应通知推送时自定义内容中设置的键和值 if (!TextUtils.isEmpty(customContentString)) { JSONObject customJson = null; try { customJson = new JSONObject(customContentString); String myvalue = null; if (!customJson.isNull("mykey")) { myvalue = customJson.getString("mykey"); } } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑 updateContent(context, notifyString); /* Intent intent = new Intent(); intent.setClass(context.getApplicationContext(), PushMessageDetailActivity.class); intent.putExtra("pushMsgId",Tools.splitStr(s1)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.getApplicationContext().startActivity(intent); */ } /** * 接收通知到达的函数。 * * @param context * 上下文 * @param title * 推送的通知的标题 * @param description * 推送的通知的描述 * @param customContentString * 自定义内容,为空或者json字符串 */ @Override public void onNotificationArrived(Context context, String title, String description, String customContentString) { String notifyString = "onNotificationArrived title=\"" + title + "\" description=\"" + description + "\" customContent=" + customContentString; Log.d(TAG, notifyString); // 自定义内容获取方式,mykey和myvalue对应通知推送时自定义内容中设置的键和值 if (!TextUtils.isEmpty(customContentString)) { JSONObject customJson = null; try { customJson = new JSONObject(customContentString); String myvalue = null; if (!customJson.isNull("mykey")) { myvalue = customJson.getString("mykey"); } } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑 // 你可以參考 onNotificationClicked中的提示从自定义内容获取具体值 updateContent(context, notifyString); } /** * setTags() 的回调函数。 * * @param context * 上下文 * @param errorCode * 错误码。0表示某些tag已经设置成功;非0表示所有tag的设置均失败。 * @param successTags * 设置成功的tag * @param failTags * 设置失败的tag * @param requestId * 分配给对云推送的请求的id */ @Override public void onSetTags(Context context, int errorCode, List<String> sucessTags, List<String> failTags, String requestId) { String responseString = "onSetTags errorCode=" + errorCode + " sucessTags=" + sucessTags + " failTags=" + failTags + " requestId=" + requestId; Log.d(TAG, responseString); // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑 updateContent(context, responseString); } /** * delTags() 的回调函数。 * * @param context * 上下文 * @param errorCode * 错误码。0表示某些tag已经删除成功;非0表示所有tag均删除失败。 * @param successTags * 成功删除的tag * @param failTags * 删除失败的tag * @param requestId * 分配给对云推送的请求的id */ @Override public void onDelTags(Context context, int errorCode, List<String> sucessTags, List<String> failTags, String requestId) { String responseString = "onDelTags errorCode=" + errorCode + " sucessTags=" + sucessTags + " failTags=" + failTags + " requestId=" + requestId; Log.d(TAG, responseString); // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑 updateContent(context, responseString); } /** * listTags() 的回调函数。 * * @param context * 上下文 * @param errorCode * 错误码。0表示列举tag成功;非0表示失败。 * @param tags * 当前应用设置的所有tag。 * @param requestId * 分配给对云推送的请求的id */ @Override public void onListTags(Context context, int errorCode, List<String> tags, String requestId) { String responseString = "onListTags errorCode=" + errorCode + " tags=" + tags; Log.d(TAG, responseString); // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑 updateContent(context, responseString); } /** * PushManager.stopWork() 的回调函数。 * * @param context * 上下文 * @param errorCode * 错误码。0表示从云推送解绑定成功;非0表示失败。 * @param requestId * 分配给对云推送的请求的id */ @Override public void onUnbind(Context context, int errorCode, String requestId) { String responseString = "onUnbind errorCode=" + errorCode + " requestId = " + requestId; Log.d(TAG, responseString); if (errorCode == 0) { // 解绑定成功 } // Demo更新界面展示代码,应用请在这里加入自己的处理逻辑 updateContent(context, responseString); } private void updateContent(Context context, String content) { } }
android 百度推送 用户绑定标签
package com.curiousby.fitnessandappointment.quote.baidupush; import java.util.List; import com.baidu.yun.core.log.YunLogEvent; import com.baidu.yun.core.log.YunLogHandler; import com.baidu.yun.push.auth.PushKeyPair; import com.baidu.yun.push.client.BaiduPushClient; import com.baidu.yun.push.constants.BaiduPushConstants; import com.baidu.yun.push.exception.PushClientException; import com.baidu.yun.push.exception.PushServerException; import com.baidu.yun.push.model.AddDevicesToTagRequest; import com.baidu.yun.push.model.AddDevicesToTagResponse; import com.baidu.yun.push.model.DeviceInfo; public class BaiduPushServerApi { public static void AddDevicesToTag(String tag,String channelId) throws Exception{ // 1. get apiKey and secretKey from developer console String apiKey = BaiduConstants.apiKey; String secretKey = BaiduConstants.secretKey; PushKeyPair pair = new PushKeyPair(apiKey, secretKey); // 2. build a BaidupushClient object to access released interfaces BaiduPushClient pushClient = new BaiduPushClient(pair, BaiduConstants.CHANNEL_REST_URL); // 3. register a YunLogHandler to get detail interacted information // in this request. pushClient.setChannelLogHandler(new YunLogHandler() { @Override public void onHandle(YunLogEvent event) { System.out.println(event.getMessage()); } }); try { // 4. specify request arguments String[] channelIds = {channelId} ; AddDevicesToTagRequest request = new AddDevicesToTagRequest() .addTagName(tag).addChannelIds(channelIds) .addDeviceType(3); // 5. http request AddDevicesToTagResponse response = pushClient .addDevicesToTag(request); // Http请求结果解析打印 if (null != response) { StringBuilder strBuilder = new StringBuilder(); strBuilder.append("devicesInTag:{"); List<?> devicesInfo = response.getDevicesInfoAfterAdded(); for (int i = 0; i < devicesInfo.size(); i++) { Object object = devicesInfo.get(i); if (i != 0) { strBuilder.append(","); } if (object instanceof DeviceInfo) { DeviceInfo deviceInfo = (DeviceInfo) object; strBuilder.append("{channelId:" + deviceInfo.getChannelId() + ",result:" + deviceInfo.getResult() + "}"); } } strBuilder.append("}"); System.out.println(strBuilder.toString()); } } catch (PushClientException e) { if (BaiduPushConstants.ERROROPTTYPE) { throw e; } else { e.printStackTrace(); } } catch (PushServerException e) { if (BaiduPushConstants.ERROROPTTYPE) { throw e; } else { System.out.println(String.format( "requestId: %d, errorCode: %d, errorMessage: %s", e.getRequestId(), e.getErrorCode(), e.getErrorMsg())); } } } }
android 接收推送 修改 小红点
public void onEventMainThread( RequestEvent event){ if (event instanceof CommonMessagesDBEvent) { CommonMessagesDBEvent dbEvent = (CommonMessagesDBEvent) event; switch (dbEvent.status) { case DB_SUCCESS: { List<CommonMessages> list = dbEvent.mDataList; if (list != null && list.size() > 0 ) { long num =0; for (CommonMessages c : list) { num += c.getRecieveNum(); } //sendMessageTip(mBottomMenuMsg, Integer.parseInt(num+""), BadgeView.POSITION_RIGHT_TOP); updateMessageTip(mBadgeViewMsg, Integer.parseInt(num+"")); } } break; case DB_NONE: { updateMessageTip(mBadgeViewMsg, 0); } break; case HTTP_ADD_ONE: { CommonMessagesDBManager.getAllFromDB(commonMsgDB); } break; default: break; } } } public static BadgeView sendMessageTip(View targetView, int messageCount, int gravity) { BadgeView badge = new BadgeView(MyApplication.newInstance(), targetView); badge.setText("" + messageCount); badge.setTextColor(Color.WHITE); badge.setTextSize(10); badge.setGravity(Gravity.CENTER); badge.setBadgePosition(gravity); Drawable drawable = MyApplication.newInstance().getResources().getDrawable( R.drawable.red_point); badge.setBackgroundDrawable(drawable); badge.hide(); return badge; } public static void updateMessageTip(BadgeView badgeView, int messageCount) { badgeView.setText(messageCount + ""); if (messageCount == 0) { badgeView.hide(); } else { badgeView.show(); } }
android 图片选择器 使用 微信的图片选择器
package com.curiousby.fitnessandappointment.activity; import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import com.curiousby.fitnessandappointment.R; import com.curiousby.fitnessandappointment.quote.picselector.ImageFloder; import com.curiousby.fitnessandappointment.quote.picselector.ListImageDirPopupWindow; import com.curiousby.fitnessandappointment.quote.picselector.ListImageDirPopupWindow.OnImageDirSelected; import com.curiousby.fitnessandappointment.quote.picselector.PicSelectorAdapter; import android.app.Activity; import android.app.ProgressDialog; import android.content.ContentResolver; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.provider.MediaStore; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import android.widget.GridView; import android.widget.PopupWindow.OnDismissListener; import android.widget.Button; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; public class PicSelectorActivity extends Activity implements OnImageDirSelected,OnClickListener { private ProgressDialog mProgressDialog; /** * 存储文件夹中的图片数量 */ private int mPicsSize; /** * 图片数量最多的文件夹 */ private File mImgDir; /** * 所有的图片 */ private List<String> mImgs; private GridView mGirdView; private PicSelectorAdapter mAdapter; private Button sureButton; private Button unsureButton; /** * 临时的辅助类,用于防止同一个文件夹的多次扫描 */ private HashSet<String> mDirPaths = new HashSet<String>(); /** * 扫描拿到所有的图片文件夹 */ private List<ImageFloder> mImageFloders = new ArrayList<ImageFloder>(); private RelativeLayout mBottomLy; private TextView mChooseDir; private TextView mImageCount; int totalCount = 0; private int mScreenHeight; private ListImageDirPopupWindow mListImageDirPopupWindow; private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { mProgressDialog.dismiss(); // 为View绑定数据 data2View(); // 初始化展示文件夹的popupWindw initListDirPopupWindw(); } }; /** * 为View绑定数据 */ private void data2View() { if (mImgDir == null) { Toast.makeText(getApplicationContext(), "擦,一张图片没扫描到", Toast.LENGTH_SHORT).show(); return; } mImgs = Arrays.asList(mImgDir.list()); /** * 可以看到文件夹的路径和图片的路径分开保存,极大的减少了内存的消耗; */ mAdapter = new PicSelectorAdapter(getApplicationContext(), mImgs, R.layout.picselector_grid_item, mImgDir.getAbsolutePath()); mGirdView.setAdapter(mAdapter); mImageCount.setText(totalCount + "张"); }; /** * 初始化展示文件夹的popupWindw */ private void initListDirPopupWindw() { mListImageDirPopupWindow = new ListImageDirPopupWindow( LayoutParams.MATCH_PARENT, (int) (mScreenHeight * 0.7), mImageFloders, LayoutInflater.from(getApplicationContext()) .inflate(R.layout.picselector_list_dir, null)); mListImageDirPopupWindow.setOnDismissListener(new OnDismissListener() { @Override public void onDismiss() { // 设置背景颜色变暗 WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.alpha = 1.0f; getWindow().setAttributes(lp); } }); // 设置选择文件夹的回调 mListImageDirPopupWindow.setOnImageDirSelected(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.picselector_activity_main); DisplayMetrics outMetrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(outMetrics); mScreenHeight = outMetrics.heightPixels; initView(); getImages(); initEvent(); } /** * 利用ContentProvider扫描手机中的图片,此方法在运行在子线程中 完成图片的扫描,最终获得jpg最多的那个文件夹 */ private void getImages() { if (!Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { Toast.makeText(this, "暂无外部存储", Toast.LENGTH_SHORT).show(); return; } // 显示进度条 mProgressDialog = ProgressDialog.show(this, null, "正在加载..."); new Thread(new Runnable() { @Override public void run() { String firstImage = null; Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; ContentResolver mContentResolver = PicSelectorActivity.this .getContentResolver(); // 只查询jpeg和png的图片 Cursor mCursor = mContentResolver.query(mImageUri, null, MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?", new String[] { "image/jpeg", "image/png" }, MediaStore.Images.Media.DATE_MODIFIED); Log.e("TAG", mCursor.getCount() + ""); while (mCursor.moveToNext()) { // 获取图片的路径 String path = mCursor.getString(mCursor .getColumnIndex(MediaStore.Images.Media.DATA)); Log.e("TAG", path); // 拿到第一张图片的路径 if (firstImage == null) firstImage = path; // 获取该图片的父路径名 File parentFile = new File(path).getParentFile(); if (parentFile == null) continue; String dirPath = parentFile.getAbsolutePath(); ImageFloder imageFloder = null; // 利用一个HashSet防止多次扫描同一个文件夹(不加这个判断,图片多起来还是相当恐怖的~~) if (mDirPaths.contains(dirPath)) { continue; } else { mDirPaths.add(dirPath); // 初始化imageFloder imageFloder = new ImageFloder(); imageFloder.setDir(dirPath); imageFloder.setFirstImagePath(path); } if(parentFile.list()==null)continue ; int picSize = parentFile.list(new FilenameFilter() { @Override public boolean accept(File dir, String filename) { if (filename.endsWith(".jpg") || filename.endsWith(".png") || filename.endsWith(".jpeg")) return true; return false; } }).length; totalCount += picSize; imageFloder.setCount(picSize); mImageFloders.add(imageFloder); if (picSize > mPicsSize) { mPicsSize = picSize; mImgDir = parentFile; } } mCursor.close(); // 扫描完成,辅助的HashSet也就可以释放内存了 mDirPaths = null; // 通知Handler扫描图片完成 mHandler.sendEmptyMessage(0x110); } }).start(); } /** * 初始化View */ private void initView() { sureButton = (Button) findViewById(R.id.b_selector_title_btn_sure); unsureButton = (Button) findViewById(R.id.b_selector_title_btn_unsure); mGirdView = (GridView) findViewById(R.id.id_gridView); mChooseDir = (TextView) findViewById(R.id.id_choose_dir); mImageCount = (TextView) findViewById(R.id.id_total_count); mBottomLy = (RelativeLayout) findViewById(R.id.id_bottom_ly); } private void initEvent() { mBottomLy.setOnClickListener(this); sureButton.setOnClickListener(this); unsureButton.setOnClickListener(this); } @Override public void selected(ImageFloder floder) { mImgDir = new File(floder.getDir()); mImgs = Arrays.asList(mImgDir.list(new FilenameFilter() { @Override public boolean accept(File dir, String filename) { if (filename.endsWith(".jpg") || filename.endsWith(".png") || filename.endsWith(".jpeg")) return true; return false; } })); /** * 可以看到文件夹的路径和图片的路径分开保存,极大的减少了内存的消耗; */ mAdapter = new PicSelectorAdapter(getApplicationContext(), mImgs, R.layout.picselector_grid_item, mImgDir.getAbsolutePath()); mGirdView.setAdapter(mAdapter); // mAdapter.notifyDataSetChanged(); mImageCount.setText(floder.getCount() + "张"); mChooseDir.setText(floder.getName()); mListImageDirPopupWindow.dismiss(); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.b_selector_title_btn_sure:{ List<String> list = PicSelectorAdapter.getmSelectedImage(); Intent intent = new Intent(); intent.putStringArrayListExtra("list", new ArrayList<String>(list)); setResult(TrendCreateActivity.RESULT_OK, intent); Log.e("baoyou", "=============="+list.toString()); PicSelectorAdapter.removeAllMSelectedImage(); Log.e("baoyou", "---------------======--------------========"); finish(); } break; case R.id.b_selector_title_btn_unsure:{ List<String> list = PicSelectorAdapter.getmSelectedImage(); Intent intent = new Intent(); intent.putStringArrayListExtra("list", new ArrayList<String>(list)); setResult(TrendCreateActivity.RESULT_NO, intent); Log.e("baoyou", "=============="+list.toString()); PicSelectorAdapter.removeAllMSelectedImage(); Log.e("baoyou", "---------------======--------------========"); finish(); } break; case R.id.id_bottom_ly: { mListImageDirPopupWindow .setAnimationStyle(R.style.anim_popup_dir); mListImageDirPopupWindow.showAsDropDown(mBottomLy, 0, 0); // 设置背景颜色变暗 WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.alpha = .3f; getWindow().setAttributes(lp); } break; default: break; } } }
android 朋友圈设计
package com.curiousby.fitnessandappointment.activity; import java.util.ArrayList; import java.util.Date; import java.util.List; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import cn.sharesdk.framework.ShareSDK; import cn.sharesdk.onekeyshare.OnekeyShare; import com.curiousby.fitnessandappointment.R; import com.curiousby.fitnessandappointment.adpter.TrendAdapter; import com.curiousby.fitnessandappointment.entity.FeedAllEntity; import com.curiousby.fitnessandappointment.entity.FeedCommentEntity; import com.curiousby.fitnessandappointment.entity.FeedEntity; import com.curiousby.fitnessandappointment.entity.FeedPicEntity; import com.curiousby.fitnessandappointment.entity.FeedZanEntity; import com.curiousby.fitnessandappointment.entity.UserEntity; import com.curiousby.fitnessandappointment.listener.OnClickCommentListener; import com.curiousby.fitnessandappointment.listener.OnClickForwordListener; import com.curiousby.fitnessandappointment.listener.OnClickToTrendCreateListener; import com.curiousby.fitnessandappointment.listener.OnClickZanListener; import com.curiousby.fitnessandappointment.quote.xlistview.MsgListView; import com.curiousby.fitnessandappointment.quote.xlistview.MsgListView.IXListViewListener; import com.curiousby.fitnessandappointment.request.db.UserDB; import com.curiousby.fitnessandappointment.request.event.TrendHttpEvent; import com.curiousby.fitnessandappointment.request.event.base.RequestEvent; import com.curiousby.fitnessandappointment.request.http.TrendHttpRequest; import com.curiousby.fitnessandappointment.utils.JsonParser; import com.curiousby.fitnessandappointment.utils.PrefInfoUtils; import de.greenrobot.event.EventBus; public class TrendActivity extends BaseActivity implements IXListViewListener , OnClickListener ,OnFocusChangeListener ,OnClickCommentListener,OnClickZanListener, OnClickToTrendCreateListener,OnClickForwordListener { final static int defaultPageNum = 5; private Context mContext; private EditText sendContent; LinearLayout inputLayout; private Button sendButton; private MsgListView mListView; private List<FeedAllEntity> mDataList; private TrendAdapter mAdapter; private UserDB userDB; private UserEntity user; private static int start = 0; // send reply need private boolean isReply; private FeedCommentEntity feedComment; private FeedEntity feedEntity; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.trend_list); mContext = TrendActivity.this; ShareSDK.initSDK(getApplicationContext()); initUtils(); initView( ); initListeners(); EventBus.getDefault().register( this ); start =0; TrendHttpRequest.getTrendFromWebByPage(user.getId()+"", start); } private void initListeners() { mListView.setPullLoadEnable(true); mListView.setPullRefreshEnable(true); mListView.setXListViewListener(this); mListView.setAdapter(mAdapter); mListView.setSelection(mAdapter.getCount() - 1); sendButton.setOnClickListener(this); sendContent.setOnFocusChangeListener(this); } private void initView() { mListView = (MsgListView) this.findViewById(R.id.trend_msgsend_list); sendContent = (EditText) this.findViewById(R.id.et_trend_msgsend); sendButton = (Button) this.findViewById(R.id.b_trend_msgsend_send); inputLayout = (LinearLayout) findViewById(R.id.ll_trend_msgsend_inputBar); mListView.setAdapter(mAdapter); } private void initUtils() { String userInfo = PrefInfoUtils.getUserInfo(mContext); user = JsonParser.parseDateJson(userInfo, UserEntity.class); mDataList = new ArrayList<FeedAllEntity>(); mAdapter = new TrendAdapter(mContext); mAdapter.setCommentListener(this); mAdapter.setZanListener(this); mAdapter.setmDataList(mDataList); mAdapter.setToTrendCreateListener(this); mAdapter.setForwordListener(this); } @Override public void onRefresh() { start =0; mListView.setPullLoadEnable(true); mListView.setPullRefreshEnable(true); TrendHttpRequest.getTrendFromWebByPage(user.getId()+"",start); } @Override public void onLoadMore() { start += defaultPageNum; TrendHttpRequest.getTrendFromWebByPage(user.getId()+"", start); } // textView // commentListener.onCommentClick(true, feedComment, feed); // button // commentListener.onCommentClick(false, null, feed); @Override public void onClick(View v) { switch (v.getId()) { case R.id.b_trend_msgsend_send: { String content = sendContent.getText().toString(); InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(sendContent.getWindowToken(), 0); inputLayout.setVisibility(View.GONE); if (mAdapter.getmDataList().contains(new FeedAllEntity(feedEntity, null, null,null))) { for (FeedAllEntity feedall : mAdapter.getmDataList()) { if (feedall.equals(new FeedAllEntity(feedEntity, null, null, null))) { FeedCommentEntity feedc = new FeedCommentEntity(); feedc.setFeedId(feedEntity.getId()); feedc.setUserAId(user.getId()); feedc.setUserANickname(user.getNickName()); feedc.setUserBId(isReply ? feedComment.getUserAId() : feedEntity.getUserFrom()); feedc.setUserBNickname(isReply ? feedComment .getUserBNickname() : feedEntity .getUserNickname()); feedc.setContent(content); feedc.setParentId(isReply ? feedComment.getId() : 0); feedc.setInsertTime(new Date()); feedc.setLastUpdateTime(new Date()); if (feedall.feedComments == null) { List<FeedCommentEntity> feedComments = new ArrayList<FeedCommentEntity>(); feedall.feedComments = feedComments; } feedall.feedComments.add(feedc); TrendHttpRequest.postTrendFeedComment(feedc); mAdapter.notifyDataSetChanged(); } } } }break; default: break; } } public void onEventMainThread(RequestEvent requestEvent){ if(requestEvent instanceof TrendHttpEvent){ TrendHttpEvent event = (TrendHttpEvent) requestEvent; switch(event.status){ case HTTP_NONE: mListView.stopRefresh(); mListView.stopLoadMore(); mListView.setPullLoadEnable(false); break; case HTTP_START: { mListView.stopRefresh(); mListView.stopLoadMore(); List<FeedAllEntity> list = event.mDataList; FeedEntity feedUser = new FeedEntity(); feedUser.setUserFrom(user.getId()); feedUser.setUserNickname(user.getNickName()); feedUser.setUserIcon(user.getPhoto()); list.add(0, new FeedAllEntity(feedUser, null, null,null)); mAdapter.setmDataList(list); mAdapter.notifyDataSetChanged(); } break; case HTTP_SUCCESS: { mListView.stopRefresh(); mListView.stopLoadMore(); List<FeedAllEntity> list = event.mDataList; mAdapter.addDataList(list); mAdapter.notifyDataSetChanged(); } break; default: break; } } } @Override public void onDestroy() { EventBus.getDefault().unregister( this ); super.onDestroy(); } @Override public void onFocusChange(View view, boolean hasFocus) { if (!hasFocus) inputLayout.setVisibility(View.GONE); else inputLayout.setVisibility(View.VISIBLE); } @Override public void onCommentClick(boolean isReply, FeedCommentEntity feedComment, FeedEntity feedEntity) { this.isReply = false; this.feedComment = null; this.feedEntity = null; inputLayout.setVisibility(View.VISIBLE); ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).toggleSoftInput(0,InputMethodManager.HIDE_NOT_ALWAYS); sendContent.setFocusable(true); sendContent.requestFocus(); this.isReply = isReply; this.feedComment = feedComment; this.feedEntity = feedEntity; } @Override public void onZanClick(FeedEntity feedEntity) { if (mAdapter.getmDataList().contains(new FeedAllEntity(feedEntity, null, null,null))) { for (FeedAllEntity feedall : mAdapter.getmDataList()) { if (feedall.equals(new FeedAllEntity(feedEntity, null, null, null))) { FeedZanEntity feedz = new FeedZanEntity(); feedz.setFeedId(feedEntity.getId()); feedz.setUserId(user.getId()); feedz.setUserNickname(user.getNickName()); feedz.setInsertTime(new Date()); feedz.setLastUpdateTime(new Date()); if (feedall.feedComments == null) { List<FeedCommentEntity> feedComments = new ArrayList<FeedCommentEntity>(); feedall.feedComments = feedComments; } if (!feedall.feedZans.contains(feedz)) { feedall.feedZans.add(feedz); }else{ feedall.feedZans.remove(feedz); } TrendHttpRequest.postTrendFeedZan(feedz); mAdapter.notifyDataSetChanged(); } } } } @Override public void onToTrendCreateClick(View view) { Intent intent = new Intent(TrendActivity.this, TrendCreateActivity.class); startActivity(intent); } @Override public void onForwordClick(List<FeedPicEntity> feedPics, FeedEntity feedEntity) { OnekeyShare oks = new OnekeyShare(); oks.setTitle("[来自fitnesschat]"); oks.setText("[来自fitnesschat]" + feedEntity.getContent()); // 应用通过审核后才能分享网络图片 /*if (feedPics.size() > 0) { String [] strArr = new String [feedPics.size()]; for (int i =0 ; i<feedPics.size(); i++) { strArr[i] = feedPics.get(i).getPicLink(); } oks.setImageUrl(strArr[0]); }*/ oks.show(mContext); } }
捐助开发者
在兴趣的驱动下,写一个免费
的东西,有欣喜,也还有汗水,希望你喜欢我的作品,同时也能支持一下。 当然,有钱捧个钱场(支持支付宝和微信 以及扣扣群),没钱捧个人场,谢谢各位。
个人主页:http://knight-black-bob.iteye.com/
谢谢您的赞助,我会做的更好!
上一篇: 控制字体加粗显示的html标签是什么