LTextureList,是一个与前例介绍的LTexturePack形似而神异的不同存在。
两者的差异在于:
LTexturePack专注于“分散纹理的统一管理,会将注入当中的所有小图合并为一”。
LTextureList专注于“分散纹理的分散管理,会保留所有小图的引用而分别调用”。
事实上,由于Android中提供个单独程序的图像空间非常有限,无论怎么有效释放,Bitmap都不能同时存在太多。而LTextureList就是专为处理海量图片所准备的,在配置了相关xml文档后,LTextureList将仅在执行对应名称纹理的loadTexture时,才会加载LTexture(也就是所谓的“惰性加载”)。而不会一次性加载完毕,且加载一次后会立即产生缓存,只要不注销资源,即使再次loadTexture也不会重复加载。因此,即使您在xml中填入几万张图片地址也无所谓,完全不必担心实时加载所带来的程序瘫痪。
LTextureList最简单的用法莫过于此:
images.xml配置:
<?xml version="1.0" standalone="yes"?> <images> <ref name="hero"> <image src="assets/hero_a.png"> </image> </ref> <ref name="enemy"> <image src="assets/enemy_a.png"> </image> </ref> </images>
对应的java配置如下:
package org.loon.test; import org.loon.framework.javase.game.GameScene; import org.loon.framework.javase.game.core.graphics.Screen; import org.loon.framework.javase.game.core.graphics.opengl.GLColor; import org.loon.framework.javase.game.core.graphics.opengl.GLEx; import org.loon.framework.javase.game.core.graphics.opengl.LTextureList; import org.loon.framework.javase.game.core.input.LTouch; import org.loon.framework.javase.game.core.input.LTransition; import org.loon.framework.javase.game.core.timer.LTimerContext; public class LTextureListTest extends Screen { LTextureList list; public LTransition onTransition() { return LTransition.newEmpty(); } public void onLoad() { // 把Screen背景色定为红色 setBackground(GLColor.red); // 从指定xml文档获取LTextureList实例 list = new LTextureList("assets/images.xml"); } public void alter(LTimerContext timer) { } public void draw(GLEx g) { if (isOnLoadComplete()) { g.drawTexture(list.loadTexture("hero"), 32, 32); g.drawTexture(list.loadTexture("enemy"), 32, 64); } } public void touchDown(LTouch e) { } public void touchDrag(LTouch e) { } public void touchMove(LTouch e) { } public void touchUp(LTouch e) { } public void dispose() { if (list != null) { list.dispose(); list = null; } } public static void main(String[] args) { GameScene game = new GameScene("LTextureListTest", 480, 320); game.setShowFPS(true); game.setShowLogo(false); game.setScreen(new LTextureListTest()); game.showScreen(); } }运行上述程序后,我们会得到如下显示画面:

此时,如果有一些基本的图像变动,也不需再修改任何程序代码,仅微调xml参数即可,比如,当我们需要hero去掉黑底时,只要进行如下设置:
<?xml version="1.0" standalone="yes"?> <images> <ref name="hero"> <image src="assets/hero_a.png"> <mask r="0" g="0" b="0" /> </image> </ref> <ref name="enemy"> <image src="assets/enemy_a.png"> </image> </ref> </images>
这时,我们在image元素中,加入了<mask r="0" g="0" b="0" />一行,即过滤掉0,0,0像素,这时我们再次执行程序,画面将变为如下所示。
<?xml version="1.0" standalone="yes"?> <images> <ref name="hero"> <image src="assets/hero_a.png" x="0" y="0" w="24" h="24" scale="2"> <mask r="0" g="0" b="0" /> </image> </ref> <ref name="enemy"> <image src="assets/enemy_a.png"> </image> </ref> </images>
这次,增加了x="0" y="0" w="24" h="24"这样一行文字,并且添加有scale="2"。这是干什么用的呢?其实在上例讲到LTexturePack时,小弟已经做过了类似的事情,不过是告知系统从一整张图片的x=0,y=0处开始取图,一直取到w=24,h=24处停止罢了(也就是第一个“士兵”的图片位置)。不过这里有处与LTexturePack明显的差别,既LTexturePack中所取得的小图是系统从一整张大纹理中“虚拟”出来的,不会消耗任何额外的内存或显存空间,而LTextureList所取出的小图,对系统而言可是真实纹理,显存中实际存在的一张图像。

当然,LTextureList也可以单纯作为图像List使用,设定统一的setBlendMode改变图像的混图模式,透明度等等,比如:
package org.loon.test; import org.loon.framework.javase.game.GameScene; import org.loon.framework.javase.game.core.graphics.Screen; import org.loon.framework.javase.game.core.graphics.opengl.GL; import org.loon.framework.javase.game.core.graphics.opengl.GLColor; import org.loon.framework.javase.game.core.graphics.opengl.GLEx; import org.loon.framework.javase.game.core.graphics.opengl.LTextureList; import org.loon.framework.javase.game.core.input.LTouch; import org.loon.framework.javase.game.core.input.LTransition; import org.loon.framework.javase.game.core.timer.LTimerContext; public class LTextureListTest extends Screen { LTextureList list; public LTransition onTransition() { return LTransition.newEmpty(); } public void onLoad() { // 把Screen背景色定为红色 setBackground(GLColor.red); list = new LTextureList(); list.add("assets/hero_a.png", 32, 32); list.add("assets/enemy_a.png", 32, 64); list.setBlendMode(GL.MODE_COLOR_MULTIPLY); } public void alter(LTimerContext timer) { } public void draw(GLEx g) { if (isOnLoadComplete()) { list.draw(g); } } public void touchDown(LTouch e) { } public void touchDrag(LTouch e) { } public void touchMove(LTouch e) { } public void touchUp(LTouch e) { } public void dispose() { if (list != null) { list.dispose(); list = null; } } public static void main(String[] args) { GameScene game = new GameScene("LTextureListTest", 480, 320); game.setShowFPS(true); game.setShowLogo(false); game.setScreen(new LTextureListTest()); game.showScreen(); } }
不过,这样就失去预设定与惰性加载的能力了。

说完了LTextureList,但这种程度的篇幅作为博文来说依旧太短了,所以小弟下面再来谈谈0.3.2中新增的几个辅助类。
关于Session类
LGame中的Session类,是为了在多Screen中传值与保存多值而设计的,RecordStore类(高仿JavaME)的再简化版本,它每次set的可以是唯一的键值对,也可以在同一键下挂多值,在执行dispose函数前,session每次save的数值都将永久保存(JavaSE版保存为本地文件,Android版保存到手机数据库)。
下面是一个非常简单的用例。
package org.loon.test; import java.util.Calendar; import org.loon.framework.javase.game.GameScene; import org.loon.framework.javase.game.core.LSystem; import org.loon.framework.javase.game.core.graphics.Screen; import org.loon.framework.javase.game.core.graphics.opengl.GLEx; import org.loon.framework.javase.game.core.input.LTouch; import org.loon.framework.javase.game.core.input.LTransition; import org.loon.framework.javase.game.core.store.Session; import org.loon.framework.javase.game.core.timer.LTimerContext; public class SessionTest extends Screen { Session session = new Session(SessionTest.class.getName()); public LTransition onTransition() { return LTransition.newEmpty(); } public void onLoad() { } public void alter(LTimerContext timer) { } public void draw(GLEx g) { g.drawString("save 0 :" + session.get("save", 0), 32, 64); g.drawString("save 1 :" + session.get("save", 1), 32, 96); } public void touchDown(LTouch e) { } public void touchDrag(LTouch e) { } public void touchMove(LTouch e) { } public void touchUp(LTouch e) { } public void dispose() { if (session != null) { session.set("save", 0, session.getInt("save", 0) + 1); session.set("save", 1, System.currentTimeMillis()); session.save(); } /** * delete session * * if (session != null) * { * session.dispose(); * session = null; * } */ } public static void main(String[] args) { GameScene game = new GameScene("SessionTest", 480, 320); game.setShowFPS(true); game.setShowLogo(false); game.setScreen(new SessionTest()); game.showScreen(); } }
HttpClient类是为了保证LGame应用在跨平台时接口一致而构建的,处理简单网页资源浏览的工具类,基本用法如下所示。
HttpClient webClient = new HttpClient("www.baidu.com"); webClient.start(); System.out.println(webClient.doHTML("gb2312")); webClient.stop();
除了常规的网页加载外,HttpClient还可以作为下载器使用,基本用例如下所示:
package org.loon.test; import org.loon.framework.javase.game.GameScene; import org.loon.framework.javase.game.action.sprite.Label; import org.loon.framework.javase.game.core.graphics.Screen; import org.loon.framework.javase.game.core.graphics.component.LButton; import org.loon.framework.javase.game.core.graphics.component.LProgress; import org.loon.framework.javase.game.core.graphics.opengl.GLColor; import org.loon.framework.javase.game.core.graphics.opengl.GLEx; import org.loon.framework.javase.game.core.input.LTouch; import org.loon.framework.javase.game.core.input.LTransition; import org.loon.framework.javase.game.core.timer.LTimerContext; import org.loon.framework.javase.game.net.HttpClient; import org.loon.framework.javase.game.net.HttpDownload; import org.loon.framework.javase.game.net.HttpDownload.HttpDownloadListener; public class DownloadTest extends Screen { public LTransition onTransition() { return LTransition.newEmpty(); } public void onLoad() { //背景红色 setBackground(GLColor.red); //构建一个字符标签 final Label label = new Label("State : None", 200, 128); label.setColor(GLColor.white); add(label); //构建HttpClient final HttpClient webClient = new HttpClient( "http://libgdx.googlecode.com/files/libgdx-0.9.1.zip"); //构建一个进度条 final LProgress progress = new LProgress(32, 52, 420, 50); progress.setProgressTitle(true); add(progress); //构建一个下载按钮 LButton button = new LButton("download", 32, 64, 100, 100) { HttpDownload download; //设定一个下载标记,避免重复点击 boolean downloadFlag = false; public void downClick() { if (!downloadFlag) { downloadFlag = true; //进度监听 final HttpDownloadListener listener = new HttpDownloadListener() { public void cancel() { label.setLabel("State : Cancel"); } public void completed() { label.setLabel("State : Completed"); downloadFlag = false; if (download != null) { download.save("c:\\test.zip"); webClient.stop(); } } public void downloading(float c) { label.setLabel("State : Downloading"); progress.percent((int) c); } public void error(Exception ex) { label.setLabel("State : Error"); } public void paused() { label.setLabel("State : Paused"); } }; Runnable runnable = new Runnable() { public void run() { webClient.start(); download = webClient.getHttpDownload(); download.setListener(listener); download.start(); } }; callEvent(runnable); } } }; add(button); } public void alter(LTimerContext timer) { } public void draw(GLEx g) { } public void touchDown(LTouch e) { } public void touchDrag(LTouch e) { } public void touchMove(LTouch e) { } public void touchUp(LTouch e) { } public void dispose() { } public static void main(String[] args) { GameScene game = new GameScene("DownloadTest", 480, 320); game.setShowFPS(true); game.setShowLogo(false); game.setScreen(new DownloadTest()); game.showScreen(); } }
效果如下所示:
另,在LGame的SVN中,有一个小弟开发中的0.3.3测试版(内含jar及源码,loon-simple.googlecode.com/svn/trunk/),此版本与小弟目前手中的最新版本仅有个别细节处的差异(差的仅为效率问题修正),因为小弟正在集中精力搞C#版的完善与C/C++版的开发,0.3.3正式版暂时不会面世(我会将C/C++、C#、Java版都统一到0.3.3结构下,所以会多用点时间),但小弟会将一些最近的修改发往此处存底,如果大家在使用0.3.2时遇到一些问题,也可以换用0.3.3测试版实验运行,也许相关问题在此版就已经解决了(此LGame-0.3.3-test文件大约每周更新一次,上次更新是三天前)。