<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Marshal&#039;s Blog</title>
	<atom:link href="http://marshal.easymorse.com/feed" rel="self" type="application/rss+xml" />
	<link>http://marshal.easymorse.com</link>
	<description>It&#039;s swap of marshal&#039;s memory.</description>
	<lastBuildDate>Fri, 03 Sep 2010 07:51:15 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>编写最简单的NDK示例</title>
		<link>http://marshal.easymorse.com/archives/3185</link>
		<comments>http://marshal.easymorse.com/archives/3185#comments</comments>
		<pubDate>Fri, 03 Sep 2010 07:46:04 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[android ndk]]></category>
		<category><![CDATA[jni]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3185</guid>
		<description><![CDATA[写NDK应用，实际上是写JNI，就是java和本地应用的接口。先看看实现后的效果：
 
首先需要安装ndk，官方网址：
http://androidappdocs.appspot.com/sdk/ndk/index.html

下载然后解压缩到某个路径下即可。本文环境是mac osx。
设置路径，在以下文件：
vim ~/.bash_profile

设置：
export PATH=${PATH}:/Developer/android-sdk-mac_86/tools:/Developer/android-ndk-r4b

因为mac自带了gcc等开发环境。所以安装十分简单。
下面就是编写ndk项目了。首先编写一个普通的android项目。在activity中，有点儿特殊：
 

package com.easymorse; 
import android.app.Activity;      import android.os.Bundle;       import android.widget.TextView; 
public class NdkHelloActivity extends Activity {      &#160;&#160;&#160; /** Called when the activity is first created. */       &#160;&#160;&#160; @Override   [...]]]></description>
			<content:encoded><![CDATA[<p>写NDK应用，实际上是写JNI，就是java和本地应用的接口。先看看实现后的效果：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image12.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb12.png" width="220" height="364" /></a> </p>
<p>首先需要安装ndk，官方网址：</p>
<blockquote><p><a href="http://androidappdocs.appspot.com/sdk/ndk/index.html">http://androidappdocs.appspot.com/sdk/ndk/index.html</a></p>
</blockquote>
<p>下载然后解压缩到某个路径下即可。本文环境是mac osx。</p>
<p>设置路径，在以下文件：</p>
<blockquote><p>vim ~/.bash_profile</p>
</blockquote>
<p>设置：</p>
<blockquote><p>export PATH=${PATH}:/Developer/android-sdk-mac_86/tools:/Developer/android-ndk-r4b</p>
</blockquote>
<p>因为mac自带了gcc等开发环境。所以安装十分简单。</p>
<p>下面就是编写ndk项目了。首先编写一个普通的android项目。在activity中，有点儿特殊：</p>
<p> <span id="more-3185"></span><br />
<blockquote>
<p>package com.easymorse; </p>
<p>import android.app.Activity;      <br />import android.os.Bundle;       <br />import android.widget.TextView; </p>
<p>public class NdkHelloActivity extends Activity {      <br />&#160;&#160;&#160; /** Called when the activity is first created. */       <br />&#160;&#160;&#160; @Override       <br />&#160;&#160;&#160; public void onCreate(Bundle savedInstanceState) {       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; super.onCreate(savedInstanceState);       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; TextView textView = new TextView(this);       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; setContentView(textView); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; textView.setText(sayHello());      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; public native String sayHello(); </p>
<p>&#160;&#160;&#160; static {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; System.loadLibrary(&quot;helloworld-jni&quot;);       <br />&#160;&#160;&#160; }       <br />}</p>
</blockquote>
<p>这里的sayHello方法，就是jni需要本地实现的方法，实际上是用c写来实现的。在static块中必须加上对库的加载语句。</p>
<p>jni的写法是自顶向下的，也就是先写java，再生成头文件，然后再写c。好像java先写接口一般。当然c的业务代码可能是写好的，只是差对接jni的部分。</p>
<p>下面生成头文件。需要用到javah命令。</p>
<p>在项目中创建一个jni目录。在命令行下，进入该目录。输入命令：</p>
<blockquote><p>javah -classpath ../bin com.easymorse.NdkHelloActivity</p>
</blockquote>
<p>这里要注意要指向的类不是源文件，而是class文件，而且不是目录结构，是类全名，类似运行class文件。在eclipse adt项目中，class文件在bin目录下。</p>
<p>成功运行后，会在jni目录下生成一个名为 com_easymorse_NdkHelloActivity.h 的头文件：</p>
<blockquote><p>/* DO NOT EDIT THIS FILE &#8211; it is machine generated */     <br />#include &lt;jni.h&gt;      <br />/* Header for class com_easymorse_NdkHelloActivity */ </p>
<p>#ifndef _Included_com_easymorse_NdkHelloActivity     <br />#define _Included_com_easymorse_NdkHelloActivity      <br />#ifdef __cplusplus      <br />extern &quot;C&quot; {      <br />#endif      <br />/*      <br /> * Class:&#160;&#160;&#160;&#160; com_easymorse_NdkHelloActivity      <br /> * Method:&#160;&#160;&#160; sayHello      <br /> * Signature: ()Ljava/lang/String;      <br /> */      <br />JNIEXPORT jstring JNICALL Java_com_easymorse_NdkHelloActivity_sayHello      <br />&#160; (JNIEnv *, jobject); </p>
<p>#ifdef __cplusplus     <br />}      <br />#endif      <br />#endif</p>
<p>&#160;</p>
</blockquote>
<p>这里主要看Java_com_easymorse_NdkHelloActivity_sayHello，这是要在c中实现的方法。</p>
<p>创建c文件，在jni目录下，名为helloword-jni.c：</p>
<blockquote><p>#include &lt;string.h&gt;     <br />#include &lt;jni.h&gt; </p>
<p>jstring JNICALL Java_com_easymorse_NdkHelloActivity_sayHello(JNIEnv* env,     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; jobject thiz) {      <br />&#160;&#160;&#160; return (*env)-&gt;NewStringUTF(env, &quot;你好，世界。来自JNI！&quot;);      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>可见实现了上述方法。这里要注意，确保项目的编码是utf-8，否则在运行时会强行退出。</p>
<p>接着，在jni目录下创建一个Android.mk文件，这是构建c的脚本：</p>
<blockquote><p>LOCAL_PATH := $(call my-dir) </p>
<p>include $(CLEAR_VARS) </p>
<p>LOCAL_MODULE&#160;&#160;&#160; := helloworld-jni     <br />LOCAL_SRC_FILES := helloworld-jni.c </p>
<p>include $(BUILD_SHARED_LIBRARY)</p>
<p>&#160;</p>
</blockquote>
<p>主要是标明编译哪个文件，以及生成so的库名。</p>
<p>倒数第二步，编译生成so文件。命令行，项目根目录下，执行：</p>
<blockquote><p>ndk-build</p>
</blockquote>
<p>会看到根目录下多了一个libs目录。里面的armeabi目录下多了个so文件。</p>
<p>最后一步，在android手机上执行。一般情况下adt环境直接run as 》adnroid application就行了。这里，需要在生成so文件后刷新一下整个adt项目，否则adt会用旧的so文件。</p>
<p>源代码见：</p>
<blockquote><p><a title="http://easymorse.googlecode.com/svn/trunk/ndk.hello/" href="http://easymorse.googlecode.com/svn/trunk/ndk.hello/">http://easymorse.googlecode.com/svn/trunk/ndk.hello/</a></p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3185/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>mac下eclipse安装cdt</title>
		<link>http://marshal.easymorse.com/archives/3182</link>
		<comments>http://marshal.easymorse.com/archives/3182#comments</comments>
		<pubDate>Fri, 03 Sep 2010 06:32:54 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[eclipse]]></category>
		<category><![CDATA[eclipse cdt]]></category>
		<category><![CDATA[mac]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3182</guid>
		<description><![CDATA[马上又要做c/c++开发了。在mac上搭建一下ide环境。还是用eclipse+cdt吧。
安装cdt。目前使用的eclipse是3.6（helios），在上面直接安装cdt 7.0。可以参见cdt链接（http://www.eclipse.org/cdt/）。
 
安装介绍：http://www.eclipse.org/cdt/downloads.php，安装链接：http://download.eclipse.org/tools/cdt/releases/helios
 
 
因为mac中已经自带gcc环境了，因此不需要再像windows下那样，安装配置cygwin啥的劳什子东西了。
测试一下是否安装成功，编写一个hellowrold项目。
 
创建标准c项目：
 
注意项目的属性中，要设置为utf-8，默认是EUC_CN。
 
默认创建后，会生成现成的代码，把英文的hellowrold改为中文，其他不动。
 
执行buildall：
 
在命令行中执行：
 
说明环境搭建成功。
]]></description>
			<content:encoded><![CDATA[<p>马上又要做c/c++开发了。在mac上搭建一下ide环境。还是用eclipse+cdt吧。</p>
<p>安装cdt。目前使用的eclipse是3.6（helios），在上面直接安装cdt 7.0。可以参见cdt链接（<a href="http://www.eclipse.org/cdt/">http://www.eclipse.org/cdt/</a>）。</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image4.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb4.png" width="244" height="242" /></a> </p>
<p>安装介绍：<a href="http://www.eclipse.org/cdt/downloads.php">http://www.eclipse.org/cdt/downloads.php</a>，安装链接：<a title="http://download.eclipse.org/tools/cdt/releases/helios" href="http://download.eclipse.org/tools/cdt/releases/helios">http://download.eclipse.org/tools/cdt/releases/helios</a></p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image5.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb5.png" width="279" height="107" /></a> </p>
<p> <span id="more-3182"></span>
<p>因为mac中已经自带gcc环境了，因此不需要再像windows下那样，安装配置cygwin啥的劳什子东西了。</p>
<p>测试一下是否安装成功，编写一个hellowrold项目。</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image6.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb6.png" width="315" height="127" /></a> </p>
<p>创建标准c项目：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image7.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb7.png" width="325" height="289" /></a> </p>
<p>注意项目的属性中，要设置为utf-8，默认是EUC_CN。</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image8.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb8.png" width="338" height="157" /></a> </p>
<p>默认创建后，会生成现成的代码，把英文的hellowrold改为中文，其他不动。</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image9.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb9.png" width="455" height="175" /></a> </p>
<p>执行buildall：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image10.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb10.png" width="314" height="265" /></a> </p>
<p>在命令行中执行：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image11.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb11.png" width="302" height="154" /></a> </p>
<p>说明环境搭建成功。</p>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3182/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在mac环境下配置android环境变量</title>
		<link>http://marshal.easymorse.com/archives/3165</link>
		<comments>http://marshal.easymorse.com/archives/3165#comments</comments>
		<pubDate>Fri, 03 Sep 2010 04:36:50 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[mac]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3165</guid>
		<description><![CDATA[在解决mac中为图形界面程序设置环境变量问题中提到在maven中的配置，看了一下，android sdk tools路径的配置没记过。这里补充一下。
编辑或者创建文件：
vim ~/.bash_profile

在里面把tools路径加上：
export PATH=${PATH}:/Developer/android-sdk-mac_86/tools
&#160;

然后，执行命令，让配置生效：
source ~/.bash_profile

测试一下：
adb devices

]]></description>
			<content:encoded><![CDATA[<p>在<a href="http://marshal.easymorse.com/archives/2731" title="解决mac中为图形界面程序设置环境变量问题">解决mac中为图形界面程序设置环境变量问题</a>中提到在maven中的配置，看了一下，android sdk tools路径的配置没记过。这里补充一下。</p>
<p>编辑或者创建文件：</p>
<blockquote><p>vim ~/.bash_profile</p>
</blockquote>
<p>在里面把tools路径加上：</p>
<blockquote><p>export PATH=${PATH}:/Developer/android-sdk-mac_86/tools</p>
<p>&#160;</p>
</blockquote>
<p>然后，执行命令，让配置生效：</p>
<blockquote><p>source ~/.bash_profile</p>
</blockquote>
<p>测试一下：</p>
<blockquote><p><font style="background-color: #ffffff">adb devices</font></p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3165/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>自定义VideoView的演进</title>
		<link>http://marshal.easymorse.com/archives/3164</link>
		<comments>http://marshal.easymorse.com/archives/3164#comments</comments>
		<pubDate>Fri, 03 Sep 2010 02:58:10 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[android ui]]></category>
		<category><![CDATA[android videoview]]></category>
		<category><![CDATA[android view]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3164</guid>
		<description><![CDATA[在编写简单自定义VideoView中尝试编写自己的VideoView实现类。这样对VideoView的实现机制有了一个比较深入的理解。经过整理发现，其实要自定义需求，还真不一定需要重新自己的VideoView实现。在本文中将原来的CustomerVideoView的方法全部删除，并继承VideoView，发现功能上没有什么不能实现的。继承的CustomerVideoView最后其实只剩下继承来的构造方法，也就是说直接使用VideoView也没问题。
这次演进，实现了自定义的播放控制条：
 
这个播放控制条，实际是替代了编写简单自定义VideoView中的MediaController。这样就可以自定义各种样式和风格的控制条界面了。
 
另外，本文示例中默认不出现播放控制条，当识别到横向手势的时候，才显示该滚动条，并且根据横向手势的x轴位移前进或者后退播放视频的位置。
如果手指单击视频，则停止播放，再单击继续播放。
CustomerVideoView已经简化到可以直接使用VideoView替代：
package com.easymorse.videoplayer; 
import android.content.Context;     import android.util.AttributeSet;      import android.widget.VideoView; 
public class CustomerVideoView extends VideoView { 
&#160;&#160;&#160; private static String TAG = &#34;customer.videoplayer&#34;; 
&#160;&#160;&#160; public CustomerVideoView(Context context, AttributeSet attrs, int defStyle) {     &#160;&#160;&#160;&#160;&#160;&#160;&#160; super(context, attrs, defStyle);      &#160;&#160;&#160; [...]]]></description>
			<content:encoded><![CDATA[<p>在<a href="http://marshal.easymorse.com/archives/3160" title="编写简单自定义VideoView">编写简单自定义VideoView</a>中尝试编写自己的VideoView实现类。这样对VideoView的实现机制有了一个比较深入的理解。经过整理发现，其实要自定义需求，还真不一定需要重新自己的VideoView实现。在本文中将原来的CustomerVideoView的方法全部删除，并继承VideoView，发现功能上没有什么不能实现的。继承的CustomerVideoView最后其实只剩下继承来的构造方法，也就是说直接使用VideoView也没问题。</p>
<p>这次演进，实现了自定义的播放控制条：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image3.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb3.png" width="244" height="148" /></a> </p>
<p>这个播放控制条，实际是替代了<a href="http://marshal.easymorse.com/archives/3160" title="编写简单自定义VideoView">编写简单自定义VideoView</a>中的MediaController。这样就可以自定义各种样式和风格的控制条界面了。</p>
<p> <span id="more-3164"></span>
<p>另外，本文示例中默认不出现播放控制条，当识别到横向手势的时候，才显示该滚动条，并且根据横向手势的x轴位移前进或者后退播放视频的位置。</p>
<p>如果手指单击视频，则停止播放，再单击继续播放。</p>
<p>CustomerVideoView已经简化到可以直接使用VideoView替代：</p>
<blockquote><p>package com.easymorse.videoplayer; </p>
<p>import android.content.Context;     <br />import android.util.AttributeSet;      <br />import android.widget.VideoView; </p>
<p>public class CustomerVideoView extends VideoView { </p>
<p>&#160;&#160;&#160; private static String TAG = &quot;customer.videoplayer&quot;; </p>
<p>&#160;&#160;&#160; public CustomerVideoView(Context context, AttributeSet attrs, int defStyle) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; super(context, attrs, defStyle);      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; public CustomerVideoView(Context context, AttributeSet attrs) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; super(context, attrs);      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; public CustomerVideoView(Context context) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; super(context);      <br />&#160;&#160;&#160; } </p>
<p>}</p>
<p>&#160;</p>
</blockquote>
<p>保留自定义CustomerVideoView的目的是：</p>
<ul>
<li>以后可以通过覆盖方法增加更加个性化的功能；</li>
<li>为什么要继承VideoView，是否能直接写自己的VideoView实现？这是一个问题，当然是可行的，比如mVideoPlayer就是这么干的，但是再查看VideoView源代码的时候，发现一些在Android源代码中公开的（public）类，在android sdk api中并没有，比如找不到android.media.Metadata类，调用MediaPlayer的时候也无法找到源代码中公开的（public）方法getMetadata方法，因此直接使用继承可以获得很多好处，间接使用未公开的api。</li>
</ul>
<p>有关播放控制条的布局：</p>
<blockquote><p>&lt;RelativeLayout android:id=&quot;@+id/mediaControllerLayout&quot;     <br />&#160;&#160;&#160; android:layout_height=&quot;55dip&quot; android:layout_width=&quot;fill_parent&quot;      <br />&#160;&#160;&#160; android:visibility=&quot;invisible&quot; android:layout_alignParentBottom=&quot;true&quot;&gt;      <br />&#160;&#160;&#160; &lt;View android:background=&quot;#50878787&quot; android:layout_width=&quot;fill_parent&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_height=&quot;1dip&quot; /&gt;      <br />&#160;&#160;&#160; &lt;ImageButton android:id=&quot;@+id/playButton&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_width=&quot;wrap_content&quot; android:src=&quot;@drawable/pause_button_gray&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_height=&quot;wrap_content&quot; android:layout_marginRight=&quot;15.0dip&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_alignParentRight=&quot;true&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_centerVertical=&quot;true&quot; /&gt;      <br />&#160;&#160;&#160; &lt;RelativeLayout android:layout_width=&quot;fill_parent&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_height=&quot;wrap_content&quot; android:layout_marginLeft=&quot;15.0dip&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_marginRight=&quot;15.0dip&quot; android:layout_centerVertical=&quot;true&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_toLeftOf=&quot;@id/playButton&quot;&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;SeekBar android:id=&quot;@+id/videoSeekBar&quot; android:focusable=&quot;false&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_width=&quot;fill_parent&quot; android:layout_height=&quot;wrap_content&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_marginBottom=&quot;5.0dip&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_alignParentBottom=&quot;true&quot; /&gt;      <br />&#160;&#160;&#160; &lt;/RelativeLayout&gt;      <br />&lt;/RelativeLayout&gt;</p>
</blockquote>
<p>在这里还能加入其他需要的信息，比如当前播放时间，总时间等等。SeekBar是使用默认样式的，可以指定自己的样式和thumb小图标。</p>
<p>为了实现横向手势指定播放进度功能，需要让activity实现OnGestureListener接口，以前写过一个简单的示例，<a href="http://marshal.easymorse.com/archives/2595" title="编写android简单的手势切换视图示例">编写android简单的手势切换视图示例</a>，可先参考了解。</p>
<p>在OnGestureListener中主要实现了以下方法。</p>
<p>onFling：</p>
<blockquote><p>@Override     <br />public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; float velocityY) {      <br />&#160;&#160;&#160; mediaControllerLayout.setVisibility(View.VISIBLE);      <br />&#160;&#160;&#160; handler.postDelayed(new Runnable() {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; @Override      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; public void run() {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mediaControllerLayout.setVisibility(View.INVISIBLE);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160; }, 1000);      <br />&#160;&#160;&#160; return false;      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>其实这个方法中的内容也可以实现在下面提到的onScroll方法中。onFling主要是处理有有关速度横向和纵向的手势。这里用来触发显示播放控制条界面。并且通过postDelayed方法在1秒钟后让控制条不可见。这里还可以改进，用动画来处理出现和消失的效果。</p>
<p>onSingleTapUp：</p>
<blockquote><p>@Override     <br />public boolean onSingleTapUp(MotionEvent e) {      <br />&#160;&#160;&#160; Toast.makeText(this, &quot;taped&quot;, Toast.LENGTH_SHORT).show();      <br />&#160;&#160;&#160; if (videoView.isPlaying()) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; videoView.pause();      <br />&#160;&#160;&#160; } else {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; videoView.start();      <br />&#160;&#160;&#160; }      <br />&#160;&#160;&#160; return false;      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>用来识别单击屏幕的手势，并做视频的暂停和继续播放。</p>
<p>onScroll：</p>
<blockquote><p>@Override     <br />public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; float distanceY) {      <br />&#160;&#160;&#160; if (distanceX &gt; 0) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.videoSeekBar      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .setProgress(this.videoSeekBar.getProgress() &#8211; 1);      <br />&#160;&#160;&#160; } else {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.videoSeekBar      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .setProgress(this.videoSeekBar.getProgress() + 1);      <br />&#160;&#160;&#160; }      <br />&#160;&#160;&#160; videoView.seekTo((int) (this.videoSeekBar.getProgress() * 1.0      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; / videoSeekBar.getMax() * videoView.getDuration()));      <br />&#160;&#160;&#160; return false;      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>识别手势横向左右位移，这里实现的很简单，进度条根据动作固定的为进度条加1%或者减1%。视频再根据滚动条做调整。正式的代码，应该根据位移的长度来适当的定位滚动条的位置。</p>
<p>手势的监听器要实现，还需要两件事情。</p>
<p>实例化GestureDetector：</p>
<blockquote><p>private GestureDetector gestureDetector;</p>
<p>this.gestureDetector = new GestureDetector(this);</p>
</blockquote>
<p>覆盖Activity的onTouchEvent方法：</p>
<blockquote><p>@Override     <br />public boolean onTouchEvent(MotionEvent event) {      <br />&#160;&#160;&#160; return this.gestureDetector.onTouchEvent(event);      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>这样才会把触摸事件转到手势监听器。</p>
<p>另外，也可以手动拨动滚动条上的thumb，需要通过下面方法支持：</p>
<blockquote><p>@Override     <br />public void onProgressChanged(SeekBar seekBar,      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; int progress, boolean fromUser) {      <br />&#160;&#160;&#160; if (fromUser) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; videoView.seekTo((int) (progress * 1.0      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; / seekBar.getMax() * videoView      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .getDuration()));      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; seekBar.setProgress(progress);      <br />&#160;&#160;&#160; }      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>源代码见：</p>
<blockquote><p><a title="http://easymorse.googlecode.com/svn/tags/videoplayer-0.5/" href="http://easymorse.googlecode.com/svn/tags/videoplayer-0.5/">http://easymorse.googlecode.com/svn/tags/videoplayer-0.5/</a></p>
</blockquote>
<p>进一步可以做的是：</p>
<ul>
<li>在播放器中保存用户播放视频中止时的播放位置，再次播放的时候从中止的位置播放；</li>
<li>设置menu项，用户可以清除保存的位置，这样可以从头播放；</li>
<li>屏幕的适配，目前是按照视频原生大小播放，可以增加拉伸适配全屏功能，并通过menu项切换；</li>
<li>视频的横屏和竖屏切花；</li>
<li>设置intent，这样可以在用户播放视频的时候，可以出现应用列表，用户可以选择android内置播放器以外的应用播放；</li>
<li>做一个比较好的动画，在单击暂停的时候播放。</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3164/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>编写简单自定义VideoView</title>
		<link>http://marshal.easymorse.com/archives/3160</link>
		<comments>http://marshal.easymorse.com/archives/3160#comments</comments>
		<pubDate>Wed, 01 Sep 2010 10:09:17 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[android surface]]></category>
		<category><![CDATA[android surfaceview]]></category>
		<category><![CDATA[android videoview]]></category>
		<category><![CDATA[android view]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3160</guid>
		<description><![CDATA[在简单定制VideoView中做了简单的VideoView定制，其实就是在布局上做了一些事情。要向更灵活的定制播放器的行为，必须写自己的VideoView。参考android VideoView源代码，写了个最简单的实现。
  
看起来和简单定制VideoView中的效果差不多，但是还有很多逻辑没有加进来，比如：

视频大小有问题，被拉长了，需要在后续版本中改进；
还没有加入MediaController，没有前进、后退、暂停等按钮界面。

 

自定义的VideoView源代码：
package com.easymorse.videoplayer; 
import android.content.Context;     import android.media.AudioManager;      import android.media.MediaPlayer;      import android.media.MediaPlayer.OnPreparedListener;      import android.net.Uri;      import android.util.AttributeSet;      import android.util.Log;      import [...]]]></description>
			<content:encoded><![CDATA[<p>在<a href="http://marshal.easymorse.com/archives/3153" title="简单定制VideoView">简单定制VideoView</a>中做了简单的VideoView定制，其实就是在布局上做了一些事情。要向更灵活的定制播放器的行为，必须写自己的VideoView。参考android VideoView源代码，写了个最简单的实现。</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" border="0" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb.png" width="244" height="148" /></a> <a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image1.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" border="0" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb1.png" width="244" height="148" /></a> </p>
<p>看起来和<a href="http://marshal.easymorse.com/archives/3153" title="简单定制VideoView">简单定制VideoView</a>中的效果差不多，但是还有很多逻辑没有加进来，比如：</p>
<ul>
<li>视频大小有问题，被拉长了，需要在后续版本中改进；</li>
<li>还没有加入MediaController，没有前进、后退、暂停等按钮界面。</li>
</ul>
<p> <span id="more-3160"></span>
</p>
<p>自定义的VideoView源代码：</p>
<blockquote><p>package com.easymorse.videoplayer; </p>
<p>import android.content.Context;     <br />import android.media.AudioManager;      <br />import android.media.MediaPlayer;      <br />import android.media.MediaPlayer.OnPreparedListener;      <br />import android.net.Uri;      <br />import android.util.AttributeSet;      <br />import android.util.Log;      <br />import android.view.SurfaceHolder;      <br />import android.view.SurfaceHolder.Callback;      <br />import android.view.SurfaceView;      <br />import android.view.View;      <br />import android.widget.MediaController;      <br />import android.widget.MediaController.MediaPlayerControl; </p>
<p>public class CustomerVideoView extends SurfaceView implements     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; MediaPlayerControl { </p>
<p>&#160;&#160;&#160; private static String TAG = &quot;customer.videoplayer&quot;; </p>
<p>&#160;&#160;&#160; private boolean pause; </p>
<p>&#160;&#160;&#160; private boolean seekBackward; </p>
<p>&#160;&#160;&#160; private boolean seekForward; </p>
<p>&#160;&#160;&#160; private Uri videoUri; </p>
<p>&#160;&#160;&#160; private MediaPlayer mediaPlayer; </p>
<p>&#160;&#160;&#160; private Context context; </p>
<p>&#160;&#160;&#160; private OnPreparedListener onPreparedListener; </p>
<p>&#160;&#160;&#160; private int videoWidth; </p>
<p>&#160;&#160;&#160; private int videoHeight; </p>
<p>&#160;&#160;&#160; private MediaController mediaController; </p>
<p>&#160;&#160;&#160; protected SurfaceHolder surfaceHolder; </p>
<p>&#160;&#160;&#160; private Callback surfaceHolderCallback = new SurfaceHolder.Callback() { </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; public void surfaceChanged(SurfaceHolder holder, int format, int w,     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; int h) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; public void surfaceCreated(SurfaceHolder holder) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; surfaceHolder = holder;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (mediaPlayer != null) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mediaPlayer.setDisplay(surfaceHolder);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; resume();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } else {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; openVideo();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; public void surfaceDestroyed(SurfaceHolder holder) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; surfaceHolder = null;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (mediaController != null) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mediaController.hide();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; release(true);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160; }; </p>
<p>&#160;&#160;&#160; private void release(boolean cleartargetstate) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (mediaPlayer != null) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mediaPlayer.reset();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mediaPlayer.release();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mediaPlayer = null;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; public void resume() {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (surfaceHolder == null) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (mediaPlayer != null) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; return;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; openVideo();      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; public CustomerVideoView(Context context, AttributeSet attrs, int defStyle) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; super(context, attrs, defStyle);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.context = context;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.initVideoView();      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; public CustomerVideoView(Context context, AttributeSet attrs) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; super(context, attrs);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.context = context;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.initVideoView();      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; public CustomerVideoView(Context context) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; super(context);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.context = context;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.initVideoView();      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; public boolean canPause() {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return this.pause;      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; public boolean canSeekBackward() {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return this.seekBackward;      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; public boolean canSeekForward() {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return this.seekForward;      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; public int getBufferPercentage() {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return 0;      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; public int getCurrentPosition() {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return mediaPlayer!=null?mediaPlayer.getCurrentPosition():0;      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; public int getDuration() {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return mediaPlayer!=null?mediaPlayer.getDuration():0;      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; public boolean isPlaying() {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; return false;      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; public void pause() {      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; public void seekTo(int mSec) {      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; public void start() {      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; public void setVideoURI(Uri uri) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.videoUri = uri;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; openVideo();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; requestLayout();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; invalidate();      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; private void openVideo() {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.mediaPlayer = new MediaPlayer();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; try {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.mediaPlayer.setDataSource(this.context, this.videoUri);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } catch (Exception e) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Log.e(TAG, e.getMessage());      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; throw new RuntimeException(e);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.mediaPlayer.prepareAsync();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.mediaPlayer.setOnPreparedListener(onPreparedListener);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; attachMediaController(); </p>
<p>&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; private void attachMediaController() {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (mediaPlayer != null &amp;&amp; mediaController != null) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mediaController.setMediaPlayer(this);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; View anchorView = this.getParent() instanceof View ? (View) this      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .getParent() : this;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mediaController.setAnchorView(anchorView);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mediaController.setEnabled(true);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; public void setMediaController(MediaController controller) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (mediaController != null) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mediaController.hide();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; mediaController = controller;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; attachMediaController();      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; public void setOnPreparedListener(OnPreparedListener onPreparedListener) {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.onPreparedListener = onPreparedListener;      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; int width = getDefaultSize(videoWidth, widthMeasureSpec);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; int height = getDefaultSize(videoHeight, heightMeasureSpec);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (videoWidth &gt; 0 &amp;&amp; videoHeight &gt; 0) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; if (videoWidth * height &gt; width * videoHeight) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; height = width * videoHeight / videoWidth;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; } else if (videoWidth * height &lt; width * videoHeight) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; width = height * videoWidth / videoHeight;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; Log.i(TAG, &quot;setting size: &quot; + width + &#8216;x&#8217; + height);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; setMeasuredDimension(width, height);      <br />&#160;&#160;&#160; } </p>
<p>&#160;&#160;&#160; private void initVideoView() {     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; videoWidth = 0;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; videoHeight = 0;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; getHolder().addCallback(surfaceHolderCallback);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; setFocusable(true);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; setFocusableInTouchMode(true);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; requestFocus();      <br />&#160;&#160;&#160; } </p>
<p>}</p>
<p>&#160;</p>
</blockquote>
<p>和VideoView实现类似，继承了SurfaceView并且实现了MediaPlayerControl。</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/09/image2.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" border="0" src="http://marshal.easymorse.com/wp-content/uploads/2010/09/image_thumb2.png" width="344" height="175" /></a> </p>
<p>一般情况下，android界面的绘制和更新，要交给主ui线程来操作，通过Handler机制。但是播放视频，需要比较优先和实时的改变和绘制界面。android提供了使用单独线程绘制UI的机制，就是SurfaceView。</p>
<p>使用SurfaceView，需要实现SurfaceHolder.Callback接口：</p>
<ul>
<li>surfaceCreated，在Surface（SurfaceView内部包含一个Surface实例）创建后，会立即调用该方法，可在该方法中做绘制界面相关的初始化工作；</li>
<li>surfaceChanged，当Surface的状态发生变化，比如大小，会调用该方法，在surfaceCreated方法调用过至少会调用一次该方法；</li>
<li>surfaceDestroyed，当销毁Surface的时候调用。</li>
</ul>
<p>开发者不能直接操作Surface实例，要通过SurfaceHandler，在SurfaceView中可以通过getHandler方法获取到SurfaceHandler实例。</p>
<p>SurfaceHander有一些类型，用来标识Surface实例界面数据来源，可以通过setType来操作：</p>
<ul>
<li>SURFACE_TYPE_NORMAL：RAM缓存的原生数据</li>
<li>SURFACE_TYPE_HARDWARE：通过DMA，direct memory access，就是直接写屏技术获取到的数据，或者其他硬件加速的数据</li>
<li>SURFACE_TYPE_GPU：通过GPU加速的数据</li>
<li>SURFACE_TYPE_PUSH_BUFFERS：标识数据来源于其他对象，比如照相机，比如视频播放服务器（android内部有视频播放的服务器，所有播放视频相当于客户端）</li>
</ul>
<p>CustomerVideoView的构造方法，使用超类的构造方法。都会执行initVideoView()方法用来初始化界面和参数。</p>
<p>另外一个主要的内容是openVideo()方法：</p>
<ul>
<li>mediaPlayer.prepareAsync()，用来异步准备播放，另外还有个prepare()方法，是同步的，也就是全部下载完毕才能播放，显然，在播放网上视频的时候需要用前者；</li>
<li>通过attachMediaController()方法，把控制条附加到播放视频的SurfaceView上，这里实现的不完全，因此还不能使用，仅仅是把MediaPlayerControl实例通过setMediaPlayer方法设置一下，供OnPreparedListener用来得到加载成功的回调，另外供外面代码调用得到视频的时长和当前时长。</li>
</ul>
<p>源代码见：</p>
<blockquote><p><a title="http://easymorse.googlecode.com/svn/tags/videoplayer-0.4/" href="http://easymorse.googlecode.com/svn/tags/videoplayer-0.4/">http://easymorse.googlecode.com/svn/tags/videoplayer-0.4/</a></p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3160/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>简单定制VideoView</title>
		<link>http://marshal.easymorse.com/archives/3153</link>
		<comments>http://marshal.easymorse.com/archives/3153#comments</comments>
		<pubDate>Tue, 31 Aug 2010 10:06:55 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[android layout]]></category>
		<category><![CDATA[android ui]]></category>
		<category><![CDATA[android videoview]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3153</guid>
		<description><![CDATA[在在播放视频前增加等待画面和进度提示中实现了简单的播放器，最近将逐渐演进，成为名副其实的定制版本播放器。本文做一个简单的定制，实际还没有动VedioView类，只是在布局上为播放器增加一些外围的信息提示。算是定制的热身运动。
效果：
  
通过相对布局，在加载的时候提示loading字样的文字，在加载到视频后，提示片名，当前时间，片子的时长和当前播放的时长。
 
首先要在原来布局文件上，加入一些TextView，用于显示提示文字，另外，要更换成相对布局（RelativeLayout），这样才能很容易确定位置。
&#60;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&#62;      &#60;FrameLayout android:layout_width=&#34;fill_parent&#34;       &#160;&#160;&#160; android:gravity=&#34;center&#34; android:layout_height=&#34;fill_parent&#34;       &#160;&#160;&#160; xmlns:android=&#34;http://schemas.android.com/apk/res/android&#34;&#62;       &#160;&#160;&#160; &#60;RelativeLayout android:layout_width=&#34;fill_parent&#34;       &#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_height=&#34;fill_parent&#34;&#62;       &#160;&#160;&#160;&#160;&#160;&#160;&#160; &#60;VideoView [...]]]></description>
			<content:encoded><![CDATA[<p>在<a href="http://marshal.easymorse.com/archives/3143" title="在播放视频前增加等待画面和进度提示">在播放视频前增加等待画面和进度提示</a>中实现了简单的播放器，最近将逐渐演进，成为名副其实的定制版本播放器。本文做一个简单的定制，实际还没有动VedioView类，只是在布局上为播放器增加一些外围的信息提示。算是定制的热身运动。</p>
<p>效果：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/08/image50.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/08/image_thumb50.png" width="244" height="148" /></a> <a href="http://marshal.easymorse.com/wp-content/uploads/2010/08/image51.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/08/image_thumb51.png" width="244" height="148" /></a> </p>
<p>通过相对布局，在加载的时候提示loading字样的文字，在加载到视频后，提示片名，当前时间，片子的时长和当前播放的时长。</p>
<p> <span id="more-3153"></span>
<p>首先要在原来布局文件上，加入一些TextView，用于显示提示文字，另外，要更换成相对布局（RelativeLayout），这样才能很容易确定位置。</p>
<blockquote><p>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;      <br />&lt;FrameLayout android:layout_width=&quot;fill_parent&quot;       <br />&#160;&#160;&#160; android:gravity=&quot;center&quot; android:layout_height=&quot;fill_parent&quot;       <br />&#160;&#160;&#160; xmlns:android=&quot;<a href="http://schemas.android.com/apk/res/android&quot;">http://schemas.android.com/apk/res/android&quot;</a>&gt;       <br />&#160;&#160;&#160; &lt;RelativeLayout android:layout_width=&quot;fill_parent&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_height=&quot;fill_parent&quot;&gt;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;VideoView android:id=&quot;@+id/videoView&quot; android:layout_width=&quot;fill_parent&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_height=&quot;fill_parent&quot; android:layout_centerHorizontal=&quot;true&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_centerVertical=&quot;true&quot; /&gt;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;TextView android:id=&quot;@+id/timeText&quot; android:focusable=&quot;false&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:visibility=&quot;invisible&quot; android:focusableInTouchMode=&quot;false&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_width=&quot;wrap_content&quot; android:layout_height=&quot;wrap_content&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_marginLeft=&quot;5.0dip&quot; android:layout_alignParentLeft=&quot;true&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_alignParentTop=&quot;true&quot; /&gt;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;TextView android:gravity=&quot;center&quot; android:id=&quot;@+id/videoTitle&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:focusable=&quot;false&quot; android:text=&quot;Loading &#8230;&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:focusableInTouchMode=&quot;false&quot; android:visibility=&quot;visible&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_width=&quot;wrap_content&quot; android:layout_height=&quot;wrap_content&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_marginLeft=&quot;10.0dip&quot; android:layout_marginRight=&quot;10.0dip&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_alignParentBottom=&quot;true&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_centerHorizontal=&quot;true&quot; /&gt;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;TextView android:id=&quot;@+id/clockText&quot; android:layout_width=&quot;wrap_content&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_height=&quot;wrap_content&quot; android:layout_marginLeft=&quot;5.0dip&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_marginRight=&quot;5.0dip&quot; android:layout_alignParentTop=&quot;true&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_alignParentRight=&quot;true&quot;&gt;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;/TextView&gt;       <br />&#160;&#160;&#160; &lt;/RelativeLayout&gt;       <br />&#160;&#160;&#160; &lt;LinearLayout android:layout_width=&quot;fill_parent&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:gravity=&quot;center&quot; android:layout_height=&quot;fill_parent&quot;&gt;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;ImageView android:id=&quot;@+id/image&quot; android:layout_height=&quot;wrap_content&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_width=&quot;wrap_content&quot; /&gt;       <br />&#160;&#160;&#160; &lt;/LinearLayout&gt;       <br />&lt;/FrameLayout&gt;</p>
</blockquote>
<p>代码上应该问题不大，就是将这些TextView，在加载完成前，不显示，加载后，再显示。</p>
<p>显示时间，有点儿特殊，比如片子的时长，或者当前进度的时间，都不是按照日期的时间。如果直接用DateFormat来做，将出现问题，比如时长是1分钟1秒，显示会变成8点01分01秒，这是因为中国处在东8区，因此比格林尼治时间早8小时。</p>
<p>那么怎么显示呢，也很简单：</p>
<blockquote><p>private static String getTimeFormatValue(long time) {      <br />&#160;&#160;&#160; return MessageFormat.format(       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; &quot;{0,number,00}:{1,number,00}:{2,number,00}&quot;,       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; time / 1000 / 60 / 60, time / 1000 / 60 % 60, time / 1000 % 60);       <br />}</p>
<p>&#160;</p>
</blockquote>
<p>源代码见：</p>
<blockquote><p><a title="http://easymorse.googlecode.com/svn/tags/videoplayer-0.3/" href="http://easymorse.googlecode.com/svn/tags/videoplayer-0.3/">http://easymorse.googlecode.com/svn/tags/videoplayer-0.3/</a></p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3153/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在mac环境下获取android源代码</title>
		<link>http://marshal.easymorse.com/archives/3147</link>
		<comments>http://marshal.easymorse.com/archives/3147#comments</comments>
		<pubDate>Mon, 30 Aug 2010 07:57:33 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[mac]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3147</guid>
		<description><![CDATA[首先，安装git。我是通过HomeBrew安装git的。安装HomeBrew见安装和使用HomeBrew。
安装git的命令：
brew install git

然后，通过git下载android源代码：
git clone git://android.git.kernel.org/platform/frameworks/base.git

]]></description>
			<content:encoded><![CDATA[<p>首先，安装git。我是通过HomeBrew安装git的。安装HomeBrew见<a href="http://marshal.easymorse.com/archives/3146" title="安装和使用HomeBrew">安装和使用HomeBrew</a>。</p>
<p>安装git的命令：</p>
<blockquote><p>brew install git</p>
</blockquote>
<p>然后，通过git下载android源代码：</p>
<blockquote><p>git clone git://android.git.kernel.org/platform/frameworks/base.git</p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3147/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>安装和使用HomeBrew</title>
		<link>http://marshal.easymorse.com/archives/3146</link>
		<comments>http://marshal.easymorse.com/archives/3146#comments</comments>
		<pubDate>Mon, 30 Aug 2010 07:42:07 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[homebrew]]></category>
		<category><![CDATA[mac]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3146</guid>
		<description><![CDATA[在ubuntu下使用惯了apt，在mac下，有MacPorts，但是有个问题，就是会强制安装很多重复的东西，MacPorts不会使用Mac机器上已经有的。
现在可以安装HomeBrew，它会检查本地是否有所需的工具，如果没有才下载编译安装。
安装过程很简单：
ruby -e &#34;$(curl -fsS http://gist.github.com/raw/323731/install_homebrew.rb)&#34;

HomeBrew是ruby脚本，但是mac下不需要安装ruby解释环境，系统自带了。
安装好后，可以：
brew –v

通过检查homebrew版本确认是否安装成功。
使用homebrew安装wget：
brew install wget

]]></description>
			<content:encoded><![CDATA[<p>在ubuntu下使用惯了apt，在mac下，有MacPorts，但是有个问题，就是会强制安装很多重复的东西，MacPorts不会使用Mac机器上已经有的。</p>
<p>现在可以安装HomeBrew，它会检查本地是否有所需的工具，如果没有才下载编译安装。</p>
<p>安装过程很简单：</p>
<blockquote><p>ruby -e &quot;$(curl -fsS http://gist.github.com/raw/323731/install_homebrew.rb)&quot;</p>
</blockquote>
<p>HomeBrew是ruby脚本，但是mac下不需要安装ruby解释环境，系统自带了。</p>
<p>安装好后，可以：</p>
<blockquote><p>brew –v</p>
</blockquote>
<p>通过检查homebrew版本确认是否安装成功。</p>
<p>使用homebrew安装wget：</p>
<blockquote><p>brew install wget</p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3146/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在播放视频前增加等待画面和进度提示</title>
		<link>http://marshal.easymorse.com/archives/3143</link>
		<comments>http://marshal.easymorse.com/archives/3143#comments</comments>
		<pubDate>Fri, 27 Aug 2010 03:11:55 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[android layout]]></category>
		<category><![CDATA[android ui]]></category>
		<category><![CDATA[android videoview]]></category>
		<category><![CDATA[android view]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3143</guid>
		<description><![CDATA[Android可以通过VideoView很方便的实现在线视频部分，可参见调用android api播放视频，不过该例子中的在线视频已经无法访问了。可使用这个源代码（实现了最简单的在线播放）：
http://easymorse.googlecode.com/svn/tags/videoplayer-0.1/

在线播放，可能需要等待片刻才能开始，或者有些客户需要在用户观看视频前看一下广告。比如：
 
然后：
 
 
基本思路是，VideoView放在一个FrameLayout中，因为FrameLayout是重叠放置视图组件的，再放一个ImageView和ProgressBar，然后注册VideoView的setOnPreparedListener监听器，当视频加载完毕后，让进图条和待机图片消失。
布局文件：
&#60;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&#62;     &#60;FrameLayout android:layout_width=&#34;fill_parent&#34;      &#160;&#160;&#160; android:gravity=&#34;center&#34; android:layout_height=&#34;fill_parent&#34;      &#160;&#160;&#160; xmlns:android=&#34;http://schemas.android.com/apk/res/android&#34;&#62;      &#160;&#160;&#160; &#60;LinearLayout android:layout_width=&#34;fill_parent&#34;      &#160;&#160;&#160;&#160;&#160;&#160;&#160; android:gravity=&#34;center&#34; android:layout_height=&#34;fill_parent&#34;&#62;      &#160;&#160;&#160;&#160;&#160;&#160;&#160; &#60;VideoView android:id=&#34;@+id/videoView&#34; android:layout_width=&#34;fill_parent&#34;   [...]]]></description>
			<content:encoded><![CDATA[<p>Android可以通过VideoView很方便的实现在线视频部分，可参见<a href="http://marshal.easymorse.com/archives/2043" title="调用android api播放视频">调用android api播放视频</a>，不过该例子中的在线视频已经无法访问了。可使用这个源代码（实现了最简单的在线播放）：</p>
<blockquote><p><a href="http://easymorse.googlecode.com/svn/tags/videoplayer-0.1/">http://easymorse.googlecode.com/svn/tags/videoplayer-0.1/</a></p>
</blockquote>
<p>在线播放，可能需要等待片刻才能开始，或者有些客户需要在用户观看视频前看一下广告。比如：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/08/image48.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/08/image_thumb48.png" width="315" height="191" /></a> </p>
<p>然后：</p>
<p><a href="http://marshal.easymorse.com/wp-content/uploads/2010/08/image49.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://marshal.easymorse.com/wp-content/uploads/2010/08/image_thumb49.png" width="321" height="194" /></a> </p>
<p> <span id="more-3143"></span>
<p>基本思路是，VideoView放在一个FrameLayout中，因为FrameLayout是重叠放置视图组件的，再放一个ImageView和ProgressBar，然后注册VideoView的setOnPreparedListener监听器，当视频加载完毕后，让进图条和待机图片消失。</p>
<p>布局文件：</p>
<blockquote><p>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;     <br />&lt;FrameLayout android:layout_width=&quot;fill_parent&quot;      <br />&#160;&#160;&#160; android:gravity=&quot;center&quot; android:layout_height=&quot;fill_parent&quot;      <br />&#160;&#160;&#160; xmlns:android=&quot;<a href="http://schemas.android.com/apk/res/android&quot;">http://schemas.android.com/apk/res/android&quot;</a>&gt;      <br />&#160;&#160;&#160; &lt;LinearLayout android:layout_width=&quot;fill_parent&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:gravity=&quot;center&quot; android:layout_height=&quot;fill_parent&quot;&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;VideoView android:id=&quot;@+id/videoView&quot; android:layout_width=&quot;fill_parent&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_height=&quot;fill_parent&quot; /&gt;      <br />&#160;&#160;&#160; &lt;/LinearLayout&gt;      <br />&#160;&#160;&#160; &lt;LinearLayout android:layout_width=&quot;fill_parent&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:gravity=&quot;center&quot; android:layout_height=&quot;fill_parent&quot;&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;ImageView android:id=&quot;@+id/image&quot; android:layout_height=&quot;wrap_content&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_width=&quot;wrap_content&quot; /&gt;      <br />&#160;&#160;&#160; &lt;/LinearLayout&gt;      <br />&#160;&#160;&#160; &lt;LinearLayout android:layout_width=&quot;fill_parent&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:gravity=&quot;center|bottom&quot; android:layout_height=&quot;fill_parent&quot;&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;ProgressBar android:id=&quot;@+id/progress&quot;       <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; android:layout_height=&quot;wrap_content&quot; android:layout_width=&quot;wrap_content&quot; /&gt;      <br />&#160;&#160;&#160; &lt;/LinearLayout&gt;      <br />&lt;/FrameLayout&gt;</p>
<p>&#160;</p>
</blockquote>
<p>有关监听器的代码：</p>
<blockquote><p>package com.easymorse.videoplayer; </p>
<p>import android.app.Activity;     <br />import android.media.MediaPlayer;      <br />import android.media.MediaPlayer.OnPreparedListener;      <br />import android.net.Uri;      <br />import android.os.Bundle;      <br />import android.view.View;      <br />import android.widget.ImageView;      <br />import android.widget.MediaController;      <br />import android.widget.ProgressBar;      <br />import android.widget.VideoView; </p>
<p>public class MediaPlayerActivity extends Activity {     <br />&#160;&#160;&#160; private VideoView videoView; </p>
<p>&#160;&#160;&#160; private ImageView imageView;     <br />&#160;&#160;&#160; private ProgressBar progressBar; </p>
<p>&#160;&#160;&#160; @Override     <br />&#160;&#160;&#160; protected void onCreate(Bundle savedInstanceState) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; super.onCreate(savedInstanceState);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.setContentView(R.layout.video); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.videoView = (VideoView) this.findViewById(R.id.videoView);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.imageView = (ImageView) this.findViewById(R.id.image);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.imageView.setImageResource(R.drawable.ad);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.progressBar=(ProgressBar) this.findViewById(R.id.progress); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; MediaController controller = new MediaController(this);     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.videoView.setMediaController(controller);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; this.videoView.setOnPreparedListener(new OnPreparedListener() {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; @Override      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; public void onPrepared(MediaPlayer mp) {      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; progressBar.setVisibility(View.GONE);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; imageView.setVisibility(View.GONE);      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; mp.start();      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; }      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; }); </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160; videoView     <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .setVideoURI(Uri      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; .parse(&quot;<a href="http://wangjun.easymorse.com/wp-content/video/mp4/tuzi.3gp&quot;));">http://wangjun.easymorse.com/wp-content/video/mp4/tuzi.3gp&quot;));</a>      <br />&#160;&#160;&#160; }      <br />}</p>
<p>&#160;</p>
</blockquote>
<p>源代码见：</p>
<blockquote><p><a href="http://easymorse.googlecode.com/svn/tags/videoplayer-0.2/">http://easymorse.googlecode.com/svn/tags/videoplayer-0.2/</a></p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3143/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>配置嵌入式的Activemq服务</title>
		<link>http://marshal.easymorse.com/archives/3138</link>
		<comments>http://marshal.easymorse.com/archives/3138#comments</comments>
		<pubDate>Wed, 25 Aug 2010 06:52:19 +0000</pubDate>
		<dc:creator>Marshal</dc:creator>
				<category><![CDATA[计算机技术]]></category>
		<category><![CDATA[activemq]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[java jms]]></category>

		<guid isPermaLink="false">http://marshal.easymorse.com/archives/3138</guid>
		<description><![CDATA[activemq提供了嵌入式的服务，即在一个JVM内部使用jms服务。我们用它来做异步发送消息。
JMS自身没有提供异步发送消息的机制，只能同步发送消息。这带来一个问题，比如外部（远端）的JMS服务器因为各种原因无法工作，那么发送消息的应用可能也连带的无法工作，这时消息会丢失。
可以使用activemq的嵌入式的服务，先同步发送消息到本地（嵌入式activemq服务），然后再用一个接收线程接收本地消息，发送到远端的JMS服务，这样即使远端JMS服务失效，消息也不会丢失，当远端服务器恢复后还能继续收到消息。
 
spring文件的头部要求有：
&#60;beans xmlns=&#34;http://www.springframework.org/schema/beans&#34;      &#160;&#160;&#160; xmlns:context=&#34;http://www.springframework.org/schema/context&#34;      &#160;&#160;  xmlns:amq=&#34;http://activemq.apache.org/schema/core&#34; xmlns:xsi=&#34;http://www.w3.org/2001/XMLSchema-instance&#34;      &#160;&#160;&#160; xmlns:aop=&#34;http://www.springframework.org/schema/aop&#34; xmlns:tx=&#34;http://www.springframework.org/schema/tx&#34;      &#160;&#160;&#160; xmlns:util=&#34;http://www.springframework.org/schema/util&#34;      &#160;&#160;&#160; xsi:schemaLocation=&#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd      http://www.springframework.org/schema/util [...]]]></description>
			<content:encoded><![CDATA[<p>activemq提供了嵌入式的服务，即在一个JVM内部使用jms服务。我们用它来做异步发送消息。</p>
<p>JMS自身没有提供异步发送消息的机制，只能同步发送消息。这带来一个问题，比如外部（远端）的JMS服务器因为各种原因无法工作，那么发送消息的应用可能也连带的无法工作，这时消息会丢失。</p>
<p>可以使用activemq的嵌入式的服务，先同步发送消息到本地（嵌入式activemq服务），然后再用一个接收线程接收本地消息，发送到远端的JMS服务，这样即使远端JMS服务失效，消息也不会丢失，当远端服务器恢复后还能继续收到消息。</p>
<p> <span id="more-3138"></span>
<p>spring文件的头部要求有：</p>
<blockquote><p>&lt;beans xmlns=&quot;<a href="http://www.springframework.org/schema/beans&quot;">http://www.springframework.org/schema/beans&quot;</a>      <br />&#160;&#160;&#160; xmlns:context=&quot;<a href="http://www.springframework.org/schema/context&quot;">http://www.springframework.org/schema/context&quot;</a>      <br /><em>&#160;&#160; <strong> xmlns:amq=&quot;</strong></em><a href="http://activemq.apache.org/schema/core&quot;"><em>http://activemq.apache.org/schema/core&quot;</em></a> xmlns:xsi=&quot;<a href="http://www.w3.org/2001/XMLSchema-instance&quot;">http://www.w3.org/2001/XMLSchema-instance&quot;</a>      <br />&#160;&#160;&#160; xmlns:aop=&quot;<a href="http://www.springframework.org/schema/aop&quot;">http://www.springframework.org/schema/aop&quot;</a> xmlns:tx=&quot;<a href="http://www.springframework.org/schema/tx&quot;">http://www.springframework.org/schema/tx&quot;</a>      <br />&#160;&#160;&#160; xmlns:util=&quot;<a href="http://www.springframework.org/schema/util&quot;">http://www.springframework.org/schema/util&quot;</a>      <br />&#160;&#160;&#160; xsi:schemaLocation=&quot;<a href="http://www.springframework.org/schema/beans">http://www.springframework.org/schema/beans</a> <a href="http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">http://www.springframework.org/schema/beans/spring-beans-3.0.xsd</a>      <br /><a href="http://www.springframework.org/schema/tx">http://www.springframework.org/schema/tx</a> <a href="http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">http://www.springframework.org/schema/tx/spring-tx-3.0.xsd</a>      <br /><a href="http://www.springframework.org/schema/util">http://www.springframework.org/schema/util</a> <a href="http://www.springframework.org/schema/util/spring-util-3.0.xsd">http://www.springframework.org/schema/util/spring-util-3.0.xsd</a>      <br /><a href="http://www.springframework.org/schema/aop">http://www.springframework.org/schema/aop</a> <a href="http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">http://www.springframework.org/schema/aop/spring-aop-3.0.xsd</a>      <br /><a href="http://www.springframework.org/schema/context">http://www.springframework.org/schema/context</a> <a href="http://www.springframework.org/schema/context/spring-context-3.0.xsd">http://www.springframework.org/schema/context/spring-context-3.0.xsd</a>      <br /><a href="http://activemq.apache.org/schema/core"><em>http://activemq.apache.org/schema/core</em></a><em> </em><a href="http://activemq.apache.org/schema/core/activemq-core-5.3.0.xsd&quot;"><em>http://activemq.apache.org/schema/core/activemq-core-5.3.0.xsd&quot;</em></a>&gt;</p>
</blockquote>
<p>对xmlns:amq的schema声明，分两部分：</p>
<blockquote><p>xmlns:amq=&quot;<a href="http://activemq.apache.org/schema/core&quot;">http://activemq.apache.org/schema/core&quot;</a> xmlns:xsi=<a href="http://www.w3.org/2001/XMLSchema-instance">http://www.w3.org/2001/XMLSchema-instance</a></p>
</blockquote>
<p>和：</p>
<blockquote><p><a href="http://activemq.apache.org/schema/core">http://activemq.apache.org/schema/core</a> <a href="http://activemq.apache.org/schema/core/activemq-core-5.3.0.xsd">http://activemq.apache.org/schema/core/activemq-core-5.3.0.xsd</a></p>
</blockquote>
<p>另外，因为使用到了xbean的功能，需要引入包，比如maven下：</p>
<blockquote><p>&lt;dependency&gt;     <br />&#160;&#160;&#160; &lt;groupId&gt;org.apache.xbean&lt;/groupId&gt;      <br />&#160;&#160;&#160; &lt;artifactId&gt;xbean-spring&lt;/artifactId&gt;      <br />&#160;&#160;&#160; &lt;version&gt;3.7&lt;/version&gt;      <br />&lt;/dependency&gt;</p>
</blockquote>
<p>在spring中和ConnectionFactory相关的配置：</p>
<blockquote><p>&lt;amq:broker useJmx=&quot;false&quot; persistent=&quot;true&quot; brokerName=&quot;localhost&quot;&gt;     <br />&#160;&#160;&#160; &lt;amq:persistenceAdapter&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;amq:amqPersistenceAdapter directory=&quot;activemq-data&quot;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; indexPageSize=&quot;64kb&quot; indexBinSize=&quot;4096&quot; maxFileLength=&quot;2147483647&quot; /&gt;      <br />&#160;&#160;&#160; &lt;/amq:persistenceAdapter&gt;      <br />&#160;&#160;&#160; &lt;amq:transportConnectors&gt;      <br />&#160;&#160;&#160;&#160;&#160;&#160;&#160; &lt;amq:transportConnector name=&quot;vm&quot; uri=&quot;vm://localhost&quot; /&gt;      <br />&#160;&#160;&#160; &lt;/amq:transportConnectors&gt;      <br />&lt;/amq:broker&gt;      <br />&lt;amq:connectionFactory id=&quot;embeddedConnectionFactory&quot;      <br />&#160;&#160;&#160; brokerURL=&quot;vm://localhost&quot; /&gt;</p>
<p>&#160;</p>
</blockquote>
<p>这里使用了amq的persistent adapter，文件最大值是int型的，最大值被限制在int.MAX_VALUE，只能到2147483647，这里使用了这个最大值，不到2GB。</p>
<p>如果需要加大文件值，需要使用比如kapa persistent adapter，它的最大值是long型的。</p>
]]></content:encoded>
			<wfw:commentRss>http://marshal.easymorse.com/archives/3138/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
