为wordpress模板添加LazyLoadxt实现延迟加载图片

公司网站中有的频道页面图片很多,所以一加载页面就需要很长的时间,为了解决这个问题,只好使用JQuery插件LazyLoadxt来实现延迟加载图片,从而加快加载速度。实现完此功能之后,刚好本博客的模板D8没有此功能,所以自己手动来添加延迟加载图片功能。

第一步: 实现LazyLoad步骤如下:

1.  加载JS文件(jquery.lazyloadxt.min.js)

20141227050757

 

20141227050819

 

2.  修改img标签属性

<img src="加载图片地址" data-src="图片地址" />

20141227051227

 

第二步: 修改wordpress模板

1.  本例已D8模板为例

20141227051423

 

2. 导入JS文件(jquery.lazyloadxt.min.js)

通过wordpress方法add_action来对init初始化进行注册,从而导入JS文件

在模板目录下的functions.php中找到init注册方法

20141227053530

然后导入JS文件

wp_deregister_script( 'lazyloadxt' );
wp_register_script( 'lazyloadxt', get_template_directory_uri() . '/js/jquery.lazyloadxt.min.js', false, '3.0', dopt('d_jquerybom_b') ? true : false );   
wp_enqueue_script( 'lazyloadxt' );

效果

20141227053704

3. 修改首页列表图片标签属性

找到模板目录下的index.php文件

20141227051607

找到图片列表位置(modules/sticky.php,modules/excerpt.php)

20141227051712

 

两个文件获得图片都是通过此方法(deel_thumbnail())来获得。

20141227052029

 

通过模板目录下的functions.php找到deel_thumbnail方法

20141227052324

然后进行修改img标签属性即可。

4. 修改内容页标签属性

找到模板目录下的page.php文件。

20141227052550

找到显示内容方法the_content();

20141227052657

然后在functions.php中找到注册方法add_filter(‘the_content’,’deel_copyright’):

20141227052854

还是在functios.php中找到deel_copyright方法,(对内容进行处理方法)

20141227053037

然后通过正则表达式对内容中的图片进行替换处理。

/*layzloadxt*/
	$content = preg_replace('/(<img.+src=\")(.+\.(jpg|gif|bmp|bnp|png)\"?)/i',"\${1}".get_bloginfo('template_url')."/img/thumbnail.png\" data-src=\"\${2} ",$content);

效果

20141227053826

centos 7.0 挂载NTFS移动硬盘

公司需要本地备份,而本地服务器硬盘容量不够,所以需要将本地服务器centos 7.0系统的备份数据拷贝到移动硬盘。所以需要在centos上挂载NTFS格式的移动硬盘。

第一步: 安装ntfs-3g

因为移动硬盘是NTFS的文件系统,所以centos 7.0不识别这文件系统,所以安装ntfs-3g.

官方网址:http://www.tuxera.com/,
文档手册:http://www.tuxera.com/community/ntfs-3g-manual/
下载地址:http://www.tuxera.com/community/ntfs-3g-download/

20141217190104

1. 下载ntfs-3g

20141217185940

 

2. 上传到centos中,并且解压。

20141217190402

3. 进入ntfs-3g_ntfprogs-2014.2.25目录

20141217190510

4. 执行安装

# ./configure 
# make 
# make install

20141217190652

 

20141217190821

20141217190943

这样就算安装成功了。

第二步: 挂载移动硬盘

1. 查看移动硬盘信息

fdisk -l

20141217183801

额,出现乱码?这个是中文编码问题,如何解决?

export LC_ALL=zh_CN.GBK
export LANG=zh_CN.GBK

20141217183836

 

2. 创建挂载目录

在/mnt目录下创建挂载目录

20141217191643

3. 挂载移动硬盘

mount -t ntfs-3g <NTFS Partition> <Mount Point>

20141217191836

4. 开机自动挂载

在/etc/fstab里面添加如下格式语句

<NTFS Partition> <Mount Point> ntfs-3g silent,umask=0,locale=zh_CN.utf8 0 0

20141217192856

5. 挂载点卸载

umount <NTFS Partition> 或者 umount <Mount Point>

20141217193102

实现Discuz x3.1 读写分离

因为公司网站的用户量越来越大,所以考虑到给服务器减轻负载,就想到mysql主从配置,然后就想通过mysql主从配置实现Discuz x3.1的数据库读写分离。实现步骤如下:

第一步: MySQL主从设置之主服务器A设置

1.  找到主服务器A的MySQL的配置文件my.ini

20141217002107

2. 打开my.ini,在[mysqld]下面添加以下参数

20141217002251

3. 在主服务器A中添加一个用于主从复制的帐号:

登陆mysql命令行,执行

GRANT REPLICATION SLAVE ON *.* TO ‘帐号’@’从服务器IP’ IDENTIFIED BY ‘密码’;

20141217002727

4. 重启MySQL ,让配置生效

5. 可以通过show master status\G;查看主从数据库是否配置成功。

20141217003356

 

第二步: MySQL主从设置之主数据库和从数据库数据一致。

1.  关闭论坛访问,停止更新数据

20141217004134

2. 在主服务器中加入只读锁

20141217004356

3. 导出数据库

通过命令导出数据库 mysqldump -u root -p 数据库名 > 导出来的位置

20141217004933

 

4. 将主服务器的数据库导入到从服务器的数据库

20141217010241

5. 将主数据库服务器解除只读锁

20141217010517

 

 

6.  开启论坛访问。

 

第三步:MySQL主从设置之从服务器B设置

1. 找到从服务器mysql配置文件my.cnf (主服务器是windows,从服务器是Linux。所以配置文件的后缀不一致)

20141217011657

2. 打开my.cnf,在[mysqld]下面添加以下参数

20141217012140

3. 重启从数据库

20141217012341

4、登录从库的MySQL命令行,执行:

change master to master_host=’主服务器IP’, master_user=’主服务器账号’, master_password=’主服务器密码’, master_log_file=’file的值’, master_log_pos=position的值;

//设置连接信息,file及position的值是之前记录下来(在主服务器上通过show master status\G;),position的值没有单引号,其他的值要单引号

20141217013520

5. 启动从库连接

start slave; //启动从库连接

20141217013700

6、查看从库状态:

show slave status\G; //查看连接情况

20141217020252

7、编辑从MYSQL服务器的MySQL配置文件my.cnf,在[mysqld]下面添加以下参数:

20141217020541

 

8. 测试,可以在主服务器上添加数据或者删除数据,从服务器会对应更新过来。

 

第四步: 读写分离

打开discuz x3.1的配置文件config/config_global.php

20141217023111

20141217023019

20141217023212

在xcode6.1和ios10.10.1环境下实现app发布

之前写过在xcode6.1和ios10.10.1环境下实现真机测试,以及最近提交的app一直在审核当中,所以木有发布如何实现app发布来分享给大家。刚好昨天app审核通过了,所以就分享一篇如何实现app发布。

第一步: 创建app发布证书以及配置文件

1.  进入ios开发中心

20141214161354

2. 点击进入 Certificates,Identifiers & Profiles

20141214161607

3. 说明:因为这次我需要的是发布app,所以证书需要选择的是Production版本,而开发测试的话是Development版本。

4. 在Identifers中创建App IDs

20141214161943

说明: 这里就不做如何创建APP IDS的教程了,因为上次真机测试中已有,而已创建一个APP IDS中就包括了开发版(Development)和发布版本(Distribution).

5. 创建发布证书,类似与创建开发证书

20141214162346

只不过是我选择的类型是Production下的即可。我选择的是 App Store and Ad Hoc.

20141214162556

6. 创建配置文件

这里是直接发布,所以不需要创建设备了。

20141214162740

选择App Store,进入下一步。

20141214162907

选择APP ID,进入下一步。

20141214163006

选择创建好的发布证书,进入下一步。

20141214163128

填写配置文件,进入下一步下载即可。

20141214163301

7. 下载发布证书以及配置文件

20141214163445

第二步: 配置xcode

双击下载好的证书和配置文件,然后打开xcode.

在TARGETS->Code Signing 中选择配置文件和证书

20141214164000

在PROJECT->Code Signing中选择配置文件和证书

20141214164203

在Project->Archive进行编译

20141214164344

编译成功之后进入Archives

20141214164816

第三步: 在iTunes Connect创建app,以及配置app信息。

1.  进入开发者中心,点击iTunes Connect进入iTunes Connect中心

20141214165341

进入iTunes Connect

20141214165610

进入我的APP,以及创建APP.

20141214165722

20141214165841

然后点击创建

20141214170007

然后对应的填写app信息。

这里需要注意的几点如下:

1. App 视频预览和屏幕快照,要根据官网提供的大小,不然无法上传。

2. App icon也是需要根据官网提供的大小来。

如果不知道其大小,可以点击旁边的问号,然后点击查看更多。

20141214170419

20141214170452

填写完app资料之后,还需要在预发行中添加构建版本。

20141214170646

如何添加构建版本?

第四步: APP验证以及提交

在xcode中进入到Archives。

20141214170957

点击Validate进行验证。

20141214171101

在此过程中,会提示你要登录开发中账号,然后对app进行验证。

验证过程中,可能会提示报错。原因是缺少了icon文件。

所以,需要按照它提示的icon图片大小来添加icon文件。添加完之后,它还是报错,缺少icon文件。还需要在配置文件中添加。请看以下截图。

20141214171525

点击submit进行app提交

20141214171723

提交完成之后,就会itunes connect中创建的app的构造版本中显示。

第五步: 提交并审核

在itunes connect中将创建好的app提交并审核。

20141214172403

提交完成之后,需要等待15-30天左右。

20141214172511

2014.11.29提交开始审核的。

2014.12.14审核通过。

20141214173042

实现android应用自动更新功能

在android应用开发中,需要考虑到以后新的应用更新,所以需要实现一个自动更新功能。当用户打开应用的时候,就获得服务器上的更新文件(版本号,以及下载地址…),然后和现在使用的APP比较,如果版本低了,就提示是否下载更新,如果版本一样就直接跳过。

实现步骤:

第一步:创建一个com.jhonse.update包,此包下创建一个UpdateManager.java类。

20141211212616

UpdateManager类的代码如下:

package com.jhonse.update;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import com.jhonse.aaa515.R;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ProgressBar;

public class UpdateManager {
	/* 下载中 */
	private static final int DOWNLOAD = 1;
	/* 下载结束 */
	private static final int DOWNLOAD_FINISH = 2;
	/* 保存解析的XML信息 */
	HashMap<String, String> mHashMap;
	/* 下载保存路径 */
	private String mSavePath;
	/* 记录进度条数量 */
	private int progress;
	/* 是否取消更新 */
	private boolean cancelUpdate = false;

	private Context mContext;
	/* 更新进度条 */
	private ProgressBar mProgress;
	private Dialog mDownloadDialog;

	private String jURL = "http://blog.jhonse.com/version.xml";

	@SuppressLint("HandlerLeak")
	private Handler mHandler = new Handler() {
		public void handleMessage(Message msg) {
			switch (msg.what) {
			// 正在下载
			case DOWNLOAD:
				// 设置进度条位置
				mProgress.setProgress(progress);
				break;
			case DOWNLOAD_FINISH:
				// 安装文件
				installApk();
				break;
			default:
				break;
			}
		};
	};

	public UpdateManager(Context context) {
		this.mContext = context;
	}

	/**
	 * 检测软件更新
	 */
	public void checkUpdate() {

		if (isUpdate()) {
			// 显示提示对话框
			showNoticeDialog();
		} 
	}

	/**
	 * 检查软件是否有更新版本
	 * 
	 * @return
	 * 
	 */
	private boolean isUpdate() {
		// 获取当前软件版本
		int versionCode = getVersionCode(mContext);
		// 把version.xml放到网络上,然后获取文件信息

		/*
		 * InputStream inStream = ParseXmlService.class.getClassLoader()
		 * .getResourceAsStream("version.xml");
		 * 
		 * // 解析网络xml进行更新软件 /*URL url = null; try { url = new URL(jURL); } catch
		 * (MalformedURLException e1) { e1.printStackTrace(); }
		 * HttpURLConnection conn = null; try { conn = (HttpURLConnection)
		 * url.openConnection(); } catch (IOException e1) {
		 * e1.printStackTrace(); } conn.setConnectTimeout(5000);
		 * conn.setRequestProperty("Content-Type", "text/plain; charset=utf-8");
		 * try { conn.setRequestMethod("GET"); } catch (ProtocolException e3) {
		 * e3.printStackTrace(); } InputStream is = null; try { is =
		 * conn.getInputStream(); } catch (IOException e1) {
		 * e1.printStackTrace(); }
		 */
		// 解析XML文件。 由于XML文件比较小,因此使用DOM方式进行解析

		InputStream inStream = null;

		try {

			HttpGet urlGet = new HttpGet(jURL); // 远程http地址

			HttpResponse httpResponse = new DefaultHttpClient().execute(urlGet);

			inStream = httpResponse.getEntity().getContent();

		} catch (IOException e) {
			e.printStackTrace();
		}

		ParseXmlService service = new ParseXmlService();
		try {
			mHashMap = service.parseXml(inStream);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (null != mHashMap) {
			int serviceCode = Integer.valueOf(mHashMap.get("version"));
			// 版本判断
			if (serviceCode > versionCode) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 获取软件版本号
	 * 
	 * @param context
	 * @return
	 */
	private int getVersionCode(Context context) {
		int versionCode = 0;
		try {
			// 获取软件版本号,对应AndroidManifest.xml下android:versionCode
			versionCode = context.getPackageManager().getPackageInfo(
					"com.jhonse.aaa515", 0).versionCode;
		} catch (NameNotFoundException e) {
			e.printStackTrace();
		}
		return versionCode;
	}

	/**
	 * 显示软件更新对话框
	 */
	private void showNoticeDialog() {
		// 构造对话框
		AlertDialog.Builder builder = new Builder(mContext);
		builder.setTitle(R.string.soft_update_title);
		builder.setMessage(R.string.soft_update_info);
		// 更新
		builder.setPositiveButton(R.string.soft_update_updatebtn,
				new OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
						// 显示下载对话框
						showDownloadDialog();
					}
				});
		// 稍后更新
		builder.setNegativeButton(R.string.soft_update_later,
				new OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
					}
				});
		Dialog noticeDialog = builder.create();
		noticeDialog.show();
	}

	/**
	 * 显示软件下载对话框
	 */
	private void showDownloadDialog() {
		// 构造软件下载对话框
		AlertDialog.Builder builder = new Builder(mContext);
		builder.setTitle(R.string.soft_updating);
		// 给下载对话框增加进度条
		final LayoutInflater inflater = LayoutInflater.from(mContext);
		View v = inflater.inflate(R.layout.softupdate_progress, null);
		mProgress = (ProgressBar) v.findViewById(R.id.update_progress);
		builder.setView(v);
		// 取消更新
		builder.setNegativeButton(R.string.soft_update_cancel,
				new OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.dismiss();
						// 设置取消状态
						cancelUpdate = true;
					}
				});
		mDownloadDialog = builder.create();
		mDownloadDialog.show();
		// 现在文件
		downloadApk();
	}

	/**
	 * 下载apk文件
	 */
	private void downloadApk() {
		// 启动新线程下载软件
		new downloadApkThread().start();
	}

	/**
	 * 下载文件线程
	 */
	private class downloadApkThread extends Thread {
		@Override
		public void run() {
			try {
				// 判断SD卡是否存在,并且是否具有读写权限
				if (Environment.getExternalStorageState().equals(
						Environment.MEDIA_MOUNTED)) {
					// 获得存储卡的路径
					String sdpath = Environment.getExternalStorageDirectory()
							+ "/";
					mSavePath = sdpath + "download";
					URL url = new URL(mHashMap.get("url"));
					// 创建连接
					HttpURLConnection conn = (HttpURLConnection) url
							.openConnection();
					conn.connect();
					// 获取文件大小
					int length = conn.getContentLength();
					// 创建输入流
					InputStream is = conn.getInputStream();

					File file = new File(mSavePath);
					// 判断文件目录是否存在
					if (!file.exists()) {
						file.mkdir();
					}
					File apkFile = new File(mSavePath, mHashMap.get("name"));
					FileOutputStream fos = new FileOutputStream(apkFile);
					int count = 0;
					// 缓存
					byte buf[] = new byte[1024];
					// 写入到文件中
					do {
						int numread = is.read(buf);
						count += numread;
						// 计算进度条位置
						progress = (int) (((float) count / length) * 100);
						// 更新进度
						mHandler.sendEmptyMessage(DOWNLOAD);
						if (numread <= 0) {
							// 下载完成
							mHandler.sendEmptyMessage(DOWNLOAD_FINISH);
							break;
						}
						// 写入文件
						fos.write(buf, 0, numread);
					} while (!cancelUpdate);// 点击取消就停止下载.
					fos.close();
					is.close();
				}
			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			// 取消下载对话框显示
			mDownloadDialog.dismiss();
		}
	};

	/**
	 * 安装APK文件
	 */
	private void installApk() {
		File apkfile = new File(mSavePath, mHashMap.get("name"));
		if (!apkfile.exists()) {
			return;
		}
		// 通过Intent安装APK文件
		Intent i = new Intent(Intent.ACTION_VIEW);
		i.setDataAndType(Uri.parse("file://" + apkfile.toString()),
				"application/vnd.android.package-archive");
		mContext.startActivity(i);
	}

}

 第二步: 创建xml解析类ParseXmlService.java

因为从服务器获取的是xml文件数据,所以需要对xml数据进行解析。

代码如下:

package com.jhonse.update;

import java.io.InputStream;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ParseXmlService
{
	public HashMap<String, String> parseXml(InputStream inStream) throws Exception
	{
		HashMap<String, String> hashMap = new HashMap<String, String>();
		
		// 实例化一个文档构建器工厂
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		// 通过文档构建器工厂获取一个文档构建器
		DocumentBuilder builder = factory.newDocumentBuilder();
		// 通过文档通过文档构建器构建一个文档实例
		Document document = builder.parse(inStream);
		//获取XML文件根节点
		Element root = document.getDocumentElement();
		//获得所有子节点
		NodeList childNodes = root.getChildNodes();
		for (int j = 0; j < childNodes.getLength(); j++)
		{
			//遍历子节点
			Node childNode = (Node) childNodes.item(j);
			if (childNode.getNodeType() == Node.ELEMENT_NODE)
			{
				Element childElement = (Element) childNode;
				//版本号
				if ("version".equals(childElement.getNodeName()))
				{
					hashMap.put("version",childElement.getFirstChild().getNodeValue());
				}
				//软件名称
				else if (("name".equals(childElement.getNodeName())))
				{
					hashMap.put("name",childElement.getFirstChild().getNodeValue());
				}
				//下载地址
				else if (("url".equals(childElement.getNodeName())))
				{
					hashMap.put("url",childElement.getFirstChild().getNodeValue());
				}
			}
		}
		return hashMap;
	}
}

 第三步: 在应用初始完之后,来检查是否需要更新

UpdateManager updateManager = new UpdateManager(aaa515.this);
updateManager.checkUpdate();

在此调用中,可能会报错,提示线程问题。解决方法如下:

if (android.os.Build.VERSION.SDK_INT > 9) {
	StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
	StrictMode.setThreadPolicy(policy);
}
UpdateManager updateManager = new UpdateManager(aaa515.this);
updateManager.checkUpdate();

第四步:添加程序所用的资源与权限

创建layout文件softupdate_progress.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">
	<ProgressBar
		android:id="@id/update_progress"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		style="?android:attr/progressBarStyleHorizontal" />
</LinearLayout>

创建string文件strings.xml,代码如下:

<?xml version='1.0' encoding='utf-8'?>
<resources>
    <string name="soft_update_no">已经是最新版本</string>
    <string name="soft_update_title">软件更新</string>
    <string name="soft_update_info">检测到新版本,立即更新吗</string>
    <string name="soft_update_updatebtn">更新</string>
    <string name="soft_update_later">稍后更新</string>
    <string name="soft_updating">正在更新</string>
    <string name="soft_update_cancel">取消</string>
    <string name="loading">加载中</string>
</resources>

创建ID文件ids.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item type="id" name="update_progress"/>
</resources>

添加网络访问权限

<uses-permission android:name="android.permission.INTERNET" />

 第五步: 在服务器上创建xml文件version.xml,提供给andriod应用访问。

<update>
	<version>版本号</version>
	<name>****.apk</name>
	<url>http://blog.jhonse.com/****.apk</url>
</update>

 第六步: 效果

20141211215732

20141211215750

android中webview控件实现拍照以及相册上传图片功能

在android中webview控件加载微网站时,有的时候需要通过html标签input file来上传图片,而android中webview不提供拍照以及相册上传图片功能。所以需要重新来写代码来实现通过input file 来上传图片。

开始实现input file来拍照以及相册上传图片功能。

第一步: 重载WebChromeClient类

this.appView.setWebChromeClient(new JCordovaChromeClient(this));

 第二步: 给重载类添加拍照以及相册方法

	// <input type="file" name="fileField" id="fileField" />
	// Android > 4.1.1
	@SuppressWarnings("static-access")
	public void openFileChooser(ValueCallback<Uri> uploadMsg,
			String acceptType, String capture) {
		super.mUploadMessage = uploadMsg;
		this.jaaa515.startActivityForResult(createDefaultOpenableIntent(),
				this.FILECHOOSER_RESULTCODE);
	}

	// 3.0 +
	@SuppressWarnings("static-access")
	public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
		super.mUploadMessage = uploadMsg;
		this.jaaa515.startActivityForResult(createDefaultOpenableIntent(),
				this.FILECHOOSER_RESULTCODE);
	}

	// Android < 3.0
	@SuppressWarnings("static-access")
	public void openFileChooser(ValueCallback<Uri> uploadMsg) {
		super.mUploadMessage = uploadMsg;
		this.jaaa515.startActivityForResult(createDefaultOpenableIntent(),
				this.FILECHOOSER_RESULTCODE);

	}

	private Intent createDefaultOpenableIntent() {
		Intent i = new Intent(Intent.ACTION_GET_CONTENT);
		i.addCategory(Intent.CATEGORY_OPENABLE);
		i.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
				"image/*");

		Intent chooser = createChooserIntent(createCameraIntent()/*
																 * ,
																 * createCamcorderIntent
																 * (),
																 * createSoundRecorderIntent
																 * ()
																 */);

		chooser.putExtra(Intent.EXTRA_INTENT, i);

		return chooser;

	}

	private Intent createChooserIntent(Intent... intents) {
		Intent chooser = new Intent(Intent.ACTION_CHOOSER);
		chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents);
		chooser.putExtra(Intent.EXTRA_TITLE, "选择图片");
		return chooser;
	}

	@SuppressWarnings("static-access")
	private Intent createCameraIntent() {
		Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
		File externalDataDir = Environment
				.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
		File cameraDataDir = new File(externalDataDir.getAbsolutePath()
				+ File.separator + "515aaa");

		cameraDataDir.mkdirs();
		String mCameraFilePath = cameraDataDir.getAbsolutePath()
				+ File.separator + System.currentTimeMillis() + ".jpg";

		this.mCameraFilePath = mCameraFilePath;

		cameraIntent.putExtra(MediaStore.Images.Media.ORIENTATION, 0);

		cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,
				Uri.fromFile(new File(mCameraFilePath)));

		return cameraIntent;

	}

	/*
	 * private Intent createCamcorderIntent() { return new
	 * Intent(MediaStore.ACTION_VIDEO_CAPTURE); }
	 * 
	 * private Intent createSoundRecorderIntent() { return new
	 * Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); }
	 */

在这里把最后两个方法已注释,因为我开发的app中不需要上传视频和上传音频。

完成之后我们还需要添加一个拍完照之后图片处理方法:

	public static Uri getImageContentUri(Context context, java.io.File imageFile) {
		String filePath = imageFile.getAbsolutePath();
		Cursor cursor = context.getContentResolver().query(
				MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
				new String[] { MediaStore.Images.Media._ID },
				MediaStore.Images.Media.DATA + "=? ",
				new String[] { filePath }, null);
		if (cursor != null && cursor.moveToFirst()) {
			int id = cursor.getInt(cursor
					.getColumnIndex(MediaStore.MediaColumns._ID));
			Uri baseUri = Uri.parse("content://media/external/images/media");
			return Uri.withAppendedPath(baseUri, "" + id);
		} else {
			if (imageFile.exists()) {
				ContentValues values = new ContentValues();
				values.put(MediaStore.Images.Media.DATA, filePath);
				return context.getContentResolver().insert(
						MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
			} else {
				return null;
			}
		}
	}

整体代码如下:

package com.jhonse.aaa515;

import java.io.File;

import org.apache.cordova.CordovaChromeClient;
import org.apache.cordova.CordovaInterface;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.webkit.ValueCallback;
import android.webkit.WebView;

public class JCordovaChromeClient extends CordovaChromeClient {

	private aaa515 jaaa515;
	public static final int FILECHOOSER_RESULTCODE = 5173;
	public static String mCameraFilePath = "";

	@SuppressWarnings("deprecation")
	public JCordovaChromeClient(CordovaInterface cordova) {
		super(cordova);
		this.jaaa515 = (aaa515) cordova;
	}

	@Override
	public void onProgressChanged(WebView view, int newProgress) {
		super.onProgressChanged(view, newProgress);
	}

	// <input type="file" name="fileField" id="fileField" />
	// Android > 4.1.1
	@SuppressWarnings("static-access")
	public void openFileChooser(ValueCallback<Uri> uploadMsg,
			String acceptType, String capture) {
		super.mUploadMessage = uploadMsg;
		this.jaaa515.startActivityForResult(createDefaultOpenableIntent(),
				this.FILECHOOSER_RESULTCODE);
	}

	// 3.0 +
	@SuppressWarnings("static-access")
	public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
		super.mUploadMessage = uploadMsg;
		this.jaaa515.startActivityForResult(createDefaultOpenableIntent(),
				this.FILECHOOSER_RESULTCODE);
	}

	// Android < 3.0
	@SuppressWarnings("static-access")
	public void openFileChooser(ValueCallback<Uri> uploadMsg) {
		super.mUploadMessage = uploadMsg;
		this.jaaa515.startActivityForResult(createDefaultOpenableIntent(),
				this.FILECHOOSER_RESULTCODE);

	}

	private Intent createDefaultOpenableIntent() {
		Intent i = new Intent(Intent.ACTION_GET_CONTENT);
		i.addCategory(Intent.CATEGORY_OPENABLE);
		i.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
				"image/*");

		Intent chooser = createChooserIntent(createCameraIntent()/*
																 * ,
																 * createCamcorderIntent
																 * (),
																 * createSoundRecorderIntent
																 * ()
																 */);

		chooser.putExtra(Intent.EXTRA_INTENT, i);

		return chooser;

	}

	private Intent createChooserIntent(Intent... intents) {
		Intent chooser = new Intent(Intent.ACTION_CHOOSER);
		chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents);
		chooser.putExtra(Intent.EXTRA_TITLE, "选择图片");
		return chooser;
	}

	@SuppressWarnings("static-access")
	private Intent createCameraIntent() {
		Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
		File externalDataDir = Environment
				.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
		File cameraDataDir = new File(externalDataDir.getAbsolutePath()
				+ File.separator + "515aaa");

		cameraDataDir.mkdirs();
		String mCameraFilePath = cameraDataDir.getAbsolutePath()
				+ File.separator + System.currentTimeMillis() + ".jpg";

		this.mCameraFilePath = mCameraFilePath;

		cameraIntent.putExtra(MediaStore.Images.Media.ORIENTATION, 0);

		cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,
				Uri.fromFile(new File(mCameraFilePath)));

		return cameraIntent;

	}

	/*
	 * private Intent createCamcorderIntent() { return new
	 * Intent(MediaStore.ACTION_VIDEO_CAPTURE); }
	 * 
	 * private Intent createSoundRecorderIntent() { return new
	 * Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); }
	 */

	public static Uri getImageContentUri(Context context, java.io.File imageFile) {
		String filePath = imageFile.getAbsolutePath();
		Cursor cursor = context.getContentResolver().query(
				MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
				new String[] { MediaStore.Images.Media._ID },
				MediaStore.Images.Media.DATA + "=? ",
				new String[] { filePath }, null);
		if (cursor != null && cursor.moveToFirst()) {
			int id = cursor.getInt(cursor
					.getColumnIndex(MediaStore.MediaColumns._ID));
			Uri baseUri = Uri.parse("content://media/external/images/media");
			return Uri.withAppendedPath(baseUri, "" + id);
		} else {
			if (imageFile.exists()) {
				ContentValues values = new ContentValues();
				values.put(MediaStore.Images.Media.DATA, filePath);
				return context.getContentResolver().insert(
						MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
			} else {
				return null;
			}
		}
	}
}

 第三步: 重载Activity的startActivityForResult方法和onActivityResult方法。

我们需要对拍照以及相册返回的数据进行处理。

public void startActivityForResult(CordovaPlugin command, Intent intent,
			int requestCode) {
		this.activityResultCallback = command;
		this.activityResultKeepRunning = this.keepRunning;

		// If multitasking turned on, then disable it for activities that return
		// results
		if (command != null) {
			this.keepRunning = false;
		}
		// Start activity
		super.startActivityForResult(intent, requestCode);
	}

	@SuppressLint("NewApi")
	@Override
	protected void onActivityResult(int requestCode, int resultCode,
			Intent intent) {
		if (appView != null
				&& requestCode == JCordovaChromeClient.FILECHOOSER_RESULTCODE) {

			ValueCallback<Uri> mUploadMessage = this.appView
					.getWebChromeClient().getValueCallback();
			if (null == mUploadMessage) {
				return;
			}
			Uri result = intent == null || resultCode != Activity.RESULT_OK ? null
					: intent.getData();

			if (intent == null && JCordovaChromeClient.mCameraFilePath != "") {
				result = JCordovaChromeClient.getImageContentUri(this,
						new File(JCordovaChromeClient.mCameraFilePath));
			}

			mUploadMessage.onReceiveValue(result);
			mUploadMessage = null;
		}
		super.onActivityResult(requestCode, resultCode, intent);
	}

 第四步: 调用流程分析

1.  首先通过重载CordovaChromeClient类中的方法:openFileChooser,来匹配input file 打开文件上传功能。

2.  通过这个openFileChooser方法,我们可以添加拍照、相册、视频、音频。

3.  当拍照或者选择相册图片之后,就会调用Activity中的startActivityForResult方法。并且是通过自定义的FILECHOOSER_RESULTCODE来标识拍照、相册。

4.  startActivityForResult把对应的拍照、相册图片数据返回给onActivityResult方法来处理。

5.  onActivityResult方法中,如果是相册选择图片的话,就会返回图片数据,而如果是拍照的话,不会返回数据。所以我们需要对拍照的图片进行处理。

6. 所以通过自定义的getImageContentUri方法来处理拍完照之后的图片。

7. 说明: 因为拍照的图片,是通过自定义图片路径来生成的,所以对应的图片路径以及图片名称都是知道的。

在xcode6.1和ios10.10.1环境下实现真机测试

开发ios app时,所有的测试都是在xcode中的模拟器中进行。现在问题来了,需要测试拍照以及信息推送功能,模拟器就无法测试了,必须使用真机测试才行。所以只好选择真机来测试。

先来看看xcode与ios对应的版本号。

20141210141630 20141210141651

开始进入真机测试步骤。

 

第一步:  真机(IPAD)与ios连接

在xcode中点击Window->Devices查看设备

20141210141952

20141210142431

这样IPAD和ios系统算真正连接上了。

第二步: 创建app证书以及配置文件

1.  进入ios开发中心

20141210143021

2. 点击Certificatis,Identifiers& Profiles,进行配置。

20141210143321

然后再点击Certificats

20141210143452

20141210143634

3. 创建App IDs

20141210224721

然后填写app信息,点击continue下一步。

20141210144017

再然后点击提交Submit,App IDs创建成功!

20141210144205

20141210144405

4. 创建证书(Certificates)

20141210144707

 

填写对应的信息,点击Continue下一步

20141210144756

 

在这里需要CSR文件,先点击Continue下一步,等下介绍如何创建CSR文件。

20141210144947

 

如何创建CSR文件?需要到钥匙串工具来创建。

打开钥匙串

20141210145335

20141210145433

20141210145555

20141210145654

然后对应的把CSR文件保存到桌面。

20141210145840

选择CSR文件。进入下一步.

20141210145116

20141210150003

Certificates创建成功!即可下载.

20141210150600

说明:  app分为开发版(Development)和发布版(Production),现在真机测试是Development版。

5. 创建设备(Devices)

20141210221312

填写名称以及设备的UDID,点击下一步(Continue).

20141210221528

创建设备成功。

6. 创建配置文件(Provisioning Profiles)。

20141210221940

0141210222136

选择创建好的APP id

20141210222229

选择创建好的证书

20141210222356

选择创建好的设备

20141210222445

填写配置文件名称

20141210222603

创建成功!

20141210222643

7. 下载证书文件以及配置文件

20141210222948

第三步: 真机测试

双击已下载好的证书文件以及配置文件。

在密钥串就可以看到对应的证书

20141210223539

配置文件既可以到xcode里可以看到。

20141210223821

配置xcode

首先在TARGETS->Code Signing 中选择配置文件和证书

20141210224124

然后在PROJECT->Code Signing中选择配置文件和证书

20141210224322

然后点击运行。

20141210224910

20141210225534

OK,搞定!

说明: 创建的Bundle ID 要和TARGETS->Info->Bundle ID要一致,不然会报错,找不到配置文件。

2014年12月编程语言排行榜:R和Swift成为年度语言候选者

TIOBE 12月份编程语言排行榜发布,一起来看下:上个月我们曾报道R语言受大数据影响,份额一度攀升。而苹果公司发布的Swift也不断受到开发者追捧,仅发布一月,就已跃至TIOBE排行榜16位,实在是不可小觑。

根据排行榜分析数据得出,R和Swift 有望成为今年TIOBE年度语言的候选者,这一结果将在来年的一月份宣布。此外,JavaScript和Google Dart也有望参与评选,这一年两者发展的较为迅速。好了,各位网友选出你心中的年度语言桂冠吧。究竟花落谁家,我们拭目以待!

编程语言排行榜TOP 20榜单:

548559d581e7f

前10名编程语言长期走势图:

54855e2898b63

以下是21-50编程语言排名:

54855e7fcd068

54855e8e54303

后50名编程语言如下:

(Visual) FoxPro, 4th Dimension/4D, ABC, Alice, Apex, Automator, Awk, Bash, bc, BlitzMax, Bourne shell, C shell, C-Omega, cg, CL (OS/400), Clean, Clojure, DiBOL, Emacs Lisp, Erlang, Factor, Forth, Icon, IDL, Inform, Ioke, J, Korn shell, Ladder Logic, M4, Mathematica, Monkey, Moto, NATURAL, NXT-G, OpenCL, Oxygene, Oz, PILOT, Programming Without Coding Technology, Pure Data, Rust, S, SIGNAL, SPARK, Standard ML, Tcl, TOM, VHDL, Z shell

必须声明,这个榜单本身采集的是英文世界的数据,虽然在反映趋势上有一些参考意义,但与中国的实际情况不完全符合,而且,这张采样本身也有相当大的局限性。

在android中实现webview与javascript之间的交互

在android开发项目中,采用了webview控件和微网站。该项目中需要实现一个分享功能,而且是需要用javascript来调用android中定义好的分享功能,所以需要实现webview与javascript之间的交互。

一. 先说说android中webview控件如何调用javascript代码?

其实android中webview控件调用javascript方法还是比较简单,只需要设置webview支持javascript,然后用loadUrl调用javascript方法即可。实现代码如下:

this.appView.getSettings().setJavaScriptEnabled(true);

 javascript方法:

function showDialog(info){
         alert(info);
}

 android代码

this.appView.loadUrl("javascript:showDialog('jhonse')");

 二.  javascript调用android中自定义好的方法(例如: 分享)

需要给webview控件添加addJavascriptInterface方法,而这个就类似于初始化一个类对象,以及类对象里的方法。这个类对象就提供给javascript来调用。请看实现代码:

this.appView.addJavascriptInterface(new Object() {
			@JavascriptInterface
			public void OnClick(String Subject, String Text) {
				Intent intent = new Intent(Intent.ACTION_SEND);
				intent.setType("text/plain");
				intent.putExtra(Intent.EXTRA_SUBJECT, Subject);
				intent.putExtra(Intent.EXTRA_TEXT, Text);
				intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				startActivity(Intent.createChooser(intent, "分享"));
			}
		}, "JShare");

javascript调用自定义好的JShare对象。

if(window.JShare){
       $("#icon-share").click(function(){
		window.JShare.OnClick("分享",document.title+" "+document.URL);
	});
}

 效果图:

20141209010514

使用Ucenter整合wordpress和Discuz (给博客提速)

本博客自从谷歌在内地无法打开之后,打开速度就开始变慢,于是Jhonse哥我作为技术开发猿而言,怎能允许这情况发生?所以就开始给本博客加速了…

第一步: 去掉wordpress默认加载的谷歌字体样式

找到wordpress的核心文件script-loader.php,在/wp-includes目录下

20141208194704

在script-loader.php文件中搜索fonts.googleapis.com,以及ajax.googleapis.com,然后用360提供的前端公共库CDN服务替换即可。请看截图:

20141208195334

20141208195418

20141208195503

第二步: 使用Ucenter整合wordpress和ucenter,来实现更换Gravatar头像

之前使用了第一步的步骤给本博客提速,确确实实速度是提上来了。不过今天本博客又开始慢了,本以为是第一步修改的代码被恢复了,结果一看代码,不是的,原来影响到打开速度慢的是Gravatar头像。

请看分析代码:

在每个文章详细页都有评论页,也就是需要加载评论用户的头像,而首页以及列表页都木有,所以打开首页和列表速度正常,而点击进入详细页就变的特慢。请看加载头像代码。

//评论头像缓存
function deel_avatar($avatar) {
  $tmp = strpos($avatar, 'http');
  $g = substr($avatar, $tmp, strpos($avatar, "'", $tmp) - $tmp);
  $tmp = strpos($g, 'avatar/') + 7;
  $f = substr($g, $tmp, strpos($g, "?", $tmp) - $tmp);
  $w = get_bloginfo('wpurl');
  $e = ABSPATH .'avatar/'. $f .'.png';
  $t = dopt('d_avatarDate')*24*60*60; 
  if ( !is_file($e) || (time() - filemtime($e)) > $t ) 
	copy(htmlspecialchars_decode($g), $e);
  else  
	$avatar = strtr($avatar, array($g => $w.'/avatar/'.$f.'.png'));
  if ( filesize($e) < 500 ) 
	copy(get_bloginfo('template_directory').'/img/default.png', $e);
  return $avatar;
}
//头像缓存  
	if( dopt('d_avatar_b') ){
		add_filter('get_avatar','deel_avatar');  
	}

而deel_avatar函数对应的参数$avatar就是Gravatar头像的html代码,所以只要页面一打开就会加载Gravatar头像的html代码,所以这速度就慢起来了。有的时候直接超时。

于是就想到自己的Jhonse技术论坛对应的有一个Ucenter用户系统,所以就想到用Ucenter来整合wordpress和discuz,来实现用户评论头像为Jhonse技术论坛的用户头像。

说了这么多废话,开始进入主题来整合…

下载wordpress插件: Ucenter

本来自己想开发对应的接口的,结果竟然有前辈已写好,那就直接用咯。(给自己偷懒的借口)

20141208201215

安装之后,开始配置Ucenter插件

20141208201637

在此配置的前提是你需要在Ucenter中添加其应用。

20141208201818

而且需要连接成功!

20141208201908

配置完成之后,还需要修改ucenter提供的代码。请看如下步骤:

找到插件的ucenter.php文件,ucenter插件目录下。

20141208202251

找到get_avatar方法

function get_avatar( $avatar, $id_or_email, $size, $default, $alt ) {
		$user_login = "";
		if ( is_numeric( $id_or_email ) ) {
			$id = (int) $id_or_email;
			$user = get_userdata( $id );
			if ( $user )
				$user_login = $user->user_login;
		} elseif ( is_object( $id_or_email ) ) {
			if ( isset( $id_or_email->comment_type ) && '' != $id_or_email->comment_type && 'comment' != $id_or_email->comment_type )
				return false;

			if ( !empty( $id_or_email->user_id ) ) {
				$id = (int) $id_or_email->user_id;
				$user = get_userdata( $id );
				if ( $user )
					$user_login = $user->user_login;
			}
		}
		list( $uid, $_, $_ ) = uc_get_user( $user_login );
		if ( uc_check_avatar( $uid, 'small' ) > 0 ) {
			$src = UC_API . "/avatar.php?uid=$uid&size=small&random=" . rand();
			$avatar = "<img alt='{$alt}' src='{$src}' class='avatar avatar-{$size} photo avatar-default' height='{$size}' width='{$size}' />";
		}
		return $avatar;
	}

此方法为,当参数id_or_email为用户id或者用户对象时,然后获得其用户名,再然后获得其头像。

当用户不存在的时候没有对其进行处理,所以需要这样来操作:

if ( uc_check_avatar( $uid, 'small' ) > 0 ) {
			$src = UC_API . "/avatar.php?uid=$uid&size=small&random=" . rand();
			$avatar = "<img alt='{$alt}' src='{$src}' class='avatar avatar-{$size} photo avatar-default' height='{$size}' width='{$size}' />";
		}else{
			$src = UC_API . "/avatar.php?uid=0&size=small&random=" . rand();
			$avatar = "<img alt='{$alt}' src='{$src}' class='avatar avatar-{$size} photo avatar-default' height='{$size}' width='{$size}' />";
		}

还有一个没有针对用户的email来操作。因为在评论信息中,传过来的值就是email,所以就需要添加对email的处理。

第一步: 在$user_login初始化的时候直接赋值为$id_or_email

$user_login = $id_or_email;

第二步: 修改ucenter核心文件user.php

通过ucenter获得用户信息。调用流程如下:

uc_get_user ->  client/client.php文件(uc_get_user方法) -> client/control/user.php文件(onget_user方法)

此方法是通过用户名或者用户ID来获得用户信息。

function onget_user() {
		$this->init_input();
		$username = $this->input('username');
		if(!$this->input('isuid')) {
			$status = $_ENV['user']->get_user_by_username($username);
		} else {
			$status = $_ENV['user']->get_user_by_uid($username);
		}
		if($status) {
			return array($status['uid'],$status['username'],$status['email']);
		} else {
			return 0;
		}
	}

所以我们需要添加通过用户邮箱来获得用户信息

function onget_user() {
		$this->init_input();
		$username = $this->input('username');
		if(!$this->input('isuid')) {
			$status = $_ENV['user']->get_user_by_username($username);
			if(!$status){
				$status = $_ENV['user']->get_user_by_email($username);
			}
		} else {
			$status = $_ENV['user']->get_user_by_uid($username);
		}
		if($status) {
			return array($status['uid'],$status['username'],$status['email']);
		} else {
			return 0;
		}
	}

这样就完成了整合工作了.

说了这么多废话,看看效果图:

20141208204100