1:新闻实现的主要功能
2:新闻加载及更新
3:进度条的实现
4:后台播放新闻服务(Service和BroadcastReceiver的使用)
5:AIDL的使用
一:主要功能描述:
启动新闻后开始更新所有新闻,更新后系统会自动查找还没播放的新闻,并开始播放。新闻播放之前,播放按钮不显示。未播放过的新闻字体为白色,播放过的新闻为灰色。播放时,播放的当前行以进度条的形式显示播放进度,字体为白色。未播放行为字体为灰色,背景色为黑色和深灰色。未播放行只显示新闻标题,播放行必须显示一个播放Icon,新闻标题,当前播放时间和新闻更新时间。
单击前进播放按钮自动播放前一条新闻并刷新界面选中当前播放行,单击后退播放按钮自动播放后一条新闻并刷新界面选中当前播放行。单击暂停按钮则停止播放新闻, 所有的播放按钮处于不可见状态。
单击新闻时可以播放该行新闻,重新单击重新播放。
当有新闻在播放时,单击返回按钮后者其他类别选项时并不影响后台播放新闻的进度,新闻继续播放。
二:新闻加载及更新:
从网上下载最新新闻存入数据库,再将数据库中的新闻数据加载显示到UI界面上。
每隔一段时间,后台服务会自动更新新闻。实现方法是:外部(类与类之间)采用BroadcastReceiver 和 Service的结合使用,内部(类中)则才采用Handler与Message的结合使用。
三:进度条的实现:
Xml文件的实现:在styles.xml文件中定义ProgressBar的style类型(如minHeight,maxHeight,progressDrawable等参数)。其中progressDrawable的完整定义利用progress_bar.xml文件实现。Progress_bar.xml文件中主要定义ProgressBar的形状。然后在newslist_row.xml文件中定义ProgressBar视图组件。
Styles.xml:
<style name=”progressbar”>
<item name=“android:indeterminateOnly”>false</item>
<item name=”android:progressDrawable”>@drawable/progress_bar</item>
<item name=”android:indeterminateDrawable”>@null</item>
<item name=”android:minHeight”>72px</item>
<item name=”android:maxHeight”>72px</item>
</style>
Progress_bar.xml:
<?xml version=”1.0” encoding=”utf-8”?>
<layer-list xmlns:android=http://schemas.android.com/apk/res/android>
<item
android:id=”@android:id/background” android:drawable=”@drawable/list_gray” />
<item android:id=”@android:id/secondaryProgress”>
<clip>
<shape>
<corners android:radius=”5dip” />
<gradient
android:startColor=”#80ffd300”
android:centerColor=”#80ffb600”
android:centerY=”0.75”
android:endColor=”#a0ffcb00”
android:angle=”270” />
</shape>
</clip>
</item>
<item android:id=”@android:id/progress”>
<clip android:drawable=”@drawable/list_blue” >
</clip>
</item>
</layer-list>
newslist_row.xml
<ProgressBar andorid:id=”@+id/progress_button”
andorid:layout_width=”540dip” android:layout_height=”wrap_content”
android:progressDrawable=”@+drawable/progress_bar”
style=”@+style/progressbar”
android:max=”100” android:progress=”1”
android:layout_alignParentLeft=”true”
android:layout_marginTop=”0px” />
Java文件的实现:采用Handler和Message每隔1000秒刷新一次进度条进度,直到下条新闻开始播放又重新开始刷新。
每次在bindView中调用doProgress(cur_number)方法:
private ProgressBar proBar = null;
public void doProgress(int cur_number){
proBar.setMax(100);
proBar.setProgress(0);
if (cur_number >= 0 && isPlay()){
refresh();
}
}
private void refresh(){
curTime = System.currentTimeMillis();
totalTime = curTime – beginTime;
try{
progressTime = ((int)totalTime * proBar.getMax() /
(int)(contentLen * 250));
}catch(Exception e){}
proBar.setProgress(progressTime);
Message message = handler.obtainMessage(25);
handler.removeMessages(25);
handler.sendMessageDelayed(message, 1000);
}
然后在Handler接收处理中再次调用refresh()方法。其中contentLen为新闻内容的总长度。
四:后台播放新闻服务(Service和BroadcastReceiver的使用):
(1) 新闻播放:响应新闻单击事件——发送Message消息——后台播放新闻。
后台播放新闻:
1)使用AIDL实现IPC通信服务:
A) 创建INewsService.aidl文件,在这个接口文件中定义一些实现控制播放新闻的方法。
B)在NewsService中创建一个类以扩展这个接口的Stub内部抽象类。并实现远程调用.aidl文件中定义的几个方法。
如:INewsService.Stub mBinder = new InewsService.Stub()
C)向客户端开放端口,在NewsService中重载onBind(Intent)方法,并返回实现上述接口的类的一个实例mBinder。
2) 调用类调用远程接口:
A) 声明一个接口类型的变量mService:InewsService mService = null;
B)实现ServiceConnection。重载onServiceDisconnected方法。
C)调用Context.bindService(),并在ServiceConnection实现中进行传递。这一步在NewsObj.java中实现。
D) 将在onServiceConnected()(NewsObj.java)实现中收到的Ibinder的实例转换成INewsService类型。
E)调用INewsService中定义的方法。一定要捕获DeadObjectException异常
F)断开服务连接,调用接口实例中的Context.unbindService()方法(NewsObj.java)。
(2) Service
运行于应用程序进程的主线程中,因此Service不会阻塞其他组件和用户界面。
Service是不能自己启动的,必须通过Context对象(如一个Activity)调用startService或bindService方法来启动(用这两种方法启动的Service的生命周期不同)。
1.调用startService方法
a)若Service没有启动,则首先会调用该Service的onCreate方法,然后再调用onStart方法。
b)若Service已经启动,则会直接调用onStart方法
c)该方法启动的Service,可以通过Context对象调用stopService来关闭,也可以通过Service自身调用stopSelf()或stopSelfResult()来关闭,关闭之前调用onDestory方法。
2.调用bindService方法,使当前Context对象通过一个ServiceConnection的对象绑定到所指定的Service
a)若Service没有启动,则首先会调用该Service的onCreate方法初始化启动,然后调用Service的onBind方法初始化绑定。
b)如果绑定Service的Context对象被销毁时,被绑定的Service也会调用onUnbind 和 onDestroy方法停止运行
c)注意: BroadcastReceiver是不能绑定服务的。
d)一个绑定Service的Context对象还可以通过unbindService()来取消对服务的绑定。
e)取消时,Service会调用unbind方法,若Service是通过bindService来启动的,还会调用onDestroy方法来停止服务。
Service状态回调:
onCreate ——onStart——onBind——onRebind——onUnbind——onDestroy 。
(3)BroadcastReceiver
A) 在NewsService中的onCreate方法中注册要发生的Action及接收Action的BroadcastReceiver。
在接收到的Action处理中发送Message,然后在Handler处理中广播这些Action,利用sendBroadcast(Intent)。
在newsmain.java中接收NewsService中的Action。在此之前必须调用NewsObj.bindToService(this, this)方法。
B) Android中的广播要么来自于系统,要么来自普通应用程序。
很多事件都可能导致系统广播,如手机所在时区发生变化,电池电量低,用户改变系统语言设置等。来自普通应用程序,如一个应用程序通知其他应用程序某些数据已经下载完毕。为了响应不同的事件通知,应用程序可以注册不同的BroadcastReceiver。所有的BroadcastReceiver都继承自基类BroadcastReceiver。
BroadcastReceiver自身并不实现图形用户界面,但是当它收到某个通知后,BroadcastReceiver可以启动Activity作为响应,或者通过NotificationMananger提醒用户。BroadcastReceiver是对发送出来的Broadcast进行过滤接收并响应的一类组件。
发送Broadcast信息
1.把要发送的信息和用于过滤得信息(如Action、Category)装入一个Intent对象
2.调用Context.sendBroadcast()、sendOrderBroadcast()、sendStickyBroadcast()方法,广播该Intent对象
3.使用sendBroadcast() 或sendStickyBroadcast()方法发出去的Intent,所有满足条件的BroadcastReceiver都会随机地执行其onReceive()方法;
4.而sendOrderBroadcast()发出去的Intent,会根据BroadcastReceiver注册时IntentFilter设置的优先级的顺序来执行,相同优先级的BroadcastReceiver则是随机执行
5.sendStickyBroadcast()方法主要的不同是,Intent在发送后一直存在,并且在以后调用registerReceiver()注册相匹配的Intent时会把这个Intent直接返回。
6.若在使用sendBroadcast()方法时指定了接收的权限,这只有在AndroidManifest.xml中用<uses-permission>标签声明了拥有此权限的BroadcastReceiver才会有可能接收到发送来的Broadcast。
7.若在注册BroadcastReciever时,指定了可接收的Broadcast的权限,则只有在包内的AndroidManifest.xml中用<uses-permission>标签声明了,拥有此权限的Context对象所发送的Broadcast才有可能被这个BroadcastReceiver所接收。
接收Broadcast消息
1.继承BroadcastReceiver 类,并实现onReceive方法
2.注册BroadcastReceiver(有2种方法:一种方法是,静态地在AndroidManifest.xml中用<receiver>标签声明,并在标签内用<intent-filter>标签设置过滤器; 另一种方法,动态地在代码中先定义并设置好一个IntentFilter对象,然后再需要注册的地方调用Context.registerReceiver()方法) (取消注册时,调用Context.unregisterReceiver()方法)
错误总结:
1:刷新界面
因为播放的新闻不定时的改变,所以每次事件引起UI发生时要调用list.invalidateViews( )方法,每次调用list.invalidateViews( )方法时会重新bindView界面以达到刷新的效果。
2:Handler 与 Message的使用
3:Broadcast与 BroadcastReceiver的使用
4:UI界面的改变不影响后台新闻的播放,当用户单击返回按钮后新闻继续播放,重新进入应用后继续播放上次的新闻。所以必须对当前后台播放的新闻所属类别与当前的position做判断。
5:进度条进度值的设置。progressTime表示进度,必须声明为static类型。
6:新闻奇数行与偶数行的背景色不一样,可以利用position求余的方法选择背景色的设置。
if(p%2 == 0){
}else{
}
7:实现进度条时最好使用Handler和Message间隔性的刷新进度,避免使用线程以防不断单击新闻创建了多个线程,增加了CPU开销。
8:调用AIDL中定义的方法时最好捕获DeadObjectException异常。
9:View.GONE ,View.INVISIBLE 和View.VISIBLE的正确使用。
10:重新设置View的LayoutParams时必须导入正确的包。
布局及特效 gallery3d 的精华
一、布局
gallery3d的界面生成和普通的应用程序不一样。普通程序一般一个界面就是一个activity,布局用xml或代码都可以实现,界面切换是activity的切换方式;而gallery3d没有用android的UI系统,而是用opengl画出来的,即界面是在同一个 activity的,如主界面,缩略图界面,单张图片查看界面,标记界面等都属于同一个activity。那么这界面布局不同的界面是如何组合到一起的呢?分析代码,可以把它看成一个状态机:
1、标记模式 public static final int MODE_SELECT = 1;(HudLayer)
包含了主界面标记模式,缩略界面矩阵游览时标记模式、缩略图界面分类游览时标记模式3个界面
2、普通模式 public static final int MODE_NORMAL = 0;(HudLayer)
包含了
public static final int STATE_MEDIA_SETS = 0;主界面 public static final int STATE_GRID_VIEW = 1;缩略图矩阵浏览 public static final int STATE_FULL_SCREEN = 2;查看界面 public static final int STATE_TIMELINE = 3;缩略图界面分类浏览
有了以上状态分类后,在渲染的时候就能根据些界面的组成来定哪些控件譔隐藏,哪些要显示了。
下面是基本控件:
com.cooliris.media.GridLayer com.cooliris.media.BackgroundLayer com.cooliris.media.HudLayer com.cooliris.media.ImageButton com.cooliris.media.TimeBar com.cooliris.media.MenuBar com.cooliris.media.PopupMenu com.cooliris.media.PathBarLayer
在渲染时,每一帧所有界面上的元素都画了,由于根据上面的状态只把特定窗口的特定元素显示出来,其它窗口中的隐藏,所以不会乱。
Layer是上面控件的基类,上面控件的类也就有了下面两个方法来隐藏不譔显示的界面元素。
public boolean isHidden() { return mHidden; } public void setHidden(boolean hidden) { if (mHidden != hidden) { mHidden = hidden; onHiddenChanged(); } }
下面是根据上面分类来画不同元素所用的标识:
public static final int PASS_THUMBNAIL_CONTENT = 0; public static final int PASS_FOCUS_CONTENT = 1; public static final int PASS_FRAME = 2; public static final int PASS_PLACEHOLDER = 3; public static final int PASS_FRAME_PLACEHOLDER = 4; public static final int PASS_TEXT_LABEL = 5; public static final int PASS_SELECTION_LABEL = 6; public static final int PASS_VIDEO_LABEL = 7; public static final int PASS_LOCATION_LABEL = 8; public static final int PASS_MEDIASET_SOURCE_LABEL = 9;
drawDisplayItem(view, gl, displayItem, texture, PASS_THUMBNAIL_CONTENT, placeholder,displayItem.mAnimatedPlaceholderFade); 画缩略图的,注掉此句,前两屏只显示框,第三屏OK drawDisplayItem(view, gl, displayItem, texture, PASS_FOCUS_CONTENT, null, 0.0f);画单张图片的,注掉,第三屏黑屏 drawDisplayItem(view, gl, itemDrawn, textureToUse, PASS_FRAME, previousTexture, ratio);画边框的,注掉,前两屏明显没有边框,巨齿明显 drawDisplayItem(view, gl, displayItem, textureString, PASS_TEXT_LABEL, null, 0);画文本标签的 drawDisplayItem(view, gl, displayItem, textureToUse, PASS_SELECTION_LABEL, null, 0);画选中标记的 drawDisplayItem(view, gl, displayItem, videoTexture, PASS_VIDEO_LABEL, null, 0);画视频标记的 drawDisplayItem(view, gl, displayItem, locationTexture, PASS_LOCATION_LABEL, null, 0);画位置标记的 drawDisplayItem(view, gl, displayItem, locationTexture, PASS_MEDIASET_SOURCE_LABEL,transparentTexture, 0.85f);画源来源图标的(相机或一般文件夹)
二、特效
举如何显示一张图片为例,在图片完全显示出来经过这样一个过程,附近的图片渐小渐出,当前图片渐大渐入,当前图片逐渐变大直到全屏。实现这个特效,要进行很多帧的渲染。就是说并不是只调一次onDrawFrame函数就可以了,要调用多次。可以把这个特效的实现想成一个状态变化的过程,在每一个状态,纹理的显示大小和位置都不同,这也符合动画的基本原理。放大、缩小我们只要改变顶点数据就可以做到,gallery3d也是这样做的,下面是主要代码:
我们知道调用onDrawFrame来渲染,最后调到下面的drawFocusItems函数,
GridQuad quad = GridDrawables.sFullscreenGrid[vboIndex]; float u = texture.getNormalizedWidth(); float v = texture.getNormalizedHeight(); float imageWidth = texture.getWidth(); float imageHeight = texture.getHeight(); boolean portrait = ((theta / 90) % 2 == 1); if (portrait) { viewAspect = 1.0f / viewAspect; } quad.resizeQuad(viewAspect, u, v, imageWidth, imageHeight);//改变用来贴图片的长方形的大小 quad.bindArrays(gl);//绑定新数据,为渲染做准备。
而位置的改变有两种方式,一种是直接以顶点数据中改变,另一种是计算出在3维3个方向的偏移量,再调用gltranslate来做,从代码可以看出采用的是第二种方式来做的,比第一种方式更方便一些。代码:
gl.glTranslatef(-translateXf, -translateYf, -translateZf);
而这里的3个偏移量的计算是和camera相关的,相关文件为GridCamera.java,GridCameraManager.java,过程很复杂,理清楚后再细化吧。
cache管理
下面是cache文件
/sdcard/Android/data/com.cooliris.media/cache/local-album-cache d---rwxr-x system sdcard_rw 2010-05-21 09:56 local-album-cache d---rwxr-x system sdcard_rw 2010-05-21 09:56 local-meta-cache ----rwxr-x system sdcard_rw 299877 2010-05-28 07:36 local-album-cachechunk_0 d---rwxr-x system sdcard_rw 2010-05-21 09:56 geocoder-cache ----rwxr-x system sdcard_rw 284 2010-05-28 07:36 local-album-cacheindex d---rwxr-x system sdcard_rw 2010-05-21 09:56 local-image-thumbs d---rwxr-x system sdcard_rw 2010-05-21 09:56 local-video-thumbs d---rwxr-x system sdcard_rw 2010-05-21 09:56 picasa-thumbs ----rwxr-x system sdcard_rw 80 2010-05-28 07:36 local-meta-cachechunk_0 ----rwxr-x system sdcard_rw 164 2010-05-28 07:36 local-meta-cacheindex d---rwxr-x system sdcard_rw 2010-05-21 09:56 hires-image-cache ----rwxr-x system sdcard_rw 627629 2010-05-28 07:37 local-image-thumbschunk_0 ----rwxr-x system sdcard_rw 3914 2010-05-21 09:56 local-image-thumbsindex ----rwxr-x system sdcard_rw 53343 2010-05-28 07:34 hires-image-cache-4982941342287215583_1024.cache ----rwxr-x system sdcard_rw 237692 2010-05-28 07:33 hires-image-cache3684568484369117627_1024.cache ----rwxr-x system sdcard_rw 133182 2010-05-28 07:34 hires-image-cache607542544081226432_1024.cache ----rwxr-x system sdcard_rw 83223 2010-05-28 07:34 hires-image-cache4275479623210216146_1024.cache ----rwxr-x system sdcard_rw 292837 2010-05-28 07:34 hires-image-cache-646316556936433937_1024.cache ----rwxr-x system sdcard_rw 191377 2010-05-28 07:35 hires-image-cache2631364604509958174_1024.cache ----rwxr-x system sdcard_rw 366905 2010-05-28 07:35 hires-image-cache-3280562009766080884_1024.cache ----rwxr-x system sdcard_rw 323671 2010-05-28 07:35 hires-image-cache5752471827533329222_1024.cache
创建cache的关键代码
LocalDataSource public static final DiskCache sThumbnailCache = new DiskCache("local-image-thumbs");----------------------local-image-thumbs local-image-thumbschunk_0 local-image-thumbsindex public static final DiskCache sThumbnailCacheVideo = new DiskCache("local-video-thumbs");--------------------local-video-thumbs public static final DiskCache sAlbumCache = new DiskCache("local-album-cache");----------------------local-album-cache local-album-cacheindex public static final DiskCache sMetaAlbumCache = new DiskCache("local-meta-cache");------------------local-meta-cache local-meta-cacheindex getChunkFile --------------local-meta-cachechunk_0 local-album-cachechunk_0 ReverseGeocoder:: private static final DiskCache sGeoCache = new DiskCache("geocoder-cache"); -------------------------geocoder-cache PicasaDataSource:: public static final DiskCache sThumbnailCache = new DiskCache("picasa-thumbs");-----------------------------picasa-thumbs UriTexture::writeToCache --------------------------hires-image-cache-xxx_1024.cache
布局补充:
在画一个界面是,是分类化的,比如第一个界面是显示所有有图片的文件夹,在代码里叫专辑.有这些元素要创建:
文本标签 显示专辑名和专辑内图片或视频数
路径条 显示路径名
按纽 拍照按纽,放大/缩小
菜单栏 全选,取消全选,分享,删除,更多等
图片边框
用于显示图片的矩形
在渲染时一次把一类元素画完,再画另一类.如主界面顺序为:
路径条->按纽->文本标签->图片边框->图片.
具体代码见drawBlendedComponents函数
1.CacheService.java 中写缓存: sAlbumCache.put(ALBUM_CACHE_LOCALE_INDEX, sDummyData, 0);
第一个是 key ,这里是正常数据,当然还有别的 key , key 分别是 -1,-2,-3,-4,-5 。
2.DiskCache.java 中,执行上面的写的过程,这里先得明白他的 cache 怎么装的:
它是由很多称之为“片”的文件组成的,形成一个 List 形式: private final
LongSparseArray<RandomAccessFile> mChunkFiles = new LongSparseArray<RandomAccessFile>();
即 mChuckFiles 就是整个 cache ,里面包括很多 chunk( 即片 ) ,每一个 chunk 大小为 1MB.
当要写入某一个 chunk 里面的时候,先要找到他在 mChuckFiles 里面的索引值即 chunkIndex, 由
mChunkFiles.get(chunkIndex); 来获取这个文件, chunkIndex 怎么来的呢?
private LongSparseArray<Record> mIndexMap;
Record record = mIndexMap.get(key); 这里的 key 就是上面用 put 方法传过来的
ALBUM_CACHE_LOCALE_INDEX 的值(就是 -5 )
int chunkIndex = record.chunk;
这么一步步来的。
当然了,第一次都是空的,也就是 get 不到东西 mChunkFiles.get(chunkIndex); 和 Record record =
mIndexMap.get(key); 都 get 不到,那么第一次就先把东西放进去, mIndexMap.put(key, new Record
(chunkIndex, record.offset, data.length, record.sizeOnDisk, timestamp)); (记录 key 值)以及 final
String chunkFilePath = mCacheDirectoryPath + CHUNK_FILE_PREFIX + chunk; chunkFile = new
RandomAccessFile(chunkFilePath, "rw");mChunkFiles.put(chunk, chunkFile); (三句代码来新建一个
chunkfile 并放到 cache 列表里面)
注意: Record 是内部类,只是一个数据集合类而已,相当于文件描述信息。每个 cache (即 chunk )对应一个。
private final LongSparseArray<RandomAccessFile> mChunkFiles = new LongSparseArray<RandomAccessFile>(); 中 mChunkFiles 最大装 13 个,每个 chunk 是 1M, 所以全部 Cache 是 13M.
所以,想请教你。 可以加个QQ么。 你的QQ是多少。
如果你直接加我的QQ那就更好了。我的QQ:82455896.
final class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.endsWith(".mp4") {
Intent intent = new Intent("android.intent.action.VIEW", Uri.parse(url));
view.getContext().startActivity(intent);
return true;
} else {
return super.shouldOverrideUrlLoading(view, url);
}
}
}
WebViewClient webViewClient = new MyWebViewClient();
webView.setWebViewClient(webViewClient);
加入
webView.getSettings().setAllowFileAccess(true);